From bd0b8e9449b4c31aa47de67e80dba98f74749d4f Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Fri, 8 Mar 2019 13:37:00 -0800 Subject: [PATCH 001/214] Collection Group Query Internals (#2378) Includes the new index and all internals for collection group queries, but the public API will not be exposed until backend support is ready. --- Firestore/CHANGELOG.md | 3 + .../Firestore.xcodeproj/project.pbxproj | 18 + .../Tests/Integration/API/FIRQueryTests.mm | 110 ++ .../Integration/API/FIRValidationTests.mm | 27 +- .../Tests/Local/FSTLevelDBMigrationsTests.mm | 57 +- .../FSTMemoryRemoteDocumentCacheTests.mm | 2 +- .../Example/Tests/SpecTests/FSTSpecTests.mm | 7 +- .../Tests/SpecTests/json/query_spec_test.json | 1170 +++++++++++++++++ Firestore/Source/API/FIRFirestore+Internal.h | 13 + Firestore/Source/API/FIRFirestore.mm | 17 + Firestore/Source/API/FIRQuery.mm | 42 +- Firestore/Source/Core/FSTQuery.h | 26 + Firestore/Source/Core/FSTQuery.mm | 75 +- Firestore/Source/Local/FSTLevelDB.mm | 10 + .../Source/Local/FSTLocalDocumentsView.h | 10 +- .../Source/Local/FSTLocalDocumentsView.mm | 44 +- Firestore/Source/Local/FSTLocalStore.mm | 12 +- .../Source/Local/FSTMemoryPersistence.mm | 16 +- Firestore/Source/Local/FSTPersistence.h | 6 +- Firestore/Source/Public/FIRFirestore.h | 1 + Firestore/Source/Remote/FSTSerializerBeta.mm | 20 +- .../Swift/Tests/API/BasicCompileTests.swift | 5 +- .../core/src/firebase/firestore/core/query.h | 2 + .../firebase/firestore/local/CMakeLists.txt | 2 + .../firebase/firestore/local/index_manager.h | 65 + .../firestore/local/leveldb_index_manager.h | 70 + .../firestore/local/leveldb_index_manager.mm | 81 ++ .../firebase/firestore/local/leveldb_key.cc | 75 +- .../firebase/firestore/local/leveldb_key.h | 58 + .../firestore/local/leveldb_migrations.cc | 71 +- .../firestore/local/leveldb_mutation_queue.mm | 5 + .../local/leveldb_remote_document_cache.mm | 6 + .../firestore/local/memory_index_manager.cc | 67 + .../firestore/local/memory_index_manager.h | 66 + .../firestore/local/memory_mutation_queue.mm | 9 +- .../local/memory_remote_document_cache.h | 5 + .../local/memory_remote_document_cache.mm | 16 +- .../firebase/firestore/model/document_key.h | 8 +- .../firebase/firestore/remote/serializer.cc | 1 + .../firebase/firestore/local/CMakeLists.txt | 3 + .../firestore/local/index_manager_test.h | 62 + .../firestore/local/index_manager_test.mm | 75 ++ .../local/leveldb_index_manager_test.mm | 49 + .../firestore/local/leveldb_key_test.cc | 10 +- .../local/memory_index_manager_test.mm | 49 + 45 files changed, 2469 insertions(+), 77 deletions(-) create mode 100644 Firestore/Example/Tests/SpecTests/json/query_spec_test.json create mode 100644 Firestore/core/src/firebase/firestore/local/index_manager.h create mode 100644 Firestore/core/src/firebase/firestore/local/leveldb_index_manager.h create mode 100644 Firestore/core/src/firebase/firestore/local/leveldb_index_manager.mm create mode 100644 Firestore/core/src/firebase/firestore/local/memory_index_manager.cc create mode 100644 Firestore/core/src/firebase/firestore/local/memory_index_manager.h create mode 100644 Firestore/core/test/firebase/firestore/local/index_manager_test.h create mode 100644 Firestore/core/test/firebase/firestore/local/index_manager_test.mm create mode 100644 Firestore/core/test/firebase/firestore/local/leveldb_index_manager_test.mm create mode 100644 Firestore/core/test/firebase/firestore/local/memory_index_manager_test.mm diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index ab848534bb9..1abc55b329b 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -4,6 +4,9 @@ - [feature] Added `FieldValue.increment()`, which can be used in `updateData(_:)` and `setData(_:merge:)` to increment or decrement numeric field values safely without transactions. +- [changed] Prepared the persistence layer to support collection group queries. + While this feature is not yet available, all schema changes are included + in this release. # v1.0.2 - [changed] Internal improvements. diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index e87f93bbc4d..eabf0f811e0 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -172,7 +172,11 @@ 6EDD3B6020BF25AE00C33877 /* FSTFuzzTestsPrincipal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EDD3B5E20BF24D000C33877 /* FSTFuzzTestsPrincipal.mm */; }; 6F3CAC76D918D6B0917EDF92 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; + 731541612214AFFA0037F4DC /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; 73866AA12082B0A5009BB4FF /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; + 73F1F73C2210F3D800E1F692 /* memory_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */; }; + 73F1F73D2210F3D800E1F692 /* index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */; }; + 73F1F7412211FEF300E1F692 /* leveldb_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */; }; 73FE5066020EF9B2892C86BF /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; 84DBE646DCB49305879D3500 /* nanopb_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; @@ -490,7 +494,12 @@ 6EDD3B5C20BF247500C33877 /* Firestore_FuzzTests_iOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Firestore_FuzzTests_iOS-Info.plist"; sourceTree = ""; }; 6EDD3B5E20BF24D000C33877 /* FSTFuzzTestsPrincipal.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTFuzzTestsPrincipal.mm; sourceTree = ""; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 731541602214AFFA0037F4DC /* query_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = query_spec_test.json; sourceTree = ""; }; 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRArrayTransformTests.mm; sourceTree = ""; }; + 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = memory_index_manager_test.mm; sourceTree = ""; }; + 73F1F73A2210F3D800E1F692 /* index_manager_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = index_manager_test.h; sourceTree = ""; }; + 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = index_manager_test.mm; sourceTree = ""; }; + 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = leveldb_index_manager_test.mm; sourceTree = ""; }; 79507DF8378D3C42F5B36268 /* string_win_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = string_win_test.cc; sourceTree = ""; }; 84434E57CA72951015FC71BC /* Pods-Firestore_FuzzTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_FuzzTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS.debug.xcconfig"; sourceTree = ""; }; 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -802,9 +811,13 @@ 54995F70205B6E1A004EFFA0 /* local */ = { isa = PBXGroup; children = ( + 73F1F73A2210F3D800E1F692 /* index_manager_test.h */, + 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */, + 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */, 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */, 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */, F8043813A5D16963EC02B182 /* local_serializer_test.cc */, + 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */, 132E32997D781B896672D30A /* reference_set_test.cc */, ); path = local; @@ -1262,6 +1275,7 @@ 54DA12A21F315EE100DD57A1 /* orderby_spec_test.json */, D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */, 54DA12A31F315EE100DD57A1 /* persistence_spec_test.json */, + 731541602214AFFA0037F4DC /* query_spec_test.json */, 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */, 54DA12A41F315EE100DD57A1 /* resume_token_spec_test.json */, 54DA12A51F315EE100DD57A1 /* write_spec_test.json */, @@ -1578,6 +1592,7 @@ 54DA12AC1F315EE100DD57A1 /* orderby_spec_test.json in Resources */, D5B25CBF07F65E885C9D68AB /* perf_spec_test.json in Resources */, 54DA12AD1F315EE100DD57A1 /* persistence_spec_test.json in Resources */, + 731541612214AFFA0037F4DC /* query_spec_test.json in Resources */, 3B843E4C1F3A182900548890 /* remote_store_spec_test.json in Resources */, 54DA12AE1F315EE100DD57A1 /* resume_token_spec_test.json in Resources */, 54DA12AF1F315EE100DD57A1 /* write_spec_test.json in Resources */, @@ -2069,13 +2084,16 @@ 54511E8E209805F8005BD28F /* hashing_test.cc in Sources */, B69CF3F12227386500B281C8 /* hashing_test_apple.mm in Sources */, 618BBEB020B89AAC00B5BCE7 /* http.pb.cc in Sources */, + 73F1F73D2210F3D800E1F692 /* index_manager_test.mm in Sources */, 54A0353520A3D8CB003E0143 /* iterator_adaptors_test.cc in Sources */, 618BBEAE20B89AAC00B5BCE7 /* latlng.pb.cc in Sources */, + 73F1F7412211FEF300E1F692 /* leveldb_index_manager_test.mm in Sources */, 54995F6F205B6E12004EFFA0 /* leveldb_key_test.cc in Sources */, BEE0294A23AB993E5DE0E946 /* leveldb_util_test.cc in Sources */, 020AFD89BB40E5175838BB76 /* local_serializer_test.cc in Sources */, 54C2294F1FECABAE007D065B /* log_test.cc in Sources */, 618BBEA720B89AAC00B5BCE7 /* maybe_document.pb.cc in Sources */, + 73F1F73C2210F3D800E1F692 /* memory_index_manager_test.mm in Sources */, 618BBEA820B89AAC00B5BCE7 /* mutation.pb.cc in Sources */, 32F022CB75AEE48CDDAF2982 /* mutation_test.cc in Sources */, 84DBE646DCB49305879D3500 /* nanopb_string_test.cc in Sources */, diff --git a/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm index da20d92ea26..18555a61334 100644 --- a/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm @@ -20,6 +20,8 @@ #import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +// TODO(b/116617988): Remove Internal include once CG queries are public. +#import "Firestore/Source/API/FIRFirestore+Internal.h" @interface FIRQueryTests : FSTIntegrationTestCase @end @@ -294,4 +296,112 @@ - (void)testArrayContainsQueries { // of anything else interesting to test. } +- (void)testCollectionGroupQueries { + // Use .document() to get a random collection group name to use but ensure it starts with 'b' + // for predictable ordering. + NSString *collectionGroup = [NSString + stringWithFormat:@"b%@", [[self.db collectionWithPath:@"foo"] documentWithAutoID].documentID]; + + NSArray *docPaths = @[ + @"abc/123/${collectionGroup}/cg-doc1", @"abc/123/${collectionGroup}/cg-doc2", + @"${collectionGroup}/cg-doc3", @"${collectionGroup}/cg-doc4", + @"def/456/${collectionGroup}/cg-doc5", @"${collectionGroup}/virtual-doc/nested-coll/not-cg-doc", + @"x${collectionGroup}/not-cg-doc", @"${collectionGroup}x/not-cg-doc", + @"abc/123/${collectionGroup}x/not-cg-doc", @"abc/123/x${collectionGroup}/not-cg-doc", + @"abc/${collectionGroup}" + ]; + + FIRWriteBatch *batch = [self.db batch]; + for (NSString *docPath in docPaths) { + NSString *path = [docPath stringByReplacingOccurrencesOfString:@"${collectionGroup}" + withString:collectionGroup]; + [batch setData:@{@"x" : @1} forDocument:[self.db documentWithPath:path]]; + } + XCTestExpectation *expectation = [self expectationWithDescription:@"batch written"]; + [batch commitWithCompletion:^(NSError *error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self awaitExpectations]; + + FIRQuerySnapshot *querySnapshot = + [self readDocumentSetForRef:[self.db collectionGroupWithID:collectionGroup]]; + NSArray *ids = FIRQuerySnapshotGetIDs(querySnapshot); + XCTAssertEqualObjects(ids, (@[ @"cg-doc1", @"cg-doc2", @"cg-doc3", @"cg-doc4", @"cg-doc5" ])); +} + +- (void)testCollectionGroupQueriesWithStartAtEndAtWithArbitraryDocumentIDs { + // Use .document() to get a random collection group name to use but ensure it starts with 'b' + // for predictable ordering. + NSString *collectionGroup = [NSString + stringWithFormat:@"b%@", [[self.db collectionWithPath:@"foo"] documentWithAutoID].documentID]; + + NSArray *docPaths = @[ + @"a/a/${collectionGroup}/cg-doc1", @"a/b/a/b/${collectionGroup}/cg-doc2", + @"a/b/${collectionGroup}/cg-doc3", @"a/b/c/d/${collectionGroup}/cg-doc4", + @"a/c/${collectionGroup}/cg-doc5", @"${collectionGroup}/cg-doc6", @"a/b/nope/nope" + ]; + + FIRWriteBatch *batch = [self.db batch]; + for (NSString *docPath in docPaths) { + NSString *path = [docPath stringByReplacingOccurrencesOfString:@"${collectionGroup}" + withString:collectionGroup]; + [batch setData:@{@"x" : @1} forDocument:[self.db documentWithPath:path]]; + } + XCTestExpectation *expectation = [self expectationWithDescription:@"batch written"]; + [batch commitWithCompletion:^(NSError *error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self awaitExpectations]; + + FIRQuerySnapshot *querySnapshot = [self + readDocumentSetForRef:[[[[self.db collectionGroupWithID:collectionGroup] + queryOrderedByFieldPath:[FIRFieldPath documentID]] + queryStartingAfterValues:@[ @"a/b" ]] + queryEndingBeforeValues:@[ + [NSString stringWithFormat:@"a/b/%@/cg-doc3", collectionGroup] + ]]]; + + NSArray *ids = FIRQuerySnapshotGetIDs(querySnapshot); + XCTAssertEqualObjects(ids, (@[ @"cg-doc2" ])); +} + +- (void)testCollectionGroupQueriesWithWhereFiltersOnArbitraryDocumentIDs { + // Use .document() to get a random collection group name to use but ensure it starts with 'b' + // for predictable ordering. + NSString *collectionGroup = [NSString + stringWithFormat:@"b%@", [[self.db collectionWithPath:@"foo"] documentWithAutoID].documentID]; + + NSArray *docPaths = @[ + @"a/a/${collectionGroup}/cg-doc1", @"a/b/a/b/${collectionGroup}/cg-doc2", + @"a/b/${collectionGroup}/cg-doc3", @"a/b/c/d/${collectionGroup}/cg-doc4", + @"a/c/${collectionGroup}/cg-doc5", @"${collectionGroup}/cg-doc6", @"a/b/nope/nope" + ]; + + FIRWriteBatch *batch = [self.db batch]; + for (NSString *docPath in docPaths) { + NSString *path = [docPath stringByReplacingOccurrencesOfString:@"${collectionGroup}" + withString:collectionGroup]; + [batch setData:@{@"x" : @1} forDocument:[self.db documentWithPath:path]]; + } + XCTestExpectation *expectation = [self expectationWithDescription:@"batch written"]; + [batch commitWithCompletion:^(NSError *error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self awaitExpectations]; + + FIRQuerySnapshot *querySnapshot = [self + readDocumentSetForRef:[[[self.db collectionGroupWithID:collectionGroup] + queryWhereFieldPath:[FIRFieldPath documentID] + isGreaterThanOrEqualTo:@"a/b"] + queryWhereFieldPath:[FIRFieldPath documentID] + isLessThan:[NSString stringWithFormat:@"a/b/%@/cg-doc3", + collectionGroup]]]; + + NSArray *ids = FIRQuerySnapshotGetIDs(querySnapshot); + XCTAssertEqualObjects(ids, (@[ @"cg-doc2" ])); +} + @end diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index b1fec91d657..1cd345026f4 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -22,6 +22,8 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +// TODO(b/116617988): Remove Internal include once CG queries are public. +#import "Firestore/Source/API/FIRFirestore+Internal.h" // We have tests for passing nil when nil is not supposed to be allowed. So suppress the warnings. #pragma clang diagnostic ignored "-Wnonnull" @@ -482,12 +484,19 @@ - (void)testQueryBoundMustNotHaveMoreComponentsThanSortOrders { } - (void)testQueryOrderedByKeyBoundMustBeAStringWithoutSlashes { - FIRCollectionReference *testCollection = [self collectionRef]; - FIRQuery *query = [testCollection queryOrderedByFieldPath:[FIRFieldPath documentID]]; + FIRQuery *query = [[self.db collectionWithPath:@"collection"] + queryOrderedByFieldPath:[FIRFieldPath documentID]]; + FIRQuery *cgQuery = [[self.db collectionGroupWithID:@"collection"] + queryOrderedByFieldPath:[FIRFieldPath documentID]]; FSTAssertThrows([query queryStartingAtValues:@[ @1 ]], @"Invalid query. Expected a string for the document ID."); FSTAssertThrows([query queryStartingAtValues:@[ @"foo/bar" ]], - @"Invalid query. Document ID 'foo/bar' contains a slash."); + @"Invalid query. When querying a collection and ordering by document " + "ID, you must pass a plain document ID, but 'foo/bar' contains a slash."); + FSTAssertThrows([cgQuery queryStartingAtValues:@[ @"foo" ]], + @"Invalid query. When querying a collection group and ordering by " + "document ID, you must pass a value that results in a valid document path, " + "but 'foo' is not because it contains an odd number of segments."); } - (void)testQueryMustNotSpecifyStartingOrEndingPointAfterOrder { @@ -508,8 +517,8 @@ - (void)testQueriesFilteredByDocumentIDMustUseStringsOrDocumentReferences { "document ID, but it was an empty string."; FSTAssertThrows([collection queryWhereFieldPath:[FIRFieldPath documentID] isEqualTo:@""], reason); - reason = @"Invalid query. When querying by document ID you must provide a valid document ID, " - "but 'foo/bar/baz' contains a '/' character."; + reason = @"Invalid query. When querying a collection by document ID you must provide a " + "plain document ID, but 'foo/bar/baz' contains a '/' character."; FSTAssertThrows( [collection queryWhereFieldPath:[FIRFieldPath documentID] isEqualTo:@"foo/bar/baz"], reason); @@ -517,6 +526,14 @@ - (void)testQueriesFilteredByDocumentIDMustUseStringsOrDocumentReferences { "DocumentReference, but it was of type: __NSCFNumber"; FSTAssertThrows([collection queryWhereFieldPath:[FIRFieldPath documentID] isEqualTo:@1], reason); + reason = @"Invalid query. When querying a collection group by document ID, the value " + "provided must result in a valid document path, but 'foo/bar/baz' is not because it " + "has an odd number of segments."; + FSTAssertThrows( + [[self.db collectionGroupWithID:@"collection"] queryWhereFieldPath:[FIRFieldPath documentID] + isEqualTo:@"foo/bar/baz"], + reason); + reason = @"Invalid query. You can't perform arrayContains queries on document ID since document IDs " "are not arrays."; diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm index 79a9b977e1c..9a48c5b0a17 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm @@ -16,6 +16,7 @@ #import +#include #include #include #include @@ -37,6 +38,7 @@ NS_ASSUME_NONNULL_BEGIN using firebase::firestore::FirestoreErrorCode; +using firebase::firestore::local::LevelDbCollectionParentKey; using firebase::firestore::local::LevelDbDocumentMutationKey; using firebase::firestore::local::LevelDbDocumentTargetKey; using firebase::firestore::local::LevelDbMigrations; @@ -334,7 +336,6 @@ - (void)testRemovesMutationBatches { // Verify std::string buffer; LevelDbTransaction transaction(_db.get(), "Verify"); - auto it = transaction.NewIterator(); // verify that we deleted the correct batches XCTAssertTrue(transaction.Get(LevelDbMutationKey::Key("foo", 1), &buffer).IsNotFound()); XCTAssertTrue(transaction.Get(LevelDbMutationKey::Key("foo", 2), &buffer).IsNotFound()); @@ -360,6 +361,60 @@ - (void)testRemovesMutationBatches { } } +- (void)testCreateCollectionParentsIndex { + // This test creates a database with schema version 5 that has a few + // mutations and a few remote documents and then ensures that appropriate + // entries are written to the collectionParentIndex. + std::vector write_paths{"cg1/x", "cg1/y", "cg1/x/cg1/x", "cg2/x", "cg1/x/cg2/x"}; + std::vector remote_doc_paths{"cg1/z", "cg1/y/cg1/x", "cg2/x/cg3/x", + "blah/x/blah/x/cg3/x"}; + std::map> expected_parents{ + {"cg1", {"", "cg1/x", "cg1/y"}}, {"cg2", {"", "cg1/x"}}, {"cg3", {"blah/x/blah/x", "cg2/x"}}}; + + std::string empty_buffer; + LevelDbMigrations::RunMigrations(_db.get(), 5); + { + LevelDbTransaction transaction(_db.get(), "Write Mutations and Remote Documents"); + // Write mutations. + for (auto write_path : write_paths) { + // We "cheat" and only write the DbDocumentMutation index entries, since + // that's all the migration uses. + DocumentKey key = DocumentKey::FromPathString(write_path); + transaction.Put(LevelDbDocumentMutationKey::Key("dummy-uid", key, /*dummy batchId=*/123), + empty_buffer); + } + + // Write remote document entries. + for (auto remote_doc_path : remote_doc_paths) { + DocumentKey key = DocumentKey::FromPathString(remote_doc_path); + transaction.Put(LevelDbRemoteDocumentKey::Key(key), empty_buffer); + } + + transaction.Commit(); + } + + // Migrate to v6 and verify index entries. + LevelDbMigrations::RunMigrations(_db.get(), 6); + { + LevelDbTransaction transaction(_db.get(), "Verify"); + + std::map> actual_parents; + auto index_iterator = transaction.NewIterator(); + std::string index_prefix = LevelDbCollectionParentKey::KeyPrefix(); + LevelDbCollectionParentKey row_key; + for (index_iterator->Seek(index_prefix); index_iterator->Valid(); index_iterator->Next()) { + if (!absl::StartsWith(index_iterator->key(), index_prefix) || + !row_key.Decode(index_iterator->key())) + break; + + std::vector &parents = actual_parents[row_key.collection_id()]; + parents.push_back(row_key.parent().CanonicalString()); + } + + XCTAssertEqual(actual_parents, expected_parents); + } +} + - (void)testCanDowngrade { // First, run all of the migrations LevelDbMigrations::RunMigrations(_db.get()); diff --git a/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm b/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm index c39a15f1fbf..7784056705b 100644 --- a/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm +++ b/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm @@ -44,7 +44,7 @@ - (void)setUp { self.persistence = [FSTPersistenceTestHelpers eagerGCMemoryPersistence]; HARD_ASSERT(!_cache, "Previous cache not torn down"); - _cache = absl::make_unique(); + _cache = absl::make_unique(self.persistence); } - (RemoteDocumentCache *)remoteDocumentCache { diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index 31172475494..fde73c3fcbf 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -39,6 +39,7 @@ #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_key_set.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/remote/existence_filter.h" @@ -59,6 +60,7 @@ using firebase::firestore::core::DocumentViewChange; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::ResourcePath; using firebase::firestore::model::SnapshotVersion; using firebase::firestore::model::TargetId; using firebase::firestore::remote::ExistenceFilter; @@ -170,7 +172,10 @@ - (nullable FSTQuery *)parseQuery:(id)querySpec { } else if ([querySpec isKindOfClass:[NSDictionary class]]) { NSDictionary *queryDict = (NSDictionary *)querySpec; NSString *path = queryDict[@"path"]; - __block FSTQuery *query = FSTTestQuery(util::MakeString(path)); + ResourcePath resource_path = ResourcePath::FromString(util::MakeString(path)); + NSString *_Nullable collectionGroup = queryDict[@"collectionGroup"]; + __block FSTQuery *query = [FSTQuery queryWithPath:resource_path + collectionGroup:collectionGroup]; if (queryDict[@"limit"]) { NSNumber *limit = queryDict[@"limit"]; query = [query queryBySettingLimit:limit.integerValue]; diff --git a/Firestore/Example/Tests/SpecTests/json/query_spec_test.json b/Firestore/Example/Tests/SpecTests/json/query_spec_test.json new file mode 100644 index 00000000000..4ca89f24cba --- /dev/null +++ b/Firestore/Example/Tests/SpecTests/json/query_spec_test.json @@ -0,0 +1,1170 @@ +{ + "Collection Group query": { + "describeName": "Queries:", + "itName": "Collection Group query", + "tags": [], + "config": { + "useGarbageCollection": true, + "numClients": 1 + }, + "steps": [ + { + "userListen": [ + 2, + { + "path": "cg/1", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "cg/1", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "version": 1000, + "targetIds": [] + }, + "expect": [ + { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "cg/1", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "userListen": [ + 4, + { + "path": "cg/2", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "cg/2", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 4 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "cg/2", + "version": 1000, + "value": { + "val": 2 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "targets": [ + 4 + ] + } + }, + { + "watchCurrent": [ + [ + 4 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "version": 1000, + "targetIds": [] + }, + "expect": [ + { + "query": { + "path": "cg/2", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "cg/2", + "version": 1000, + "value": { + "val": 2 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "userListen": [ + 6, + { + "path": "not-cg/nope/cg/3", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "cg/2", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "6": { + "query": { + "path": "not-cg/nope/cg/3", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 6 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "not-cg/nope/cg/3", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "targets": [ + 6 + ] + } + }, + { + "watchCurrent": [ + [ + 6 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "version": 1000, + "targetIds": [] + }, + "expect": [ + { + "query": { + "path": "not-cg/nope/cg/3", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "not-cg/nope/cg/3", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "userListen": [ + 8, + { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "cg/2", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "6": { + "query": { + "path": "not-cg/nope/cg/3", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "8": { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 8 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "not-cg/nope", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "targets": [ + 8 + ] + } + }, + { + "watchCurrent": [ + [ + 8 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "version": 1000, + "targetIds": [] + }, + "expect": [ + { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "not-cg/nope", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "userListen": [ + 10, + { + "path": "cg/1/not-cg/nope", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "cg/2", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "6": { + "query": { + "path": "not-cg/nope/cg/3", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "8": { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "10": { + "query": { + "path": "cg/1/not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 10 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "cg/1/not-cg/nope", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "targets": [ + 10 + ] + } + }, + { + "watchCurrent": [ + [ + 10 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "version": 1000, + "targetIds": [] + }, + "expect": [ + { + "query": { + "path": "cg/1/not-cg/nope", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "cg/1/not-cg/nope", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "userListen": [ + 12, + { + "path": "", + "collectionGroup": "cg", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "cg/2", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "6": { + "query": { + "path": "not-cg/nope/cg/3", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "8": { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "10": { + "query": { + "path": "cg/1/not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "12": { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + }, + "expect": [ + { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "cg/1", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + }, + { + "key": "cg/2", + "version": 1000, + "value": { + "val": 2 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + }, + { + "key": "not-cg/nope/cg/3", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false + } + ] + }, + { + "userListen": [ + 14, + { + "path": "", + "collectionGroup": "cg", + "filters": [ + [ + "val", + "==", + 1 + ] + ], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "cg/2", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "6": { + "query": { + "path": "not-cg/nope/cg/3", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "8": { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "10": { + "query": { + "path": "cg/1/not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "12": { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "14": { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [ + [ + "val", + "==", + 1 + ] + ], + "orderBys": [] + }, + "resumeToken": "" + } + } + }, + "expect": [ + { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [ + [ + "val", + "==", + 1 + ] + ], + "orderBys": [] + }, + "added": [ + { + "key": "cg/1", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + }, + { + "key": "not-cg/nope/cg/3", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false + } + ] + } + ] + }, + "Collection Group query with mutations": { + "describeName": "Queries:", + "itName": "Collection Group query with mutations", + "tags": [], + "config": { + "useGarbageCollection": true, + "numClients": 1 + }, + "steps": [ + { + "userListen": [ + 2, + { + "path": "cg/1", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "cg/1", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "version": 1000, + "targetIds": [] + }, + "expect": [ + { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "cg/1", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "userListen": [ + 4, + { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 4 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "not-cg/nope", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "targets": [ + 4 + ] + } + }, + { + "watchCurrent": [ + [ + 4 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "version": 1000, + "targetIds": [] + }, + "expect": [ + { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "not-cg/nope", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "userSet": [ + "cg/2", + { + "val": 2 + } + ] + }, + { + "userSet": [ + "not-cg/nope/cg/3", + { + "val": 1 + } + ] + }, + { + "userSet": [ + "not-cg2/nope", + { + "val": 1 + } + ] + }, + { + "userListen": [ + 6, + { + "path": "", + "collectionGroup": "cg", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "6": { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + }, + "expect": [ + { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [], + "orderBys": [] + }, + "added": [ + { + "key": "cg/1", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + }, + { + "key": "cg/2", + "version": 0, + "value": { + "val": 2 + }, + "options": { + "hasLocalMutations": true, + "hasCommittedMutations": false + } + }, + { + "key": "not-cg/nope/cg/3", + "version": 0, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": true, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true + } + ] + }, + { + "userListen": [ + 8, + { + "path": "", + "collectionGroup": "cg", + "filters": [ + [ + "val", + "==", + 1 + ] + ], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "cg/1", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "4": { + "query": { + "path": "not-cg/nope", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "6": { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "8": { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [ + [ + "val", + "==", + 1 + ] + ], + "orderBys": [] + }, + "resumeToken": "" + } + } + }, + "expect": [ + { + "query": { + "path": "", + "collectionGroup": "cg", + "filters": [ + [ + "val", + "==", + 1 + ] + ], + "orderBys": [] + }, + "added": [ + { + "key": "cg/1", + "version": 1000, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": false, + "hasCommittedMutations": false + } + }, + { + "key": "not-cg/nope/cg/3", + "version": 0, + "value": { + "val": 1 + }, + "options": { + "hasLocalMutations": true, + "hasCommittedMutations": false + } + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true + } + ] + } + ] + } +} diff --git a/Firestore/Source/API/FIRFirestore+Internal.h b/Firestore/Source/API/FIRFirestore+Internal.h index 0878237316a..ad9da8ed109 100644 --- a/Firestore/Source/API/FIRFirestore+Internal.h +++ b/Firestore/Source/API/FIRFirestore+Internal.h @@ -49,6 +49,19 @@ NS_ASSUME_NONNULL_BEGIN /** Internal FIRFirestore API we don't want exposed in our public header files. */ @interface FIRFirestore (Internal) +// TODO(b/116617988): Move this to FIRFirestore.h and update CHANGELOG.md once backend support is +// ready. +#pragma mark - Collection Group Queries +/** + * Creates and returns a new `Query` that includes all documents in the database that are contained + * in a collection or subcollection with the given collectionID. + * + * @param collectionID Identifies the collections to query over. Every collection or subcollection + * with this ID as the last segment of its path will be included. Cannot contain a slash. + * @return The created `Query`. + */ +- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID NS_SWIFT_NAME(collectionGroup(_:)); + /** Checks to see if logging is is globally enabled for the Firestore client. */ + (BOOL)isLoggingEnabled; diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index 70771bf8305..6da95559240 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -30,11 +30,13 @@ #import "Firestore/Source/API/FIRCollectionReference+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/API/FIRTransaction+Internal.h" #import "Firestore/Source/API/FIRWriteBatch+Internal.h" #import "Firestore/Source/API/FSTFirestoreComponent.h" #import "Firestore/Source/API/FSTUserDataConverter.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" +#import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" @@ -272,6 +274,21 @@ - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath { return [FIRDocumentReference referenceWithPath:path firestore:self]; } +- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID { + if (!collectionID) { + FSTThrowInvalidArgument(@"Collection ID cannot be nil."); + } + if ([collectionID containsString:@"/"]) { + FSTThrowInvalidArgument( + @"Invalid collection ID (%@). Collection IDs must not contain / in them.", collectionID); + } + + [self ensureClientConfigured]; + return [FIRQuery referenceWithQuery:[FSTQuery queryWithPath:ResourcePath::Empty() + collectionGroup:collectionID] + firestore:self]; +} + - (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **))updateBlock dispatchQueue:(dispatch_queue_t)queue completion: diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index c6ef8731644..0665a335ab7 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -472,15 +472,25 @@ - (FIRQuery *)queryWithFilterOperator:(FSTRelationFilterOperator)filterOperator } if ([value isKindOfClass:[NSString class]]) { NSString *documentKey = (NSString *)value; - if ([documentKey containsString:@"/"]) { - FSTThrowInvalidArgument(@"Invalid query. When querying by document ID you must provide " - "a valid document ID, but '%@' contains a '/' character.", - documentKey); - } else if (documentKey.length == 0) { + if (documentKey.length == 0) { FSTThrowInvalidArgument(@"Invalid query. When querying by document ID you must provide " "a valid document ID, but it was an empty string."); } - ResourcePath path = self.query.path.Append([documentKey UTF8String]); + if (![self.query isCollectionGroupQuery] && [documentKey containsString:@"/"]) { + FSTThrowInvalidArgument( + @"Invalid query. When querying a collection by document ID you must provide " + "a plain document ID, but '%@' contains a '/' character.", + documentKey); + } + ResourcePath path = + self.query.path.Append(ResourcePath::FromString([documentKey UTF8String])); + if (!DocumentKey::IsDocumentKey(path)) { + FSTThrowInvalidArgument( + @"Invalid query. When querying a collection group by document ID, " + "the value provided must result in a valid document path, but '%s' is not because it " + "has an odd number of segments.", + path.CanonicalString().c_str()); + } fieldValue = [FSTReferenceValue referenceValue:[FSTDocumentKey keyWithDocumentKey:DocumentKey{path}] databaseID:self.firestore.databaseID]; @@ -629,11 +639,23 @@ - (FSTBound *)boundFromFieldValues:(NSArray *)fieldValues isBefore:(BOOL)isB @"Invalid query. Expected a string for the document ID."); } NSString *documentID = (NSString *)rawValue; - if ([documentID containsString:@"/"]) { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid query. Document ID '%@' contains a slash.", documentID); + if (![self.query isCollectionGroupQuery] && [documentID containsString:@"/"]) { + FSTThrowInvalidUsage( + @"InvalidQueryException", + @"Invalid query. When querying a collection and ordering by document ID, " + "you must pass a plain document ID, but '%@' contains a slash.", + documentID); + } + ResourcePath path = self.query.path.Append(ResourcePath::FromString([documentID UTF8String])); + if (!DocumentKey::IsDocumentKey(path)) { + FSTThrowInvalidUsage( + @"InvalidQueryException", + @"Invalid query. When querying a collection group and ordering by document ID, " + "you must pass a value that results in a valid document path, but '%s' " + "is not because it contains an odd number of segments.", + path.CanonicalString().c_str()); } - const DocumentKey key{self.query.path.Append([documentID UTF8String])}; + DocumentKey key{path}; [components addObject:[FSTReferenceValue referenceValue:[FSTDocumentKey keyWithDocumentKey:key] databaseID:self.firestore.databaseID]]; diff --git a/Firestore/Source/Core/FSTQuery.h b/Firestore/Source/Core/FSTQuery.h index 17c781b2838..851da923d53 100644 --- a/Firestore/Source/Core/FSTQuery.h +++ b/Firestore/Source/Core/FSTQuery.h @@ -174,6 +174,7 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { * Initializes a query with all of its components directly. */ - (instancetype)initWithPath:(firebase::firestore::model::ResourcePath)path + collectionGroup:(nullable NSString *)collectionGroup filterBy:(NSArray *)filters orderBy:(NSArray *)sortOrders limit:(NSInteger)limit @@ -188,6 +189,18 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { */ + (instancetype)queryWithPath:(firebase::firestore::model::ResourcePath)path; +/** + * Creates and returns a new FSTQuery. + * + * @param path The path to the location to be queried over. Must currently be + * empty in the case of a collection group query. + * @param collectionGroup The collection group to be queried over. nil if this + * is not a collection group query. + * @return A new instance of FSTQuery. + */ ++ (instancetype)queryWithPath:(firebase::firestore::model::ResourcePath)path + collectionGroup:(nullable NSString *)collectionGroup; + /** * Returns the list of ordering constraints that were explicitly requested on the query by the * user. @@ -244,9 +257,19 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { */ - (instancetype)queryByAddingEndAt:(FSTBound *)bound; +/** + * Helper to convert a collection group query into a collection query at a specific path. This is + * used when executing collection group queries, since we have to split the query into a set of + * collection queries at multiple paths. + */ +- (instancetype)collectionQueryAtPath:(firebase::firestore::model::ResourcePath)path; + /** Returns YES if the receiver is query for a specific document. */ - (BOOL)isDocumentQuery; +/** Returns YES if the receiver is a collection group query. */ +- (BOOL)isCollectionGroupQuery; + /** Returns YES if the @a document matches the constraints of the receiver. */ - (BOOL)matchesDocument:(FSTDocument *)document; @@ -266,6 +289,9 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { /** The base path of the query. */ - (const firebase::firestore::model::ResourcePath &)path; +/** The collection group of the query. */ +@property(nonatomic, nullable, strong, readonly) NSString *collectionGroup; + /** The filters on the documents returned by the query. */ @property(nonatomic, strong, readonly) NSArray *filters; diff --git a/Firestore/Source/Core/FSTQuery.mm b/Firestore/Source/Core/FSTQuery.mm index 76598d9c0d5..d057c87c438 100644 --- a/Firestore/Source/Core/FSTQuery.mm +++ b/Firestore/Source/Core/FSTQuery.mm @@ -535,21 +535,6 @@ @interface FSTQuery () { ResourcePath _path; } -/** - * Initializes the receiver with the given query constraints. - * - * @param path The base path of the query. - * @param filters Filters specify which documents to include in the results. - * @param sortOrders The fields and directions to sort the results. - * @param limit If not NSNotFound, only this many results will be returned. - */ -- (instancetype)initWithPath:(ResourcePath)path - filterBy:(NSArray *)filters - orderBy:(NSArray *)sortOrders - limit:(NSInteger)limit - startAt:(nullable FSTBound *)startAtBound - endAt:(nullable FSTBound *)endAtBound NS_DESIGNATED_INITIALIZER; - /** A list of fields given to sort by. This does not include the implicit key sort at the end. */ @property(nonatomic, strong, readonly) NSArray *explicitSortOrders; @@ -563,7 +548,13 @@ @implementation FSTQuery #pragma mark - Constructors + (instancetype)queryWithPath:(ResourcePath)path { + return [FSTQuery queryWithPath:std::move(path) collectionGroup:nil]; +} + ++ (instancetype)queryWithPath:(ResourcePath)path + collectionGroup:(nullable NSString *)collectionGroup { return [[FSTQuery alloc] initWithPath:std::move(path) + collectionGroup:collectionGroup filterBy:@[] orderBy:@[] limit:NSNotFound @@ -572,6 +563,7 @@ + (instancetype)queryWithPath:(ResourcePath)path { } - (instancetype)initWithPath:(ResourcePath)path + collectionGroup:(nullable NSString *)collectionGroup filterBy:(NSArray *)filters orderBy:(NSArray *)sortOrders limit:(NSInteger)limit @@ -579,6 +571,7 @@ - (instancetype)initWithPath:(ResourcePath)path endAt:(nullable FSTBound *)endAtBound { if (self = [super init]) { _path = std::move(path); + _collectionGroup = collectionGroup; _filters = filters; _explicitSortOrders = sortOrders; _limit = limit; @@ -662,7 +655,7 @@ - (NSArray *)sortOrders { } - (instancetype)queryByAddingFilter:(FSTFilter *)filter { - HARD_ASSERT(!DocumentKey::IsDocumentKey(_path), "No filtering allowed for document query"); + HARD_ASSERT(![self isDocumentQuery], "No filtering allowed for document query"); const FieldPath *newInequalityField = nullptr; if ([filter isKindOfClass:[FSTRelationFilter class]] && @@ -675,6 +668,7 @@ - (instancetype)queryByAddingFilter:(FSTFilter *)filter { "Query must only have one inequality field."); return [[FSTQuery alloc] initWithPath:self.path + collectionGroup:self.collectionGroup filterBy:[self.filters arrayByAddingObject:filter] orderBy:self.explicitSortOrders limit:self.limit @@ -683,10 +677,11 @@ - (instancetype)queryByAddingFilter:(FSTFilter *)filter { } - (instancetype)queryByAddingSortOrder:(FSTSortOrder *)sortOrder { - HARD_ASSERT(!DocumentKey::IsDocumentKey(_path), "No ordering is allowed for a document query."); + HARD_ASSERT(![self isDocumentQuery], "No ordering is allowed for a document query."); // TODO(klimt): Validate that the same key isn't added twice. return [[FSTQuery alloc] initWithPath:self.path + collectionGroup:self.collectionGroup filterBy:self.filters orderBy:[self.explicitSortOrders arrayByAddingObject:sortOrder] limit:self.limit @@ -696,6 +691,7 @@ - (instancetype)queryByAddingSortOrder:(FSTSortOrder *)sortOrder { - (instancetype)queryBySettingLimit:(NSInteger)limit { return [[FSTQuery alloc] initWithPath:self.path + collectionGroup:self.collectionGroup filterBy:self.filters orderBy:self.explicitSortOrders limit:limit @@ -705,6 +701,7 @@ - (instancetype)queryBySettingLimit:(NSInteger)limit { - (instancetype)queryByAddingStartAt:(FSTBound *)bound { return [[FSTQuery alloc] initWithPath:self.path + collectionGroup:self.collectionGroup filterBy:self.filters orderBy:self.explicitSortOrders limit:self.limit @@ -714,6 +711,7 @@ - (instancetype)queryByAddingStartAt:(FSTBound *)bound { - (instancetype)queryByAddingEndAt:(FSTBound *)bound { return [[FSTQuery alloc] initWithPath:self.path + collectionGroup:self.collectionGroup filterBy:self.filters orderBy:self.explicitSortOrders limit:self.limit @@ -721,13 +719,28 @@ - (instancetype)queryByAddingEndAt:(FSTBound *)bound { endAt:bound]; } +- (instancetype)collectionQueryAtPath:(firebase::firestore::model::ResourcePath)path { + return [[FSTQuery alloc] initWithPath:path + collectionGroup:nil + filterBy:self.filters + orderBy:self.explicitSortOrders + limit:self.limit + startAt:self.startAt + endAt:self.endAt]; +} + - (BOOL)isDocumentQuery { - return DocumentKey::IsDocumentKey(_path) && self.filters.count == 0; + return DocumentKey::IsDocumentKey(_path) && !self.collectionGroup && self.filters.count == 0; +} + +- (BOOL)isCollectionGroupQuery { + return self.collectionGroup != nil; } - (BOOL)matchesDocument:(FSTDocument *)document { - return [self pathMatchesDocument:document] && [self orderByMatchesDocument:document] && - [self filtersMatchDocument:document] && [self boundsMatchDocument:document]; + return [self pathAndCollectionGroupMatchDocument:document] && + [self orderByMatchesDocument:document] && [self filtersMatchDocument:document] && + [self boundsMatchDocument:document]; } - (NSComparator)comparator { @@ -787,6 +800,10 @@ - (NSString *)canonicalID { NSMutableString *canonicalID = [NSMutableString string]; [canonicalID appendFormat:@"%s", _path.CanonicalString().c_str()]; + if (self.collectionGroup) { + [canonicalID appendFormat:@"|cg:%@", self.collectionGroup]; + } + // Add filters. [canonicalID appendString:@"|f:"]; for (FSTFilter *predicate in self.filters) { @@ -819,16 +836,24 @@ - (NSString *)canonicalID { #pragma mark - Private methods - (BOOL)isEqualToQuery:(FSTQuery *)other { - return self.path == other.path && self.limit == other.limit && - [self.filters isEqual:other.filters] && [self.sortOrders isEqual:other.sortOrders] && + return self.path == other.path && + (self.collectionGroup == other.collectionGroup || + [self.collectionGroup isEqual:other.collectionGroup]) && + self.limit == other.limit && [self.filters isEqual:other.filters] && + [self.sortOrders isEqual:other.sortOrders] && (self.startAt == other.startAt || [self.startAt isEqual:other.startAt]) && (self.endAt == other.endAt || [self.endAt isEqual:other.endAt]); } -/* Returns YES if the document matches the path for the receiver. */ -- (BOOL)pathMatchesDocument:(FSTDocument *)document { +/* Returns YES if the document matches the path and collection group for the receiver. */ +- (BOOL)pathAndCollectionGroupMatchDocument:(FSTDocument *)document { const ResourcePath &documentPath = document.key.path(); - if (DocumentKey::IsDocumentKey(_path)) { + if (self.collectionGroup) { + // NOTE: self.path is currently always empty since we don't expose Collection Group queries + // rooted at a document path yet. + return document.key.HasCollectionId(util::MakeString(self.collectionGroup)) && + self.path.IsPrefixOf(documentPath); + } else if (DocumentKey::IsDocumentKey(_path)) { // Exact match for document queries. return self.path == documentPath; } else { diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index ae010cfc272..94b1c47958e 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -27,6 +27,8 @@ #include "Firestore/core/include/firebase/firestore/firestore_errors.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/index_manager.h" +#include "Firestore/core/src/firebase/firestore/local/leveldb_index_manager.h" #include "Firestore/core/src/firebase/firestore/local/leveldb_key.h" #include "Firestore/core/src/firebase/firestore/local/leveldb_migrations.h" #include "Firestore/core/src/firebase/firestore/local/leveldb_mutation_queue.h" @@ -59,8 +61,10 @@ using firebase::firestore::auth::User; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::local::ConvertStatus; +using firebase::firestore::local::IndexManager; using firebase::firestore::local::LevelDbDocumentMutationKey; using firebase::firestore::local::LevelDbDocumentTargetKey; +using firebase::firestore::local::LevelDbIndexManager; using firebase::firestore::local::LevelDbMigrations; using firebase::firestore::local::LevelDbMutationKey; using firebase::firestore::local::LevelDbMutationQueue; @@ -279,6 +283,7 @@ @implementation FSTLevelDB { std::unique_ptr _transaction; std::unique_ptr _ptr; std::unique_ptr _documentCache; + std::unique_ptr _indexManager; FSTTransactionRunner _transactionRunner; FSTLevelDBLRUDelegate *_referenceDelegate; std::unique_ptr _queryCache; @@ -351,6 +356,7 @@ - (instancetype)initWithLevelDB:(std::unique_ptr)db _serializer = serializer; _queryCache = absl::make_unique(self, _serializer); _documentCache = absl::make_unique(self, _serializer); + _indexManager = absl::make_unique(self); _referenceDelegate = [[FSTLevelDBLRUDelegate alloc] initWithPersistence:self lruParams:lruParams]; _transactionRunner.SetBackingPersistence(self); @@ -481,6 +487,10 @@ - (RemoteDocumentCache *)remoteDocumentCache { return _documentCache.get(); } +- (IndexManager *)indexManager { + return _indexManager.get(); +} + - (void)startTransaction:(absl::string_view)label { HARD_ASSERT(_transaction == nullptr, "Starting a transaction while one is already outstanding"); _transaction = absl::make_unique(_ptr.get(), label); diff --git a/Firestore/Source/Local/FSTLocalDocumentsView.h b/Firestore/Source/Local/FSTLocalDocumentsView.h index 730e0ea47bc..1072b1b63b1 100644 --- a/Firestore/Source/Local/FSTLocalDocumentsView.h +++ b/Firestore/Source/Local/FSTLocalDocumentsView.h @@ -16,6 +16,7 @@ #import +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" #include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" #include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" @@ -34,10 +35,11 @@ NS_ASSUME_NONNULL_BEGIN */ @interface FSTLocalDocumentsView : NSObject -+ (instancetype)viewWithRemoteDocumentCache: - (firebase::firestore::local::RemoteDocumentCache *)remoteDocumentCache - mutationQueue: - (firebase::firestore::local::MutationQueue *)mutationQueue; ++ (instancetype) + viewWithRemoteDocumentCache: + (firebase::firestore::local::RemoteDocumentCache *)remoteDocumentCache + mutationQueue:(firebase::firestore::local::MutationQueue *)mutationQueue + indexManager:(firebase::firestore::local::IndexManager *)indexManager; - (instancetype)init __attribute__((unavailable("Use a static constructor"))); diff --git a/Firestore/Source/Local/FSTLocalDocumentsView.mm b/Firestore/Source/Local/FSTLocalDocumentsView.mm index a357f5b5f4f..23a38276891 100644 --- a/Firestore/Source/Local/FSTLocalDocumentsView.mm +++ b/Firestore/Source/Local/FSTLocalDocumentsView.mm @@ -16,6 +16,7 @@ #import "Firestore/Source/Local/FSTLocalDocumentsView.h" +#include #include #import "Firestore/Source/Core/FSTQuery.h" @@ -23,6 +24,7 @@ #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Model/FSTMutationBatch.h" +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" #include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" #include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" @@ -30,7 +32,9 @@ #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +using firebase::firestore::local::IndexManager; using firebase::firestore::local::MutationQueue; using firebase::firestore::local::RemoteDocumentCache; using firebase::firestore::model::DocumentKey; @@ -39,32 +43,38 @@ using firebase::firestore::model::MaybeDocumentMap; using firebase::firestore::model::ResourcePath; using firebase::firestore::model::SnapshotVersion; +using firebase::firestore::util::MakeString; NS_ASSUME_NONNULL_BEGIN @interface FSTLocalDocumentsView () - (instancetype)initWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache mutationQueue:(MutationQueue *)mutationQueue - NS_DESIGNATED_INITIALIZER; + indexManager:(IndexManager *)indexManager NS_DESIGNATED_INITIALIZER; @end @implementation FSTLocalDocumentsView { RemoteDocumentCache *_remoteDocumentCache; MutationQueue *_mutationQueue; + IndexManager *_indexManager; } + (instancetype)viewWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache - mutationQueue:(MutationQueue *)mutationQueue { + mutationQueue:(MutationQueue *)mutationQueue + indexManager:(IndexManager *)indexManager { return [[FSTLocalDocumentsView alloc] initWithRemoteDocumentCache:remoteDocumentCache - mutationQueue:mutationQueue]; + mutationQueue:mutationQueue + indexManager:indexManager]; } - (instancetype)initWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache - mutationQueue:(MutationQueue *)mutationQueue { + mutationQueue:(MutationQueue *)mutationQueue + indexManager:(IndexManager *)indexManager { if (self = [super init]) { _remoteDocumentCache = remoteDocumentCache; _mutationQueue = mutationQueue; + _indexManager = indexManager; } return self; } @@ -142,8 +152,10 @@ - (MaybeDocumentMap)localViewsForDocuments:(const MaybeDocumentMap &)baseDocs { } - (DocumentMap)documentsMatchingQuery:(FSTQuery *)query { - if (DocumentKey::IsDocumentKey(query.path)) { + if ([query isDocumentQuery]) { return [self documentsMatchingDocumentQuery:query.path]; + } else if ([query isCollectionGroupQuery]) { + return [self documentsMatchingCollectionGroupQuery:query]; } else { return [self documentsMatchingCollectionQuery:query]; } @@ -159,6 +171,28 @@ - (DocumentMap)documentsMatchingDocumentQuery:(const ResourcePath &)docPath { return result; } +- (DocumentMap)documentsMatchingCollectionGroupQuery:(FSTQuery *)query { + HARD_ASSERT(query.path.empty(), + "Currently we only support collection group queries at the root."); + + std::string collection_id = MakeString(query.collectionGroup); + std::vector parents = _indexManager->GetCollectionParents(collection_id); + DocumentMap results; + + // Perform a collection query against each parent that contains the collection_id and + // aggregate the results. + for (const ResourcePath &parent : parents) { + FSTQuery *collectionQuery = [query collectionQueryAtPath:parent.Append(collection_id)]; + DocumentMap collectionResults = [self documentsMatchingCollectionQuery:collectionQuery]; + for (const auto &kv : collectionResults.underlying_map()) { + const DocumentKey &key = kv.first; + FSTDocument *doc = static_cast(kv.second); + results = results.insert(key, doc); + } + } + return results; +} + - (DocumentMap)documentsMatchingCollectionQuery:(FSTQuery *)query { DocumentMap results = _remoteDocumentCache->GetMatching(query); // Get locally persisted mutation batches. diff --git a/Firestore/Source/Local/FSTLocalStore.mm b/Firestore/Source/Local/FSTLocalStore.mm index 7a6785242f3..a5115d2e825 100644 --- a/Firestore/Source/Local/FSTLocalStore.mm +++ b/Firestore/Source/Local/FSTLocalStore.mm @@ -113,8 +113,10 @@ - (instancetype)initWithPersistence:(id)persistence _mutationQueue = [persistence mutationQueueForUser:initialUser]; _remoteDocumentCache = [persistence remoteDocumentCache]; _queryCache = [persistence queryCache]; - _localDocuments = [FSTLocalDocumentsView viewWithRemoteDocumentCache:_remoteDocumentCache - mutationQueue:_mutationQueue]; + _localDocuments = + [FSTLocalDocumentsView viewWithRemoteDocumentCache:_remoteDocumentCache + mutationQueue:_mutationQueue + indexManager:[persistence indexManager]]; [_persistence.referenceDelegate addInMemoryPins:&_localViewReferences]; _targetIDGenerator = TargetIdGenerator::QueryCacheTargetIdGenerator(0); @@ -148,8 +150,10 @@ - (MaybeDocumentMap)userDidChange:(const User &)user { std::vector newBatches = _mutationQueue->AllMutationBatches(); // Recreate our LocalDocumentsView using the new MutationQueue. - self.localDocuments = [FSTLocalDocumentsView viewWithRemoteDocumentCache:_remoteDocumentCache - mutationQueue:_mutationQueue]; + self.localDocuments = + [FSTLocalDocumentsView viewWithRemoteDocumentCache:_remoteDocumentCache + mutationQueue:_mutationQueue + indexManager:[_persistence indexManager]]; // Union the old/new changed keys. DocumentKeySet changedKeys; diff --git a/Firestore/Source/Local/FSTMemoryPersistence.mm b/Firestore/Source/Local/FSTMemoryPersistence.mm index 6dc197cab32..fa7b4657b24 100644 --- a/Firestore/Source/Local/FSTMemoryPersistence.mm +++ b/Firestore/Source/Local/FSTMemoryPersistence.mm @@ -22,7 +22,9 @@ #include #include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" #include "Firestore/core/src/firebase/firestore/local/listen_sequence.h" +#include "Firestore/core/src/firebase/firestore/local/memory_index_manager.h" #include "Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h" #include "Firestore/core/src/firebase/firestore/local/memory_query_cache.h" #include "Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h" @@ -35,6 +37,7 @@ using firebase::firestore::auth::User; using firebase::firestore::local::ListenSequence; using firebase::firestore::local::LruParams; +using firebase::firestore::local::MemoryIndexManager; using firebase::firestore::local::MemoryMutationQueue; using firebase::firestore::local::MemoryQueryCache; using firebase::firestore::local::MemoryRemoteDocumentCache; @@ -55,6 +58,8 @@ - (MemoryQueryCache *)queryCache; - (MemoryRemoteDocumentCache *)remoteDocumentCache; +- (MemoryIndexManager *)indexManager; + - (MemoryMutationQueue *)mutationQueueForUser:(const User &)user; @property(nonatomic, readonly) MutationQueues &mutationQueues; @@ -78,7 +83,9 @@ @implementation FSTMemoryPersistence { std::unique_ptr _queryCache; /** The RemoteDocumentCache representing the persisted cache of remote documents. */ - MemoryRemoteDocumentCache _remoteDocumentCache; + std::unique_ptr _remoteDocumentCache; + + MemoryIndexManager _indexManager; FSTTransactionRunner _transactionRunner; @@ -105,6 +112,7 @@ + (instancetype)persistenceWithLruParams:(firebase::firestore::local::LruParams) - (instancetype)init { if (self = [super init]) { _queryCache = absl::make_unique(self); + _remoteDocumentCache = absl::make_unique(self); self.started = YES; } return self; @@ -151,7 +159,11 @@ - (MemoryQueryCache *)queryCache { } - (MemoryRemoteDocumentCache *)remoteDocumentCache { - return &_remoteDocumentCache; + return _remoteDocumentCache.get(); +} + +- (MemoryIndexManager *)indexManager { + return &_indexManager; } @end diff --git a/Firestore/Source/Local/FSTPersistence.h b/Firestore/Source/Local/FSTPersistence.h index 961ad0dcbf7..8595f107a1e 100644 --- a/Firestore/Source/Local/FSTPersistence.h +++ b/Firestore/Source/Local/FSTPersistence.h @@ -17,6 +17,7 @@ #import #include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" #include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" #include "Firestore/core/src/firebase/firestore/local/query_cache.h" #include "Firestore/core/src/firebase/firestore/local/reference_set.h" @@ -82,9 +83,12 @@ NS_ASSUME_NONNULL_BEGIN /** Creates an FSTQueryCache representing the persisted cache of queries. */ - (firebase::firestore::local::QueryCache *)queryCache; -/** Creates an FSTRemoteDocumentCache representing the persisted cache of remote documents. */ +/** Creates a RemoteDocumentCache representing the persisted cache of remote documents. */ - (firebase::firestore::local::RemoteDocumentCache *)remoteDocumentCache; +/** Creates an IndexManager that manages our persisted query indexes. */ +- (firebase::firestore::local::IndexManager *)indexManager; + @property(nonatomic, readonly, assign) const FSTTransactionRunner &run; /** diff --git a/Firestore/Source/Public/FIRFirestore.h b/Firestore/Source/Public/FIRFirestore.h index f2c914398ee..968a7511c2a 100644 --- a/Firestore/Source/Public/FIRFirestore.h +++ b/Firestore/Source/Public/FIRFirestore.h @@ -20,6 +20,7 @@ @class FIRCollectionReference; @class FIRDocumentReference; @class FIRFirestoreSettings; +@class FIRQuery; @class FIRTransaction; @class FIRWriteBatch; diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm index 3e8fc6809da..e1115729fd8 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.mm +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -777,10 +777,16 @@ - (FSTQuery *)decodedQueryFromDocumentsTarget:(GCFSTarget_DocumentsTarget *)targ - (GCFSTarget_QueryTarget *)encodedQueryTarget:(FSTQuery *)query { // Dissect the path into parent, collectionId, and optional key filter. GCFSTarget_QueryTarget *queryTarget = [GCFSTarget_QueryTarget message]; - if (query.path.size() == 0) { - queryTarget.parent = [self encodedQueryPath:query.path]; + const ResourcePath &path = query.path; + if (query.collectionGroup) { + HARD_ASSERT(path.size() % 2 == 0, + "Collection group queries should be within a document path or root."); + queryTarget.parent = [self encodedQueryPath:path]; + GCFSStructuredQuery_CollectionSelector *from = [GCFSStructuredQuery_CollectionSelector message]; + from.collectionId = query.collectionGroup; + from.allDescendants = YES; + [queryTarget.structuredQuery.fromArray addObject:from]; } else { - const ResourcePath &path = query.path; HARD_ASSERT(path.size() % 2 != 0, "Document queries with filters are not supported."); queryTarget.parent = [self encodedQueryPath:path.PopLast()]; GCFSStructuredQuery_CollectionSelector *from = [GCFSStructuredQuery_CollectionSelector message]; @@ -818,13 +824,18 @@ - (FSTQuery *)decodedQueryFromQueryTarget:(GCFSTarget_QueryTarget *)target { ResourcePath path = [self decodedQueryPath:target.parent]; GCFSStructuredQuery *query = target.structuredQuery; + NSString *collectionGroup; NSUInteger fromCount = query.fromArray_Count; if (fromCount > 0) { HARD_ASSERT(fromCount == 1, "StructuredQuery.from with more than one collection is not supported."); GCFSStructuredQuery_CollectionSelector *from = query.fromArray[0]; - path = path.Append(util::MakeString(from.collectionId)); + if (from.allDescendants) { + collectionGroup = from.collectionId; + } else { + path = path.Append(util::MakeString(from.collectionId)); + } } NSArray *filterBy; @@ -857,6 +868,7 @@ - (FSTQuery *)decodedQueryFromQueryTarget:(GCFSTarget_QueryTarget *)target { } return [[FSTQuery alloc] initWithPath:path + collectionGroup:collectionGroup filterBy:filterBy orderBy:orderBy limit:limit diff --git a/Firestore/Swift/Tests/API/BasicCompileTests.swift b/Firestore/Swift/Tests/API/BasicCompileTests.swift index 0889979c94e..fb117caf981 100644 --- a/Firestore/Swift/Tests/API/BasicCompileTests.swift +++ b/Firestore/Swift/Tests/API/BasicCompileTests.swift @@ -90,7 +90,7 @@ func makeRefs(database db: Firestore) -> (CollectionReference, DocumentReference } func makeQuery(collection collectionRef: CollectionReference) -> Query { - let query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") + var query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") .whereField("age", isGreaterThanOrEqualTo: 24) .whereField("tags", arrayContains: "active") .whereField(FieldPath(["tags"]), arrayContains: "active") @@ -99,6 +99,9 @@ func makeQuery(collection collectionRef: CollectionReference) -> Query { .order(by: "name", descending: true) .limit(to: 10) + // TODO(b/116617988): collectionGroup query. + // query = collectionRef.firestore.collectionGroup("collection") + return query } diff --git a/Firestore/core/src/firebase/firestore/core/query.h b/Firestore/core/src/firebase/firestore/core/query.h index 41d0df136bf..1d9bb37a6b9 100644 --- a/Firestore/core/src/firebase/firestore/core/query.h +++ b/Firestore/core/src/firebase/firestore/core/query.h @@ -92,6 +92,8 @@ class Query { // existing filters, plus the new one. (Both Query and Filter objects are // immutable.) Filters are not shared across unrelated Query instances. std::vector> filters_; + + // TODO(rsgowman): Port collection group queries logic. }; inline bool operator==(const Query& lhs, const Query& rhs) { diff --git a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt index f27fcb2f82b..8341c79df17 100644 --- a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt @@ -18,6 +18,7 @@ if(HAVE_LEVELDB) SOURCES leveldb_key.cc leveldb_key.h + #leveldb_index_manager.mm leveldb_migrations.cc leveldb_migrations.h leveldb_transaction.cc @@ -50,6 +51,7 @@ cc_library( document_reference.cc local_serializer.h local_serializer.cc + memory_index_manager.cc query_data.cc query_data.h reference_set.cc diff --git a/Firestore/core/src/firebase/firestore/local/index_manager.h b/Firestore/core/src/firebase/firestore/local/index_manager.h new file mode 100644 index 00000000000..c4bad32eb86 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/index_manager.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019 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_LOCAL_INDEX_MANAGER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_INDEX_MANAGER_H_ + +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" + +namespace firebase { +namespace firestore { +namespace local { + +/** + * Represents a set of indexes that are used to execute queries efficiently. + * + * Currently the only index is a [collection id] => [parent path] index, used + * to execute Collection Group queries. + */ +class IndexManager { + public: + virtual ~IndexManager() { + } + + /** + * Creates an index entry mapping the collectionId (last segment of the path) + * to the parent path (either the containing document location or the empty + * path for root-level collections). Index entries can be retrieved via + * GetCollectionParents(). + * + * NOTE: Currently we don't remove index entries. If this ends up being an + * issue we can devise some sort of GC strategy. + */ + virtual void AddToCollectionParentIndex( + const model::ResourcePath& collection_path) = 0; + + /** + * Retrieves all parent locations containing the given collectionId, as a set + * of paths (each path being either a document location or the empty path for + * a root-level collection). + */ + virtual std::vector GetCollectionParents( + const std::string& collection_id) = 0; +}; + +} // namespace local +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_INDEX_MANAGER_H_ diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_index_manager.h b/Firestore/core/src/firebase/firestore/local/leveldb_index_manager.h new file mode 100644 index 00000000000..3b44cbd0d2e --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/leveldb_index_manager.h @@ -0,0 +1,70 @@ +/* + * Copyright 2019 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_LOCAL_LEVELDB_INDEX_MANAGER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LEVELDB_INDEX_MANAGER_H_ + +#if !defined(__OBJC__) +#error "For now, this file must only be included by ObjC source files." +#endif // !defined(__OBJC__) + +#include +#include + +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" +#include "Firestore/core/src/firebase/firestore/local/memory_index_manager.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" + +@class FSTLevelDB; + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace local { + +/** A persisted implementation of IndexManager. */ +class LevelDbIndexManager : public IndexManager { + public: + explicit LevelDbIndexManager(FSTLevelDB* db); + + void AddToCollectionParentIndex( + const model::ResourcePath& collection_path) override; + + std::vector GetCollectionParents( + const std::string& collection_id) override; + + private: + // This instance is owned by FSTLevelDB; avoid a retain cycle. + __weak FSTLevelDB* db_; + + /** + * An in-memory copy of the index entries we've already written since the SDK + * launched. Used to avoid re-writing the same entry repeatedly. + * + * This is *NOT* a complete cache of what's in persistence and so can never + * be used to satisfy reads. + */ + MemoryCollectionParentIndex collection_parents_cache_; +}; + +} // namespace local +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LEVELDB_INDEX_MANAGER_H_ diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_index_manager.mm b/Firestore/core/src/firebase/firestore/local/leveldb_index_manager.mm new file mode 100644 index 00000000000..ef7773b541e --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/leveldb_index_manager.mm @@ -0,0 +1,81 @@ +/* + * Copyright 2019 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/local/leveldb_index_manager.h" + +#include +#include + +#include "Firestore/core/src/firebase/firestore/local/leveldb_key.h" +#include "Firestore/core/src/firebase/firestore/local/memory_index_manager.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "absl/strings/match.h" + +#import "Firestore/Source/Local/FSTLevelDB.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace local { + +using model::ResourcePath; + +LevelDbIndexManager::LevelDbIndexManager(FSTLevelDB* db) : db_(db) { +} + +void LevelDbIndexManager::AddToCollectionParentIndex( + const ResourcePath& collection_path) { + HARD_ASSERT(collection_path.size() % 2 == 1, "Expected a collection path."); + + if (collection_parents_cache_.Add(collection_path)) { + std::string collection_id = collection_path.last_segment(); + ResourcePath parent_path = collection_path.PopLast(); + + std::string key = + LevelDbCollectionParentKey::Key(collection_id, parent_path); + std::string empty_buffer; + db_.currentTransaction->Put(key, empty_buffer); + } +} + +std::vector LevelDbIndexManager::GetCollectionParents( + const std::string& collection_id) { + std::vector results; + + auto index_iterator = db_.currentTransaction->NewIterator(); + std::string index_prefix = + LevelDbCollectionParentKey::KeyPrefix(collection_id); + LevelDbCollectionParentKey row_key; + for (index_iterator->Seek(index_prefix); index_iterator->Valid(); + index_iterator->Next()) { + if (!absl::StartsWith(index_iterator->key(), index_prefix) || + !row_key.Decode(index_iterator->key()) || + row_key.collection_id() != collection_id) { + break; + } + + results.push_back(row_key.parent()); + } + return results; +} + +} // namespace local +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_key.cc b/Firestore/core/src/firebase/firestore/local/leveldb_key.cc index d0875dc65d5..e0f632896a2 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_key.cc +++ b/Firestore/core/src/firebase/firestore/local/leveldb_key.cc @@ -45,6 +45,7 @@ const char* kQueryTargetsTable = "query_target"; const char* kTargetDocumentsTable = "target_document"; const char* kDocumentTargetsTable = "document_target"; const char* kRemoteDocumentsTable = "remote_document"; +const char* kCollectionParentsTable = "collection_parent"; /** * Labels for the components of keys. These serve to make keys self-describing. @@ -89,6 +90,12 @@ enum ComponentLabel { /** A component containing a user Id. */ UserId = 13, + /** + * A component containing a standalone collection ID (e.g. as used by the + * collection_parent table, but not for collection IDs within paths). + */ + CollectionId = 14, + /** * A path segment describes just a single segment in a resource path. Path * segments that occur sequentially in a key represent successive segments in @@ -159,6 +166,17 @@ class Reader { return ReadLabeledString(ComponentLabel::UserId); } + std::string ReadCollectionId() { + return ReadLabeledString(ComponentLabel::CollectionId); + } + + /** + * Reads component labels and strings from the key until it finds a component + * label other than ComponentLabel::PathSegment (or the key is exhausted). + * All matched path segments are assembled into a ResourcePath. + */ + ResourcePath ReadResourcePath(); + /** * Reads component labels and strings from the key until it finds a component * label other than ComponentLabel::PathSegment (or the key is exhausted). @@ -358,7 +376,7 @@ class Reader { bool ok_; }; -DocumentKey Reader::ReadDocumentKey() { +ResourcePath Reader::ReadResourcePath() { std::vector path_segments; while (!empty()) { // Advance a temporary slice to avoid advancing contents into the next key @@ -375,7 +393,13 @@ DocumentKey Reader::ReadDocumentKey() { path_segments.push_back(std::move(segment)); } - ResourcePath path{std::move(path_segments)}; + return ResourcePath{std::move(path_segments)}; +} + +DocumentKey Reader::ReadDocumentKey() { + ResourcePath path = ReadResourcePath(); + + // Avoid assertion failures in DocumentKey if path is invalid. if (ok_ && !path.empty() && DocumentKey::IsDocumentKey(path)) { return DocumentKey{std::move(path)}; } @@ -419,10 +443,10 @@ std::string Reader::Describe() { src_ = saved_source; if (label == ComponentLabel::PathSegment) { - DocumentKey document_key = ReadDocumentKey(); + ResourcePath resource_path = ReadResourcePath(); if (ok_) { absl::StrAppend(&description, - " key=", document_key.path().CanonicalString()); + " path=", resource_path.CanonicalString()); } } else if (label == ComponentLabel::TableName) { @@ -455,6 +479,12 @@ std::string Reader::Describe() { absl::StrAppend(&description, " user_id=", user_id); } + } else if (label == ComponentLabel::CollectionId) { + std::string collection_id = ReadCollectionId(); + if (ok_) { + absl::StrAppend(&description, " collection_id=", collection_id); + } + } else { absl::StrAppend(&description, " unknown label=", static_cast(label)); Fail(); @@ -502,6 +532,10 @@ class Writer { WriteLabeledString(ComponentLabel::UserId, user_id); } + void WriteCollectionId(absl::string_view collection_id) { + WriteLabeledString(ComponentLabel::CollectionId, collection_id); + } + /** * For each segment in the given resource path writes a * ComponentLabel::PathSegment component label and a string containing the @@ -848,6 +882,39 @@ bool LevelDbRemoteDocumentKey::Decode(absl::string_view key) { return reader.ok(); } +std::string LevelDbCollectionParentKey::KeyPrefix() { + Writer writer; + writer.WriteTableName(kCollectionParentsTable); + return writer.result(); +} + +std::string LevelDbCollectionParentKey::KeyPrefix( + absl::string_view collection_id) { + Writer writer; + writer.WriteTableName(kCollectionParentsTable); + writer.WriteCollectionId(collection_id); + return writer.result(); +} + +std::string LevelDbCollectionParentKey::Key(absl::string_view collection_id, + const ResourcePath& parent) { + Writer writer; + writer.WriteTableName(kCollectionParentsTable); + writer.WriteCollectionId(collection_id); + writer.WriteResourcePath(parent); + writer.WriteTerminator(); + return writer.result(); +} + +bool LevelDbCollectionParentKey::Decode(absl::string_view key) { + Reader reader{key}; + reader.ReadTableNameMatching(kCollectionParentsTable); + collection_id_ = reader.ReadCollectionId(); + parent_ = reader.ReadResourcePath(); + reader.ReadTerminator(); + return reader.ok(); +} + } // namespace local } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_key.h b/Firestore/core/src/firebase/firestore/local/leveldb_key.h index 4bbf738e5ed..505cb871214 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_key.h +++ b/Firestore/core/src/firebase/firestore/local/leveldb_key.h @@ -77,6 +77,11 @@ namespace local { // remote_documents: // - table_name: string = "remote_document" // - path: ResourcePath +// +// collection_parents: +// - table_name: string = "collection_parent" +// - collectionId: string +// - parent: ResourcePath /** * Parses the given key and returns a human readable description of its @@ -520,6 +525,59 @@ class LevelDbRemoteDocumentKey { model::DocumentKey document_key_; }; +/** + * A key in the collection parents index, which stores an association between a + * Collection ID (e.g. 'messages') to a parent path (e.g. '/chats/123') that + * contains it as a (sub)collection. This is used to efficiently find all + * collections to query when performing a Collection Group query. Note that the + * parent path will be an empty path in the case of root-level collections. + */ +class LevelDbCollectionParentKey { + public: + /** + * Creates a key prefix that points just before the first key in the table. + */ + static std::string KeyPrefix(); + + /** + * Creates a key prefix that points just before the first key for the given + * collection_id. + */ + static std::string KeyPrefix(absl::string_view collection_id); + + /** + * Creates a complete key that points to a specific collection_id and parent. + */ + static std::string Key(absl::string_view collection_id, + const model::ResourcePath& parent); + + /** + * Decodes the given complete key, storing the decoded values in this + * instance. + * + * @return true if the key successfully decoded, false otherwise. If false is + * returned, this instance is in an undefined state until the next call to + * `Decode()`. + */ + ABSL_MUST_USE_RESULT + bool Decode(absl::string_view key); + + /** The collection_id, as encoded in the key. */ + const std::string& collection_id() const { + return collection_id_; + } + + /** The parent path, as encoded in the key. */ + const model::ResourcePath& parent() const { + return parent_; + } + + private: + // Deliberately uninitialized: will be assigned in Decode + std::string collection_id_; + model::ResourcePath parent_; +}; + } // namespace local } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_migrations.cc b/Firestore/core/src/firebase/firestore/local/leveldb_migrations.cc index 8b780a104d3..dda29e78354 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_migrations.cc +++ b/Firestore/core/src/firebase/firestore/local/leveldb_migrations.cc @@ -22,6 +22,7 @@ #include "Firestore/Protos/nanopb/firestore/local/mutation.nanopb.h" #include "Firestore/Protos/nanopb/firestore/local/target.nanopb.h" #include "Firestore/core/src/firebase/firestore/local/leveldb_key.h" +#include "Firestore/core/src/firebase/firestore/local/memory_index_manager.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/nanopb/reader.h" @@ -36,6 +37,8 @@ using leveldb::Iterator; using leveldb::Slice; using leveldb::Status; using leveldb::WriteOptions; +using model::DocumentKey; +using model::ResourcePath; using nanopb::Reader; using nanopb::Writer; @@ -60,8 +63,9 @@ namespace { * * Migration 4 ensures that every document in the remote document cache * has a sentinel row with a sequence number. * * Migration 5 drops held write acks. + * * Migration 6 populates the collection_parents index. */ -const LevelDbMigrations::SchemaVersion kSchemaVersion = 5; +const LevelDbMigrations::SchemaVersion kSchemaVersion = 6; /** * Save the given version number as the current version of the schema of the @@ -214,6 +218,8 @@ void EnsureSentinelRow(LevelDbTransaction* transaction, } /** + * Migration 4. + * * Ensure each document in the remote document table has a corresponding * sentinel row in the document target index. */ @@ -241,6 +247,65 @@ void EnsureSentinelRows(leveldb::DB* db) { transaction.Commit(); } +// Helper to add an index entry iff we haven't already written it (as determined +// by the provided cache). +void EnsureCollectionParentRow(LevelDbTransaction* transaction, + MemoryCollectionParentIndex* cache, + const DocumentKey& key) { + const ResourcePath& collection_path = key.path().PopLast(); + if (cache->Add(collection_path)) { + std::string collection_id = collection_path.last_segment(); + ResourcePath parent_path = collection_path.PopLast(); + + std::string key = + LevelDbCollectionParentKey::Key(collection_id, parent_path); + std::string empty_buffer; + transaction->Put(key, empty_buffer); + } +} + +/** + * Migration 6. + * + * Creates appropriate LevelDbCollectionParentKey rows for all collections + * of documents in the remote document cache and mutation queue. + */ +void EnsureCollectionParentsIndex(leveldb::DB* db) { + LevelDbTransaction transaction(db, "Ensure Collection Parents Index"); + + MemoryCollectionParentIndex cache; + + // Index existing remote documents. + std::string documents_prefix = LevelDbRemoteDocumentKey::KeyPrefix(); + auto it = transaction.NewIterator(); + it->Seek(documents_prefix); + LevelDbRemoteDocumentKey document_key; + for (; it->Valid() && absl::StartsWith(it->key(), documents_prefix); + it->Next()) { + HARD_ASSERT(document_key.Decode(it->key()), + "Failed to decode document key"); + + EnsureCollectionParentRow(&transaction, &cache, + document_key.document_key()); + } + + // Index existing mutations. + std::string mutations_prefix = LevelDbDocumentMutationKey::KeyPrefix(); + it = transaction.NewIterator(); + it->Seek(mutations_prefix); + LevelDbDocumentMutationKey key; + for (; it->Valid() && absl::StartsWith(it->key(), mutations_prefix); + it->Next()) { + HARD_ASSERT(key.Decode(it->key()), + "Failed to decode document-mutation key"); + + EnsureCollectionParentRow(&transaction, &cache, key.document_key()); + } + + SaveVersion(6, &transaction); + transaction.Commit(); +} + } // namespace LevelDbMigrations::SchemaVersion LevelDbMigrations::ReadSchemaVersion( @@ -287,6 +352,10 @@ void LevelDbMigrations::RunMigrations(leveldb::DB* db, if (from_version < 5 && to_version >= 5) { RemoveAcknowledgedMutations(db); } + + if (from_version < 6 && to_version >= 6) { + EnsureCollectionParentsIndex(db); + } } } // namespace local diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_mutation_queue.mm b/Firestore/core/src/firebase/firestore/local/leveldb_mutation_queue.mm index 53928703206..0abb5f14bd1 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_mutation_queue.mm +++ b/Firestore/core/src/firebase/firestore/local/leveldb_mutation_queue.mm @@ -179,6 +179,8 @@ BatchId LoadNextBatchIdFromDb(DB* db) { for (FSTMutation* mutation : [batch mutations]) { key = LevelDbDocumentMutationKey::Key(user_id_, mutation.key, batch_id); db_.currentTransaction->Put(key, empty_buffer); + + db_.indexManager->AddToCollectionParentIndex(mutation.key.path().PopLast()); } return batch; @@ -270,6 +272,9 @@ BatchId LoadNextBatchIdFromDb(DB* db) { LevelDbMutationQueue::AllMutationBatchesAffectingQuery(FSTQuery* query) { HARD_ASSERT(![query isDocumentQuery], "Document queries shouldn't go down this path"); + HARD_ASSERT( + ![query isCollectionGroupQuery], + "CollectionGroup queries should be handled in LocalDocumentsView"); const ResourcePath& query_path = query.path; size_t immediate_children_path_length = query_path.size() + 1; diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.mm b/Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.mm index 07bb1205c59..69002e03795 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.mm +++ b/Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.mm @@ -47,6 +47,8 @@ std::string ldb_key = LevelDbRemoteDocumentKey::Key(document.key); db_.currentTransaction->Put(ldb_key, [serializer_ encodedMaybeDocument:document]); + + db_.indexManager->AddToCollectionParentIndex(document.key.path().PopLast()); } void LevelDbRemoteDocumentCache::Remove(const DocumentKey& key) { @@ -90,6 +92,10 @@ } DocumentMap LevelDbRemoteDocumentCache::GetMatching(FSTQuery* query) { + HARD_ASSERT( + ![query isCollectionGroupQuery], + "CollectionGroup queries should be handled in LocalDocumentsView"); + DocumentMap results; // Use the query path as a prefix for testing if a document matches the query. diff --git a/Firestore/core/src/firebase/firestore/local/memory_index_manager.cc b/Firestore/core/src/firebase/firestore/local/memory_index_manager.cc new file mode 100644 index 00000000000..08274167c3c --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/memory_index_manager.cc @@ -0,0 +1,67 @@ +/* + * Copyright 2019 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/local/memory_index_manager.h" + +#include +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + +namespace firebase { +namespace firestore { +namespace local { + +using model::ResourcePath; + +bool MemoryCollectionParentIndex::Add(const ResourcePath& collection_path) { + HARD_ASSERT(collection_path.size() % 2 == 1, "Expected a collection path."); + + std::string collection_id = collection_path.last_segment(); + ResourcePath parent_path = collection_path.PopLast(); + std::set& existingParents = index_[collection_id]; + bool inserted = existingParents.insert(parent_path).second; + return inserted; +} + +std::vector MemoryCollectionParentIndex::GetEntries( + const std::string& collection_id) const { + std::vector result; + auto found = index_.find(collection_id); + if (found != index_.end()) { + const std::set& parent_paths = found->second; + std::copy(parent_paths.begin(), parent_paths.end(), + std::back_inserter(result)); + } + return result; +} + +void MemoryIndexManager::AddToCollectionParentIndex( + const ResourcePath& collection_path) { + collection_parents_index_.Add(collection_path); +} + +std::vector MemoryIndexManager::GetCollectionParents( + const std::string& collection_id) { + return collection_parents_index_.GetEntries(collection_id); +} + +} // namespace local +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/local/memory_index_manager.h b/Firestore/core/src/firebase/firestore/local/memory_index_manager.h new file mode 100644 index 00000000000..06a0eb83ba1 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/memory_index_manager.h @@ -0,0 +1,66 @@ +/* + * Copyright 2019 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_LOCAL_MEMORY_INDEX_MANAGER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_MEMORY_INDEX_MANAGER_H_ + +#include +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" + +namespace firebase { +namespace firestore { +namespace local { + +/** + * Internal implementation of the collection-parent index. Also used for + * in-memory caching by LevelDbIndexManager and initial index population during + * schema migration. + */ +class MemoryCollectionParentIndex { + public: + // Returns false if the entry already existed. + bool Add(const model::ResourcePath& collection_path); + + std::vector GetEntries( + const std::string& collection_id) const; + + private: + std::unordered_map> index_; +}; + +/** An in-memory implementation of IndexManager. */ +class MemoryIndexManager : public IndexManager { + public: + void AddToCollectionParentIndex( + const model::ResourcePath& collection_path) override; + + std::vector GetCollectionParents( + const std::string& collection_id) override; + + private: + MemoryCollectionParentIndex collection_parents_index_; +}; + +} // namespace local +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_MEMORY_INDEX_MANAGER_H_ diff --git a/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.mm b/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.mm index c17dfd2c90c..52ff632d808 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.mm +++ b/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.mm @@ -95,10 +95,13 @@ mutations:std::move(mutations)]; queue_.push_back(batch); - // Track references by document key. + // Track references by document key and index collection parents. for (FSTMutation* mutation : [batch mutations]) { batches_by_document_key_ = batches_by_document_key_.insert( DocumentReference{mutation.key, batch_id}); + + persistence_.indexManager->AddToCollectionParentIndex( + mutation.key.path().PopLast()); } return batch; @@ -159,6 +162,10 @@ std::vector MemoryMutationQueue::AllMutationBatchesAffectingQuery(FSTQuery* query) { + HARD_ASSERT( + ![query isCollectionGroupQuery], + "CollectionGroup queries should be handled in LocalDocumentsView"); + // Use the query path as a prefix for testing if a document matches the query. const ResourcePath& prefix = query.path; size_t immediate_children_path_length = prefix.size() + 1; diff --git a/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h index d1eebd08e99..85bddc37472 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h +++ b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h @@ -32,6 +32,7 @@ @class FSTLocalSerializer; @class FSTMaybeDocument; @class FSTMemoryLRUReferenceDelegate; +@class FSTMemoryPersistence; @class FSTQuery; NS_ASSUME_NONNULL_BEGIN @@ -42,6 +43,8 @@ namespace local { class MemoryRemoteDocumentCache : public RemoteDocumentCache { public: + explicit MemoryRemoteDocumentCache(FSTMemoryPersistence *persistence); + void Add(FSTMaybeDocument *document) override; void Remove(const model::DocumentKey &key) override; @@ -58,6 +61,8 @@ class MemoryRemoteDocumentCache : public RemoteDocumentCache { private: /** Underlying cache of documents. */ model::MaybeDocumentMap docs_; + + FSTMemoryPersistence *persistence_; }; } // namespace local diff --git a/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.mm b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.mm index 2514eaaa16d..82503a0f604 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.mm +++ b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.mm @@ -20,6 +20,8 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTMemoryPersistence.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + using firebase::firestore::model::DocumentKey; using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::DocumentMap; @@ -45,8 +47,16 @@ size_t DocumentKeyByteSize(const DocumentKey& key) { } } // namespace +MemoryRemoteDocumentCache::MemoryRemoteDocumentCache( + FSTMemoryPersistence* persistence) { + persistence_ = persistence; +} + void MemoryRemoteDocumentCache::Add(FSTMaybeDocument* document) { docs_ = docs_.insert(document.key, document); + + persistence_.indexManager->AddToCollectionParentIndex( + document.key.path().PopLast()); } void MemoryRemoteDocumentCache::Remove(const DocumentKey& key) { @@ -71,6 +81,10 @@ size_t DocumentKeyByteSize(const DocumentKey& key) { } DocumentMap MemoryRemoteDocumentCache::GetMatching(FSTQuery* query) { + HARD_ASSERT( + ![query isCollectionGroupQuery], + "CollectionGroup queries should be handled in LocalDocumentsView"); + DocumentMap results; // Documents are ordered by key, so we can use a prefix scan to narrow down @@ -122,4 +136,4 @@ size_t DocumentKeyByteSize(const DocumentKey& key) { } // namespace local } // namespace firestore -} // namespace firebase \ No newline at end of file +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/document_key.h b/Firestore/core/src/firebase/firestore/model/document_key.h index ab4e3373648..2443e26a9fb 100644 --- a/Firestore/core/src/firebase/firestore/model/document_key.h +++ b/Firestore/core/src/firebase/firestore/model/document_key.h @@ -64,7 +64,7 @@ class DocumentKey { * Creates and returns a new document key using '/' to split the string into * segments. */ - static DocumentKey FromPathString(const absl::string_view path) { + static DocumentKey FromPathString(absl::string_view path) { return DocumentKey{ResourcePath::FromString(path)}; } @@ -86,6 +86,12 @@ class DocumentKey { return path_ ? *path_ : Empty().path(); } + /** Returns true if the document is in the specified collectionId. */ + bool HasCollectionId(absl::string_view collection_id) const { + size_t size = path().size(); + return size >= 2 && path()[size - 2] == collection_id; + } + private: // This is an optimization to make passing DocumentKey around cheaper (it's // copied often). diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc index a9c825047a6..fc727da2ef0 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.cc +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -701,6 +701,7 @@ google_firestore_v1_Target_QueryTarget Serializer::EncodeQueryTarget( // Dissect the path into parent, collection_id and optional key filter. std::string collection_id; + // TODO(rsgowman): Port Collection Group Queries logic. if (query.path().empty()) { result.parent = EncodeString(EncodeQueryPath(ResourcePath::Empty())); } else { diff --git a/Firestore/core/test/firebase/firestore/local/CMakeLists.txt b/Firestore/core/test/firebase/firestore/local/CMakeLists.txt index 8a97514fa46..f178b2baed6 100644 --- a/Firestore/core/test/firebase/firestore/local/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/local/CMakeLists.txt @@ -26,7 +26,10 @@ endif() cc_test( firebase_firestore_local_test SOURCES + #index_manager_test.mm + #leveldb_index_manager_test.mm local_serializer_test.cc + #memory_index_manager_test.mm DEPENDS firebase_firestore_local firebase_firestore_model diff --git a/Firestore/core/test/firebase/firestore/local/index_manager_test.h b/Firestore/core/test/firebase/firestore/local/index_manager_test.h new file mode 100644 index 00000000000..5c51424625b --- /dev/null +++ b/Firestore/core/test/firebase/firestore/local/index_manager_test.h @@ -0,0 +1,62 @@ +/* + * Copyright 2019 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_TEST_FIREBASE_FIRESTORE_LOCAL_INDEX_MANAGER_TEST_H_ +#define FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_LOCAL_INDEX_MANAGER_TEST_H_ + +#if !defined(__OBJC__) +#error "For now, this file must only be included by ObjC source files." +#endif // !defined(__OBJC__) + +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" +#include "gtest/gtest.h" + +#import "Firestore/Source/Local/FSTPersistence.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace local { + +using FactoryFunc = id _Nonnull (*)(); + +class IndexManagerTest : public ::testing::TestWithParam { + public: + // `GetParam()` must return a factory function. + IndexManagerTest() : persistence{GetParam()()} { + } + + id persistence; + + virtual ~IndexManagerTest(); + + protected: + void AssertParents(const std::string &collection_id, + std::vector expected); +}; + +} // namespace local +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_LOCAL_INDEX_MANAGER_TEST_H_ diff --git a/Firestore/core/test/firebase/firestore/local/index_manager_test.mm b/Firestore/core/test/firebase/firestore/local/index_manager_test.mm new file mode 100644 index 00000000000..a3f88631ab1 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/local/index_manager_test.mm @@ -0,0 +1,75 @@ +/* + * Copyright 2019 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 +#include +#include +#include + +#include "Firestore/core/test/firebase/firestore/local/index_manager_test.h" + +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace local { + +using model::ResourcePath; + +void IndexManagerTest::AssertParents(const std::string &collection_id, + std::vector expected) { + IndexManager *index_manager = persistence.indexManager; + std::vector actual_paths = + index_manager->GetCollectionParents(collection_id); + std::vector actual; + for (const ResourcePath &actual_path : actual_paths) { + actual.push_back(actual_path.CanonicalString()); + } + std::sort(expected.begin(), expected.end()); + std::sort(actual.begin(), actual.end()); + + SCOPED_TRACE("AssertParents(\"" + collection_id + "\", ...)"); + EXPECT_EQ(actual, expected); +} + +IndexManagerTest::~IndexManagerTest() { + [persistence shutdown]; +} + +TEST_P(IndexManagerTest, AddAndReadCollectionParentIndexEntries) { + IndexManager *index_manager = persistence.indexManager; + persistence.run("AddAndReadCollectionParentIndexEntries", [&]() { + index_manager->AddToCollectionParentIndex(ResourcePath{"messages"}); + index_manager->AddToCollectionParentIndex(ResourcePath{"messages"}); + index_manager->AddToCollectionParentIndex( + ResourcePath{"rooms", "foo", "messages"}); + index_manager->AddToCollectionParentIndex( + ResourcePath{"rooms", "bar", "messages"}); + index_manager->AddToCollectionParentIndex( + ResourcePath{"rooms", "foo", "messages2"}); + + AssertParents("messages", + std::vector{"", "rooms/bar", "rooms/foo"}); + AssertParents("messages2", std::vector{"rooms/foo"}); + AssertParents("messages3", std::vector{}); + }); +} + +} // namespace local +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/local/leveldb_index_manager_test.mm b/Firestore/core/test/firebase/firestore/local/leveldb_index_manager_test.mm new file mode 100644 index 00000000000..a68581b17b1 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/local/leveldb_index_manager_test.mm @@ -0,0 +1,49 @@ +/* + * Copyright 2019 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/test/firebase/firestore/local/index_manager_test.h" + +#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" +#import "Firestore/Source/Local/FSTPersistence.h" + +#include "Firestore/core/src/firebase/firestore/local/leveldb_index_manager.h" +#include "absl/memory/memory.h" +#include "gtest/gtest.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace local { + +namespace { + +id PersistenceFactory() { + return static_cast>( + [FSTPersistenceTestHelpers levelDBPersistence]); +} + +} // namespace + +INSTANTIATE_TEST_CASE_P(LevelDbIndexManagerTest, + IndexManagerTest, + ::testing::Values(PersistenceFactory)); + +NS_ASSUME_NONNULL_END + +} // namespace local +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/local/leveldb_key_test.cc b/Firestore/core/test/firebase/firestore/local/leveldb_key_test.cc index c2b1ea622e8..c6843f76943 100644 --- a/Firestore/core/test/firebase/firestore/local/leveldb_key_test.cc +++ b/Firestore/core/test/firebase/firestore/local/leveldb_key_test.cc @@ -201,11 +201,11 @@ TEST(LevelDbDocumentMutationKeyTest, Description) { auto key = LevelDbDocumentMutationKey::KeyPrefix( "user1", testutil::Resource("foo/bar")); AssertExpectedKeyDescription( - "[document_mutation: user_id=user1 key=foo/bar incomplete key]", key); + "[document_mutation: user_id=user1 path=foo/bar incomplete key]", key); key = LevelDbDocumentMutationKey::Key("user1", testutil::Key("foo/bar"), 42); AssertExpectedKeyDescription( - "[document_mutation: user_id=user1 key=foo/bar batch_id=42]", key); + "[document_mutation: user_id=user1 path=foo/bar batch_id=42]", key); } TEST(LevelDbTargetGlobalKeyTest, EncodeDecodeCycle) { @@ -279,7 +279,7 @@ TEST(TargetDocumentKeyTest, Ordering) { TEST(TargetDocumentKeyTest, Description) { auto key = LevelDbTargetDocumentKey::Key(42, testutil::Key("foo/bar")); - ASSERT_EQ("[target_document: target_id=42 key=foo/bar]", DescribeKey(key)); + ASSERT_EQ("[target_document: target_id=42 path=foo/bar]", DescribeKey(key)); } TEST(DocumentTargetKeyTest, EncodeDecodeCycle) { @@ -294,7 +294,7 @@ TEST(DocumentTargetKeyTest, EncodeDecodeCycle) { TEST(DocumentTargetKeyTest, Description) { auto key = LevelDbDocumentTargetKey::Key(testutil::Key("foo/bar"), 42); - ASSERT_EQ("[document_target: key=foo/bar target_id=42]", DescribeKey(key)); + ASSERT_EQ("[document_target: path=foo/bar target_id=42]", DescribeKey(key)); } TEST(DocumentTargetKeyTest, Ordering) { @@ -352,7 +352,7 @@ TEST(RemoteDocumentKeyTest, EncodeDecodeCycle) { TEST(RemoteDocumentKeyTest, Description) { AssertExpectedKeyDescription( - "[remote_document: key=foo/bar/baz/quux]", + "[remote_document: path=foo/bar/baz/quux]", LevelDbRemoteDocumentKey::Key(testutil::Key("foo/bar/baz/quux"))); } diff --git a/Firestore/core/test/firebase/firestore/local/memory_index_manager_test.mm b/Firestore/core/test/firebase/firestore/local/memory_index_manager_test.mm new file mode 100644 index 00000000000..28039b37243 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/local/memory_index_manager_test.mm @@ -0,0 +1,49 @@ +/* + * Copyright 2019 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/test/firebase/firestore/local/index_manager_test.h" + +#include "Firestore/core/src/firebase/firestore/local/memory_index_manager.h" +#include "absl/memory/memory.h" +#include "gtest/gtest.h" + +#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" +#import "Firestore/Source/Local/FSTPersistence.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace local { + +namespace { + +id PersistenceFactory() { + return static_cast>( + [FSTPersistenceTestHelpers lruMemoryPersistence]); +} + +} // namespace + +INSTANTIATE_TEST_CASE_P(MemoryIndexManagerTest, + IndexManagerTest, + ::testing::Values(PersistenceFactory)); + +NS_ASSUME_NONNULL_END + +} // namespace local +} // namespace firestore +} // namespace firebase From 3c10927f28afd480eea584bb813ead9e012f9ace Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 8 Mar 2019 19:13:42 -0500 Subject: [PATCH 002/214] Update CHANGELOG for 5.18.0 (#2474) --- Firebase/Messaging/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firebase/Messaging/CHANGELOG.md b/Firebase/Messaging/CHANGELOG.md index 8cccf94546b..4eea22d7eea 100644 --- a/Firebase/Messaging/CHANGELOG.md +++ b/Firebase/Messaging/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2019-03-05 -- v3.3.2 +- Replaced `NSUserDefaults` with `GULUserDefaults` to avoid potential crashes. (#2443) + # 2019-02-20 -- v3.3.1 - Internal code cleanup. From 7abbcabf12a2d5159d7ecc07280746360fcf931b Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Sat, 9 Mar 2019 06:42:36 -0800 Subject: [PATCH 003/214] Add missing nil check (#2488) --- Firebase/Database/CHANGELOG.md | 3 +++ Firebase/Database/third_party/SocketRocket/FSRWebSocket.m | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Firebase/Database/CHANGELOG.md b/Firebase/Database/CHANGELOG.md index b7eb71cfc74..cd2526ac4d1 100644 --- a/Firebase/Database/CHANGELOG.md +++ b/Firebase/Database/CHANGELOG.md @@ -1,3 +1,6 @@ +# v5.1.1 +- [fixed] Fixed crash in FSRWebSocket. (#2485) + # v5.0.2 - [fixed] Fixed undefined behavior sanitizer issues. (#1443, #1444) diff --git a/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m b/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m index 4cd481b4e61..d0c1e20dd5d 100644 --- a/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m +++ b/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m @@ -1354,7 +1354,11 @@ - (void)_sendFrameWithOpcode:(FSROpCode)opcode data:(id)data; { [self assertOnWorkQueue]; - NSAssert(data == nil || [data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"Function expects nil, NSString or NSData"); + if (data == nil) { + return; + } + + NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"Function expects nil, NSString or NSData"); size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length]; From d100e02496c045b4af1740066f775df5081468e4 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 11 Mar 2019 08:59:12 -0700 Subject: [PATCH 004/214] Restore GULLogger APIs, unit tests, and contents (#2504) --- Firebase/Core/FIRLogger.m | 34 +++++++++--------- .../Example/Tests/Logger/GULOSLoggerTest.m | 2 +- GoogleUtilities/Logger/GULASLLogger.m | 15 ++------ GoogleUtilities/Logger/GULLogger.m | 36 +++++++++++++------ GoogleUtilities/Logger/GULOSLogger.m | 14 ++------ GoogleUtilities/Logger/Private/GULLogger.h | 12 +++++-- .../Logger/Private/GULLoggerSystem.h | 2 +- 7 files changed, 58 insertions(+), 57 deletions(-) diff --git a/Firebase/Core/FIRLogger.m b/Firebase/Core/FIRLogger.m index b8c40e9747d..67aee32b8ee 100644 --- a/Firebase/Core/FIRLogger.m +++ b/Firebase/Core/FIRLogger.m @@ -20,23 +20,23 @@ #import "Private/FIRVersion.h" -FIRLoggerService kFIRLoggerABTesting = @"[Firebase/ABTesting]"; -FIRLoggerService kFIRLoggerAdMob = @"[Firebase/AdMob]"; -FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]"; -FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]"; -FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]"; -FIRLoggerService kFIRLoggerCrash = @"[Firebase/Crash]"; -FIRLoggerService kFIRLoggerDatabase = @"[Firebase/Database]"; -FIRLoggerService kFIRLoggerDynamicLinks = @"[Firebase/DynamicLinks]"; -FIRLoggerService kFIRLoggerFirestore = @"[Firebase/Firestore]"; -FIRLoggerService kFIRLoggerInstanceID = @"[Firebase/InstanceID]"; -FIRLoggerService kFIRLoggerInvites = @"[Firebase/Invites]"; -FIRLoggerService kFIRLoggerMLKit = @"[Firebase/MLKit]"; -FIRLoggerService kFIRLoggerMessaging = @"[Firebase/Messaging]"; -FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]"; -FIRLoggerService kFIRLoggerRemoteConfig = @"[Firebase/RemoteConfig]"; -FIRLoggerService kFIRLoggerStorage = @"[Firebase/Storage]"; -FIRLoggerService kFIRLoggerSwizzler = @"[FirebaseSwizzlingUtilities]"; +FIRLoggerService kFIRLoggerABTesting = @"Firebase/ABTesting"; +FIRLoggerService kFIRLoggerAdMob = @"Firebase/AdMob"; +FIRLoggerService kFIRLoggerAnalytics = @"Firebase/Analytics"; +FIRLoggerService kFIRLoggerAuth = @"Firebase/Auth"; +FIRLoggerService kFIRLoggerCore = @"Firebase/Core"; +FIRLoggerService kFIRLoggerCrash = @"Firebase/Crash"; +FIRLoggerService kFIRLoggerDatabase = @"Firebase/Database"; +FIRLoggerService kFIRLoggerDynamicLinks = @"Firebase/DynamicLinks"; +FIRLoggerService kFIRLoggerFirestore = @"Firebase/Firestore"; +FIRLoggerService kFIRLoggerInstanceID = @"Firebase/InstanceID"; +FIRLoggerService kFIRLoggerInvites = @"Firebase/Invites"; +FIRLoggerService kFIRLoggerMLKit = @"Firebase/MLKit"; +FIRLoggerService kFIRLoggerMessaging = @"Firebase/Messaging"; +FIRLoggerService kFIRLoggerPerf = @"Firebase/Performance"; +FIRLoggerService kFIRLoggerRemoteConfig = @"Firebase/RemoteConfig"; +FIRLoggerService kFIRLoggerStorage = @"Firebase/Storage"; +FIRLoggerService kFIRLoggerSwizzler = @"FirebaseSwizzlingUtilities"; /// Arguments passed on launch. NSString *const kFIRDisableDebugModeApplicationArgument = @"-FIRDebugDisabled"; diff --git a/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m index f9ad7bc2879..1fd6a5da231 100644 --- a/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m +++ b/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m @@ -261,7 +261,7 @@ - (void)testLoggingValidWithVarArgs { withService:kService isForced:NO withCode:kCode - withMessage:@"%@", message]; + withMessage:message]; [self waitForExpectations:sExpectations timeout:kTimeout]; } diff --git a/GoogleUtilities/Logger/GULASLLogger.m b/GoogleUtilities/Logger/GULASLLogger.m index 51d6c03bff8..fe8e480e335 100644 --- a/GoogleUtilities/Logger/GULASLLogger.m +++ b/GoogleUtilities/Logger/GULASLLogger.m @@ -122,27 +122,16 @@ - (void)logWithLevel:(GULLoggerLevel)level withService:(GULLoggerService)service isForced:(BOOL)forced withCode:(NSString *)messageCode - withMessage:(NSString *)message, ... { + withMessage:(NSString *)message { // Skip logging this if the level isn't to be logged unless it's forced. if (![self isLoggableLevel:level] && !forced) { return; } [self initializeLogger]; - - // Process the va_list here, while the parameters are on the stack. - va_list args; - va_start(args, message); - message = [[NSString alloc] initWithFormat:message arguments:args]; - va_end(args); - const char *logMsg = [GULLogger messageFromLogger:self - withService:service - code:messageCode - message:message] - .UTF8String; dispatch_async(self.dispatchQueue, ^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // asl is deprecated - asl_log(self.aslClient, NULL, (int)level, "%s", logMsg); + asl_log(self.aslClient, NULL, (int)level, "%s", message.UTF8String); #pragma clang diagnostic pop }); } diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index b5d5e6804a7..412d4063d82 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -25,8 +25,9 @@ #endif #ifdef DEBUG +/// The regex pattern for the message code. +static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$"; static NSRegularExpression *sMessageCodeRegex; -static NSString *const kGULLoggerMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$"; #endif @implementation GULLogger (Internal) @@ -48,10 +49,9 @@ + (NSString *)messageFromLogger:(id)logger NSCAssert(code.length == 11, @"Incorrect message code length."); static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sMessageCodeRegex = - [NSRegularExpression regularExpressionWithPattern:kGULLoggerMessageCodePattern - options:0 - error:NULL]; + sMessageCodeRegex = [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern + options:0 + error:NULL]; }); NSRange messageCodeRange = NSMakeRange(0, code.length); NSUInteger numberOfMatches = [sMessageCodeRegex numberOfMatchesInString:code @@ -68,6 +68,11 @@ + (NSString *)messageFromLogger:(id)logger void GULLoggerInitialize(void) { [GULLogger.logger initializeLogger]; +#ifdef DEBUG + sMessageCodeRegex = [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern + options:0 + error:NULL]; +#endif } void GULLoggerInitializeASL(void) { @@ -99,15 +104,26 @@ void GULLogBasic(GULLoggerLevel level, BOOL forceLog, NSString *messageCode, NSString *message, - ...) { - va_list formatArgs; - va_start(formatArgs, message); + va_list args_ptr) { +#ifdef DEBUG + sMessageCodeRegex = [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern + options:0 + error:NULL]; + NSCAssert(messageCode.length == 11, @"Incorrect message code length."); + NSRange messageCodeRange = NSMakeRange(0, messageCode.length); + NSUInteger numberOfMatches = [sMessageCodeRegex numberOfMatchesInString:messageCode + options:0 + range:messageCodeRange]; + NSCAssert(numberOfMatches == 1, @"Incorrect message code format."); +#endif + NSString *logMsg = [[NSString alloc] initWithFormat:message arguments:args_ptr]; + NSString *formattedMsg = + [NSString stringWithFormat:@"%@ - [%@] %@", GULLogger.logger.version, messageCode, logMsg]; [GULLogger.logger logWithLevel:level withService:service isForced:forceLog withCode:messageCode - withMessage:messageCode, formatArgs]; - va_end(formatArgs); + withMessage:formattedMsg]; } /** diff --git a/GoogleUtilities/Logger/GULOSLogger.m b/GoogleUtilities/Logger/GULOSLogger.m index 388db6af9cd..cf6de63c8d5 100644 --- a/GoogleUtilities/Logger/GULOSLogger.m +++ b/GoogleUtilities/Logger/GULOSLogger.m @@ -153,23 +153,13 @@ - (void)logWithLevel:(GULLoggerLevel)level withService:(GULLoggerService)service isForced:(BOOL)forced withCode:(NSString *)messageCode - withMessage:(NSString *)message, ... { + withMessage:(NSString *)message { // Skip logging this if the level isn't to be logged unless it's forced. if (![self isLoggableLevel:level] && !forced) { return; } [self initializeLogger]; - // Process the va_list here, while the parameters are on the stack. - va_list args; - va_start(args, message); - NSString *completeMessage = [[NSString alloc] initWithFormat:message arguments:args]; - va_end(args); - completeMessage = [GULLogger messageFromLogger:self - withService:service - code:messageCode - message:[completeMessage copy]]; - // Avoid blocking during logging. dispatch_async(self.dispatchQueue, ^{ os_log_t osLog = self.categoryLoggers[service]; @@ -197,7 +187,7 @@ - (void)logWithLevel:(GULLoggerLevel)level } // Call the function pointer using the message constructed by GULLogger. (*self.logFunction)(osLog, [[self class] osLogTypeForGULLoggerLevel:level], "%s", - completeMessage.UTF8String); + message.UTF8String); }); } diff --git a/GoogleUtilities/Logger/Private/GULLogger.h b/GoogleUtilities/Logger/Private/GULLogger.h index ae8e6fb46be..c7bc7e30c87 100644 --- a/GoogleUtilities/Logger/Private/GULLogger.h +++ b/GoogleUtilities/Logger/Private/GULLogger.h @@ -80,15 +80,21 @@ extern void GULLoggerRegisterVersion(const char *version); * within the service. * An example of the message code is @"I-COR000001". * @param message string which can be a format string. - * @param ... variable arguments list obtained from calling va_start, used when message is a format - * string. + * @param args_ptr the list of arguments to substitute into the format string. */ extern void GULLogBasic(GULLoggerLevel level, GULLoggerService service, BOOL forceLog, NSString *messageCode, NSString *message, - ...); +// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable +// See: http://stackoverflow.com/q/29095469 +#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX + va_list args_ptr +#else + va_list _Nullable args_ptr +#endif +); /** * The following functions accept the following parameters in order: diff --git a/GoogleUtilities/Logger/Private/GULLoggerSystem.h b/GoogleUtilities/Logger/Private/GULLoggerSystem.h index f19bacadc1a..ceff5b125fa 100644 --- a/GoogleUtilities/Logger/Private/GULLoggerSystem.h +++ b/GoogleUtilities/Logger/Private/GULLoggerSystem.h @@ -45,7 +45,7 @@ typedef NSString *const GULLoggerService; withService:(GULLoggerService)service isForced:(BOOL)forced withCode:(NSString *)messageCode - withMessage:(NSString *)message, ... NS_FORMAT_FUNCTION(5, 6); + withMessage:(NSString *)message; @end NS_ASSUME_NONNULL_END From 8a86551019ec855824a8f258c347c37ec13b1e4c Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 11 Mar 2019 10:12:58 -0700 Subject: [PATCH 005/214] Delete test with new (#2514) --- .../Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m | 5 ----- 1 file changed, 5 deletions(-) diff --git a/GoogleUtilities/Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m b/GoogleUtilities/Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m index e340a576c86..cbe4faa9939 100644 --- a/GoogleUtilities/Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m +++ b/GoogleUtilities/Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m @@ -47,11 +47,6 @@ @interface GULRuntimeClassSnapshotTests : XCTestCase @implementation GULRuntimeClassSnapshotTests -/** Tests the assurance that init throws. */ -- (void)testInitThrows { - XCTAssertThrows([GULRuntimeClassSnapshot new]); -} - /** Tests initialization. */ - (void)testInitWithClass { Class NSObjectClass = [NSObject class]; From 6ef85e43448ab1b9cc4da596fabbaf08208da096 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Mon, 11 Mar 2019 11:01:46 -0700 Subject: [PATCH 006/214] Change sample code to use "microsoft.com" instead of string constant, which will be remove soon. (#2501) --- Example/Auth/Sample/MainViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 278425def00..9069d02de9c 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -1850,7 +1850,7 @@ - (void)signInGoogleHeadfulLite { @brief Invoked when "Sign in with Microsoft (headful-lite)" row is pressed. */ - (void)signInMicrosoftHeadfulLite { - FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRMicrosoftAuthProviderID]; + FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:@"microsoft.com"]; provider.customParameters = @{ @"prompt" : @"consent", @"login_hint" : @"tu8731@gmail.com", From 0956f5d5919f98ae19a75b48714d98427705821c Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Mon, 11 Mar 2019 14:04:26 -0400 Subject: [PATCH 007/214] Send Firebase user agent from FIRInstanceIDCheckinService directly (#2509) (b/124533860) --- .../Tests/FIRInstanceIDCheckinServiceTest.m | 38 +++++++++++++++++-- .../InstanceID/FIRInstanceIDCheckinService.h | 1 + .../InstanceID/FIRInstanceIDCheckinService.m | 6 +++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m b/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m index b595fca0e91..b25fac54c49 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m @@ -16,6 +16,7 @@ #import +#import #import #import "Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h" #import "Firebase/InstanceID/FIRInstanceIDCheckinPreferences.h" @@ -38,15 +39,15 @@ @implementation FIRInstanceIDCheckinServiceTest - (void)setUp { [super setUp]; + self.checkinService = [[FIRInstanceIDCheckinService alloc] init]; } - (void)tearDown { + self.checkinService = nil; [super tearDown]; } - (void)testCheckinWithSuccessfulCompletion { - self.checkinService = [[FIRInstanceIDCheckinService alloc] init]; - FIRInstanceIDCheckinPreferences *existingCheckin = [self stubCheckinCacheWithValidData]; [FIRInstanceIDCheckinService setCheckinTestBlock:[self successfulCheckinCompletionHandler]]; @@ -79,8 +80,6 @@ - (void)testCheckinWithSuccessfulCompletion { } - (void)testFailedCheckinService { - self.checkinService = [[FIRInstanceIDCheckinService alloc] init]; - [FIRInstanceIDCheckinService setCheckinTestBlock:[self failCheckinCompletionHandler]]; XCTestExpectation *checkinCompletionExpectation = @@ -104,6 +103,37 @@ - (void)testFailedCheckinService { }]; } +- (void)testCheckinServiceAddsFirebaseUserAgentToHTTPHeader { + NSString *expectedFirebaseUserAgent = [FIRApp firebaseUserAgent]; + + FIRInstanceIDURLRequestTestBlock successHandler = [self successfulCheckinCompletionHandler]; + + [FIRInstanceIDCheckinService + setCheckinTestBlock:^(NSURLRequest *request, + FIRInstanceIDURLRequestTestResponseBlock response) { + NSString *requestFirebaseUserAgentValue = + request.allHTTPHeaderFields[kFIRInstanceIDFirebaseUserAgentKey]; + XCTAssertEqualObjects(requestFirebaseUserAgentValue, expectedFirebaseUserAgent); + successHandler(request, response); + }]; + + XCTestExpectation *checkinCompletionExpectation = + [self expectationWithDescription:@"Checkin Completion"]; + + [self.checkinService + checkinWithExistingCheckin:nil + completion:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) { + [checkinCompletionExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Checkin Timeout Error: %@", error); + } + }]; +} + #pragma mark - Stub - (FIRInstanceIDCheckinPreferences *)stubCheckinCacheWithValidData { diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinService.h b/Firebase/InstanceID/FIRInstanceIDCheckinService.h index cc97e4700a3..9d05eb4514a 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinService.h +++ b/Firebase/InstanceID/FIRInstanceIDCheckinService.h @@ -28,6 +28,7 @@ FOUNDATION_EXPORT NSString *const kFIRInstanceIDLastCheckinTimeKey; FOUNDATION_EXPORT NSString *const kFIRInstanceIDVersionInfoStringKey; FOUNDATION_EXPORT NSString *const kFIRInstanceIDGServicesDictionaryKey; FOUNDATION_EXPORT NSString *const kFIRInstanceIDDeviceDataVersionKey; +FOUNDATION_EXPORT NSString *const kFIRInstanceIDFirebaseUserAgentKey; @class FIRInstanceIDCheckinPreferences; diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinService.m b/Firebase/InstanceID/FIRInstanceIDCheckinService.m index a006831c7f7..9b79c770c62 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinService.m +++ b/Firebase/InstanceID/FIRInstanceIDCheckinService.m @@ -16,6 +16,7 @@ #import "FIRInstanceIDCheckinService.h" +#import #import "FIRInstanceIDCheckinPreferences+Internal.h" #import "FIRInstanceIDCheckinPreferences_Private.h" #import "FIRInstanceIDDefines.h" @@ -34,6 +35,7 @@ NSString *const kFIRInstanceIDVersionInfoStringKey = @"GMSInstanceIDVersionInfo"; NSString *const kFIRInstanceIDGServicesDictionaryKey = @"GMSInstanceIDGServicesData"; NSString *const kFIRInstanceIDDeviceDataVersionKey = @"GMSInstanceIDDeviceDataVersion"; +NSString *const kFIRInstanceIDFirebaseUserAgentKey = @"X-firebase-client"; static NSUInteger const kCheckinType = 2; // DeviceType IOS in l/w/a/_checkin.proto static NSUInteger const kCheckinVersion = 2; @@ -74,7 +76,11 @@ - (void)checkinWithExistingCheckin:(FIRInstanceIDCheckinPreferences *)existingCh NSURL *url = [NSURL URLWithString:kDeviceCheckinURL]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request setValue:@"application/json" forHTTPHeaderField:@"content-type"]; + [request setValue:[FIRApp firebaseUserAgent] + forHTTPHeaderField:kFIRInstanceIDFirebaseUserAgentKey]; + NSDictionary *checkinParameters = [self checkinParametersWithExistingCheckin:existingCheckin]; NSData *checkinData = [NSJSONSerialization dataWithJSONObject:checkinParameters options:0 From b2bcf8f7914d9d3a0c9307d33e4d8eaca3d82851 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Mon, 11 Mar 2019 14:40:48 -0400 Subject: [PATCH 008/214] Use free() for memory allocated with malloc. (#2513) Rather than delete, which technically results in undefined behaviour. --- Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc | 4 ++++ Firestore/core/src/firebase/firestore/nanopb/nanopb_string.h | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc b/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc index 89a9be66db4..98151c1561b 100644 --- a/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc +++ b/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc @@ -23,6 +23,10 @@ namespace firebase { namespace firestore { namespace nanopb { +String::~String() { + std::free(bytes_); +} + /* static */ pb_bytes_array_t* String::MakeBytesArray(absl::string_view value) { auto size = static_cast(value.size()); diff --git a/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.h b/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.h index 444d53d9541..53a96682306 100644 --- a/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.h +++ b/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.h @@ -72,9 +72,7 @@ class String : public util::Comparable { swap(*this, other); } - ~String() { - delete bytes_; - } + ~String(); String& operator=(String other) { swap(*this, other); From 847d9ddf3918a69662cd364c76809f09b5cfaff2 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Mon, 11 Mar 2019 15:49:17 -0400 Subject: [PATCH 009/214] FIRDynamicLink: preserve all parameters passed to initializer (#2478) FIRDynamicLink: preserve all parameters passed to initializer (#2478) --- .../DynamicLinks/Tests/FIRDynamicLinksTest.m | 44 +++++++++++++ .../DynamicLinks/FIRDynamicLink+Private.h | 4 +- Firebase/DynamicLinks/FIRDynamicLink.m | 62 +++++++++++++++---- 3 files changed, 96 insertions(+), 14 deletions(-) diff --git a/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m b/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m index 74fc2373d78..92a4cef2590 100644 --- a/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m +++ b/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m @@ -21,6 +21,7 @@ #import #import #import "DynamicLinks/FIRDLRetrievalProcessFactory.h" +#import "DynamicLinks/FIRDLRetrievalProcessResult+Private.h" #import "DynamicLinks/FIRDynamicLink+Private.h" #import "DynamicLinks/FIRDynamicLinkNetworking+Private.h" #import "DynamicLinks/FIRDynamicLinks+FirstParty.h" @@ -967,6 +968,49 @@ - (void)testCheckForPendingDynamicLinkReturnsImmediatelyIfAlreadyRead { [mockService stopMocking]; } +- (void)testRetrievalProcessResultURLContainsAllParametersPassedToDynamicLinkInitializer { + NSDictionary *linkParameters = @{ + @"deep_link_id" : @"https://mmaksym.com/test-app1", + @"match_message" : @"Link is uniquely matched for this device.", + @"match_type" : @"unique", + @"utm_campaign" : @"Maksym M Test", + @"utm_medium" : @"test_medium", + @"utm_source" : @"test_source", + @"a_parameter" : @"a_value" + }; + + FIRDynamicLink *dynamicLink = + [[FIRDynamicLink alloc] initWithParametersDictionary:linkParameters]; + FIRDLRetrievalProcessResult *result = + [[FIRDLRetrievalProcessResult alloc] initWithDynamicLink:dynamicLink + error:nil + message:nil + matchSource:nil]; + + NSURL *customSchemeURL = [result URLWithCustomURLScheme:@"scheme"]; + XCTAssertNotNil(customSchemeURL); + + // Validate URL parameters + NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:customSchemeURL + resolvingAgainstBaseURL:NO]; + XCTAssertNotNil(urlComponents); + XCTAssertEqualObjects(urlComponents.scheme, @"scheme"); + + NSMutableDictionary *notEncodedParameters = [linkParameters mutableCopy]; + + for (NSURLQueryItem *queryItem in urlComponents.queryItems) { + NSString *expectedValue = notEncodedParameters[queryItem.name]; + XCTAssertNotNil(expectedValue, @"Extra parameter encoded: %@ = %@", queryItem.name, + queryItem.value); + + XCTAssertEqualObjects(queryItem.value, expectedValue); + [notEncodedParameters removeObjectForKey:queryItem.name]; + } + + XCTAssertEqual(notEncodedParameters.count, 0, @"The parameters must have been encoded: %@", + notEncodedParameters); +} + - (void)test_multipleRequestsToRetrievePendingDeepLinkShouldNotCrash { id mockService = OCMPartialMock(self.service); [[mockService expect] handlePendingDynamicLinkRetrievalFailureWithErrorCode:-1 diff --git a/Firebase/DynamicLinks/FIRDynamicLink+Private.h b/Firebase/DynamicLinks/FIRDynamicLink+Private.h index 72bb666bb1a..43b310e8975 100644 --- a/Firebase/DynamicLinks/FIRDynamicLink+Private.h +++ b/Firebase/DynamicLinks/FIRDynamicLink+Private.h @@ -41,11 +41,11 @@ typedef NS_ENUM(NSUInteger, FIRDynamicLinkMatchConfidence) { @property(nonatomic, copy, nullable) NSString *matchMessage; -@property(nonatomic, copy, readonly) NSDictionary *parametersDictionary; +@property(nonatomic, copy, readonly) NSDictionary *parametersDictionary; @property(nonatomic, assign, readwrite) FIRDLMatchType matchType; -- (instancetype)initWithParametersDictionary:(NSDictionary *)parametersDictionary; +- (instancetype)initWithParametersDictionary:(NSDictionary *)parametersDictionary; @end diff --git a/Firebase/DynamicLinks/FIRDynamicLink.m b/Firebase/DynamicLinks/FIRDynamicLink.m index cbe2e23d973..bbe7c4ef5a7 100644 --- a/Firebase/DynamicLinks/FIRDynamicLink.m +++ b/Firebase/DynamicLinks/FIRDynamicLink.m @@ -28,10 +28,12 @@ - (NSString *)description { self.minimumAppVersion ?: @"N/A", self.matchMessage]; } -- (instancetype)initWithParametersDictionary:(NSDictionary *)parameters { +- (instancetype)initWithParametersDictionary:(NSDictionary *)parameters { NSParameterAssert(parameters.count > 0); if (self = [super init]) { + _parametersDictionary = [parameters copy]; + NSString *urlString = parameters[kFIRDLParameterDeepLinkIdentifier]; _url = [NSURL URLWithString:urlString]; _inviteId = parameters[kFIRDLParameterInviteId]; @@ -39,26 +41,62 @@ - (instancetype)initWithParametersDictionary:(NSDictionary *)parameters { _minimumAppVersion = parameters[kFIRDLParameterMinimumAppVersion]; if (parameters[kFIRDLParameterMatchType]) { - _matchType = [[self class] matchTypeWithString:parameters[kFIRDLParameterMatchType]]; + [self setMatchType:[[self class] matchTypeWithString:parameters[kFIRDLParameterMatchType]]]; } else if (_url || _inviteId) { // If matchType not present assume unique match for compatibility with server side behavior // on iOS 8. - _matchType = FIRDLMatchTypeUnique; + [self setMatchType:FIRDLMatchTypeUnique]; } + _matchMessage = parameters[kFIRDLParameterMatchMessage]; } return self; } -- (NSDictionary *)parametersDictionary { - NSMutableDictionary *parametersDictionary = [NSMutableDictionary dictionary]; - parametersDictionary[kFIRDLParameterInviteId] = _inviteId; - parametersDictionary[kFIRDLParameterDeepLinkIdentifier] = [_url absoluteString]; - parametersDictionary[kFIRDLParameterMatchType] = [[self class] stringWithMatchType:_matchType]; - parametersDictionary[kFIRDLParameterWeakMatchEndpoint] = _weakMatchEndpoint; - parametersDictionary[kFIRDLParameterMinimumAppVersion] = _minimumAppVersion; - parametersDictionary[kFIRDLParameterMatchMessage] = _matchMessage; - return parametersDictionary; +#pragma mark - Properties + +- (void)setUrl:(NSURL *)url { + _url = [url copy]; + [self setParametersDictionaryValue:[_url absoluteString] + forKey:kFIRDLParameterDeepLinkIdentifier]; +} + +- (void)setMinimumAppVersion:(NSString *)minimumAppVersion { + _minimumAppVersion = [minimumAppVersion copy]; + [self setParametersDictionaryValue:_minimumAppVersion forKey:kFIRDLParameterMinimumAppVersion]; +} + +- (void)setInviteId:(NSString *)inviteId { + _inviteId = [inviteId copy]; + [self setParametersDictionaryValue:_inviteId forKey:kFIRDLParameterInviteId]; +} + +- (void)setWeakMatchEndpoint:(NSString *)weakMatchEndpoint { + _weakMatchEndpoint = [weakMatchEndpoint copy]; + [self setParametersDictionaryValue:_weakMatchEndpoint forKey:kFIRDLParameterWeakMatchEndpoint]; +} + +- (void)setMatchType:(FIRDLMatchType)matchType { + _matchType = matchType; + [self setParametersDictionaryValue:[[self class] stringWithMatchType:_matchType] + forKey:kFIRDLParameterMatchType]; +} + +- (void)setMatchMessage:(NSString *)matchMessage { + _matchMessage = [matchMessage copy]; + [self setParametersDictionaryValue:_matchMessage forKey:kFIRDLParameterMatchMessage]; +} + +- (void)setParametersDictionaryValue:(id)value forKey:(NSString *)key { + NSMutableDictionary *parametersDictionary = + [self.parametersDictionary mutableCopy]; + if (value == nil) { + [parametersDictionary removeObjectForKey:key]; + } else { + parametersDictionary[key] = value; + } + + _parametersDictionary = [parametersDictionary copy]; } - (FIRDynamicLinkMatchConfidence)matchConfidence { From c2f6b78c146db41601b2c9db662a5884bb6ddfda Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 11 Mar 2019 13:07:20 -0700 Subject: [PATCH 010/214] Add community supported tvOS and macOS for Functions (#2506) --- FirebaseFunctions.podspec | 2 ++ Functions/CHANGELOG.md | 3 +++ README.md | 11 ++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/FirebaseFunctions.podspec b/FirebaseFunctions.podspec index b365a184714..e2d427de7c3 100644 --- a/FirebaseFunctions.podspec +++ b/FirebaseFunctions.podspec @@ -16,6 +16,8 @@ iOS SDK for Cloud Functions for Firebase. } s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.11' + s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' s.static_framework = true diff --git a/Functions/CHANGELOG.md b/Functions/CHANGELOG.md index 7fa5793ae21..3321f498727 100644 --- a/Functions/CHANGELOG.md +++ b/Functions/CHANGELOG.md @@ -1,3 +1,6 @@ +# v2.4.0 +- Introduce community support for tvOS and macOS (#2506). + # v2.3.0 - Change the default timeout for callable functions to 70s (#2329). - Add a method to change the timeout for a callable (#2329). diff --git a/README.md b/README.md index 688f343cda9..903fc9fc76b 100644 --- a/README.md +++ b/README.md @@ -169,10 +169,10 @@ very grateful! We'd like to empower as many developers as we can to be able to participate in the Firebase community. ### macOS and tvOS -FirebaseAuth, FirebaseCore, FirebaseDatabase and FirebaseStorage now compile, run unit tests, and -work on macOS and tvOS, thanks to contributions from the community. There are a few tweaks needed, -like ensuring iOS-only, macOS-only, or tvOS-only code is correctly guarded with checks for -`TARGET_OS_IOS`, `TARGET_OS_OSX` and `TARGET_OS_TV`. +FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseFunctions and FirebaseStorage now compile, run +unit tests, and work on macOS and tvOS, thanks to contributions from the community. There are a few +tweaks needed, like ensuring iOS-only, macOS-only, or tvOS-only code is correctly guarded with checks +for `TARGET_OS_IOS`, `TARGET_OS_OSX` and `TARGET_OS_TV`. For tvOS, checkout the [Sample](Example/tvOSSample). @@ -184,7 +184,8 @@ this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues For installation instructions, see [above](README.md#accessing-firebase-source-snapshots). Note that the Firebase pod is not available for macOS and tvOS. Install a selection of the -`FirebaseAuth`, `FirebaseCore`, `FirebaseDatabase` and `FirebaseStorage` CocoaPods. +`FirebaseAuth`, `FirebaseCore`, `FirebaseDatabase`, `FirebaseFunctions`, and `FirebaseStorage` +CocoaPods. ## Roadmap From 681a5740b07cfb82a63984fdc602da9dd5529982 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Mon, 11 Mar 2019 14:36:40 -0700 Subject: [PATCH 011/214] Add image on notification support (#2491) This is official image notification support for FCM. Now an image can be rendered in notification when user enable service extension and calling the extensionHelper API. --- Example/Firebase.xcodeproj/project.pbxproj | 10 ++ .../Tests/FIRMessagingExtensionHelperTest.m | 110 +++++++++++++++++ Firebase/Messaging/FIRMMessageCode.h | 6 + Firebase/Messaging/FIRMessaging.m | 10 ++ .../Messaging/FIRMessagingExtensionHelper.m | 116 ++++++++++++++++++ Firebase/Messaging/Public/FIRMessaging.h | 13 ++ .../Public/FIRMessagingExtensionHelper.h | 34 +++++ Firebase/Messaging/Public/FirebaseMessaging.h | 1 + 8 files changed, 300 insertions(+) create mode 100644 Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m create mode 100644 Firebase/Messaging/FIRMessagingExtensionHelper.m create mode 100644 Firebase/Messaging/Public/FIRMessagingExtensionHelper.h diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index be4844b799f..ce9f674d0db 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -176,6 +176,8 @@ 51885509223067E900CA4141 /* FIRInstanceIDTokenOperationsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE958BDB21F7DF0B00E6C1C5 /* FIRInstanceIDTokenOperationsTest.m */; }; 5188550A223067E900CA4141 /* FIRInstanceIDUtilitiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE958BE121F7DF0C00E6C1C5 /* FIRInstanceIDUtilitiesTest.m */; }; 5188550C2230873000CA4141 /* FIRInstanceIDAPNSInfoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE958BDA21F7DF0B00E6C1C5 /* FIRInstanceIDAPNSInfoTest.m */; }; + 5188550E2231E02400CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */; }; + 518855112231E09300CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 518855102231E09300CA4141 /* FIRMessagingExtensionHelperTest.m */; }; 7E21E0731F857DFC00D0AC1C /* FIROAuthProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E21E0721F857DFC00D0AC1C /* FIROAuthProviderTests.m */; }; 7E9485421F578AC4005A3939 /* FIRAuthURLPresenterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */; }; 7EE21F7A1FE89193009B1370 /* FIREmailLinkRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */; }; @@ -1078,6 +1080,9 @@ 518854E22230652B00CA4141 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 518854E32230652B00CA4141 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 518854EC223066BE00CA4141 /* InstanceID_Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InstanceID_Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingExtensionHelperTest.m; path = Messaging/Tests/FIRMessagingExtensionHelperTest.m; sourceTree = ""; }; + 5188550F2231E04C00CA4141 /* FIRMessagingClientTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingClientTest.m; path = Messaging/Tests/FIRMessagingClientTest.m; sourceTree = ""; }; + 518855102231E09300CA4141 /* FIRMessagingExtensionHelperTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingExtensionHelperTest.m; path = Messaging/Tests/FIRMessagingExtensionHelperTest.m; sourceTree = ""; }; 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -1914,6 +1919,9 @@ 6003F581195388D10070C39A = { isa = PBXGroup; children = ( + 518855102231E09300CA4141 /* FIRMessagingExtensionHelperTest.m */, + 5188550F2231E04C00CA4141 /* FIRMessagingClientTest.m */, + 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */, 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, DE9314EB1E86C6FF0083EDBF /* Auth */, DEE14D661E844677006FA992 /* Core */, @@ -4297,6 +4305,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 518855112231E09300CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */, 511DD2AA2225C8D50094D78D /* FIRMessagingTestUtilities.h in Sources */, 511DD2AB2225C8D50094D78D /* FIRMessagingTestUtilities.m in Sources */, 511DD2922225C8C40094D78D /* FIRInstanceIDWithFCMTest.m in Sources */, @@ -4921,6 +4930,7 @@ buildActionMask = 2147483647; files = ( DE9315F41E8738E60083EDBF /* FIRMessagingClientTest.m in Sources */, + 5188550E2231E02400CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */, DE9315F51E8738E60083EDBF /* FIRMessagingCodedInputStreamTest.m in Sources */, DE9315F71E8738E60083EDBF /* FIRMessagingContextManagerServiceTest.m in Sources */, DE9315FD1E8738E60083EDBF /* FIRMessagingPubSubTest.m in Sources */, diff --git a/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m new file mode 100644 index 00000000000..a58efcd0810 --- /dev/null +++ b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m @@ -0,0 +1,110 @@ +/* + * Copyright 2019 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 +#import + +#import + +#import "FIRMessaging.h" +#import "FIRMessagingExtensionHelper.h" + +typedef void (^FIRMessagingContentHandler)(UNNotificationContent *content); + +static NSString *const kFCMPayloadOptionsName = @"fcm_options"; +static NSString *const kFCMPayloadOptionsImageURLName = @"image"; +static NSString *const kValidImageURL = + @"https://firebasestorage.googleapis.com/v0/b/fcm-ios-f7f9c.appspot.com/o/" + @"chubbyBunny.jpg?alt=media&token=d6c56a57-c007-4b27-b20f-f267cc83e9e5"; + +@interface FIRMessagingExtensionHelper (ExposedForTest) +#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +- (void)loadAttachmentForURL:(NSURL *)attachmentURL + completionHandler:(void (^)(UNNotificationAttachment *))completionHandler; +#endif +@end + +@interface FIRMessagingExtensionHelperTest : XCTestCase { + id _mockExtensionHelper; +} + +@end + +@implementation FIRMessagingExtensionHelperTest + +- (void)setUp { + [super setUp]; + FIRMessagingExtensionHelper *extensionHelper = [FIRMessaging extensionHelper]; + _mockExtensionHelper = OCMPartialMock(extensionHelper); +} + +- (void)tearDown { + [_mockExtensionHelper stopMocking]; +} + +#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +- (void)testModifyNotificationWithValidPayloadData { + XCTestExpectation *validPayloadExpectation = + [self expectationWithDescription:@"Test payload is valid."]; + + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.userInfo = @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : kValidImageURL}}; + FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { + [validPayloadExpectation fulfill]; + }; + [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; + + OCMVerify([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] + completionHandler:[OCMArg any]]); + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +- (void)testModifyNotificationWithInvalidPayloadData { + XCTestExpectation *validPayloadExpectation = + [self expectationWithDescription:@"Test payload is valid."]; + + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.userInfo = + @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : @"a invalid URL"}}; + FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { + [validPayloadExpectation fulfill]; + }; + [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; + + OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] + completionHandler:[OCMArg any]]); + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +- (void)testModifyNotificationWithEmptyPayloadData { + XCTestExpectation *validPayloadExpectation = + [self expectationWithDescription:@"Test payload is valid."]; + + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.userInfo = + @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : @"a invalid URL"}}; + FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { + [validPayloadExpectation fulfill]; + }; + [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; + + OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] + completionHandler:[OCMArg any]]); + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} +#endif + +@end diff --git a/Firebase/Messaging/FIRMMessageCode.h b/Firebase/Messaging/FIRMMessageCode.h index 4b16189affb..810c44c3042 100644 --- a/Firebase/Messaging/FIRMMessageCode.h +++ b/Firebase/Messaging/FIRMMessageCode.h @@ -188,4 +188,10 @@ typedef NS_ENUM(NSInteger, FIRMessagingMessageCode) { kFIRMessagingMessageCodeAnalyticsInvalidEvent = 19006, // I-FCM019006 kFIRMessagingMessageCodeAnalytics007 = 19007, // I-FCM019007 kFIRMessagingMessageCodeAnalyticsCouldNotInvokeAnalyticsLog = 19008, // I-FCM019008 + // FIRMessagingExtensionHelper.m + kFIRMessagingServiceExtensionImageInvalidURL = 20000, + kFIRMessagingServiceExtensionImageNotDownloaded = 20001, + kFIRMessagingServiceExtensionLocalFileNotCreated = 20002, + kFIRMessagingServiceExtensionImageNotAttached = 20003, + }; diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index d360ec0ab2d..6aa6ed93197 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -29,6 +29,7 @@ #import "FIRMessagingContextManagerService.h" #import "FIRMessagingDataMessageManager.h" #import "FIRMessagingDefines.h" +#import "FIRMessagingExtensionHelper.h" #import "FIRMessagingLogger.h" #import "FIRMessagingPubSub.h" #import "FIRMessagingReceiver.h" @@ -178,6 +179,15 @@ + (FIRMessaging *)messaging { return messaging; } ++ (FIRMessagingExtensionHelper *)extensionHelper { + static dispatch_once_t once; + static FIRMessagingExtensionHelper *extensionHelper; + dispatch_once(&once, ^{ + extensionHelper = [[FIRMessagingExtensionHelper alloc] init]; + }); + return extensionHelper; +} + - (instancetype)initWithAnalytics:(nullable id)analytics withInstanceID:(FIRInstanceID *)instanceID withUserDefaults:(GULUserDefaults *)defaults { diff --git a/Firebase/Messaging/FIRMessagingExtensionHelper.m b/Firebase/Messaging/FIRMessagingExtensionHelper.m new file mode 100644 index 00000000000..542bd7254b9 --- /dev/null +++ b/Firebase/Messaging/FIRMessagingExtensionHelper.m @@ -0,0 +1,116 @@ +/* + * Copyright 2019 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 "FIRMessagingExtensionHelper.h" + +#import "FIRMMessageCode.h" +#import "FIRMessagingLogger.h" + +static NSString *const kPayloadOptionsName = @"fcm_options"; +static NSString *const kPayloadOptionsImageURLName = @"image"; + +@interface FIRMessagingExtensionHelper () +@property(nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); +@property(nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; + +@end + +@implementation FIRMessagingExtensionHelper + +- (void)populateNotificationContent:(UNMutableNotificationContent *)content + withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler { + self.contentHandler = [contentHandler copy]; + self.bestAttemptContent = content; + + NSString *currentImageURL = content.userInfo[kPayloadOptionsName][kPayloadOptionsImageURLName]; + if (!currentImageURL) { + [self deliverNotification]; + return; + } +#if TARGET_OS_IOS + NSURL *attachmentURL = [NSURL URLWithString:currentImageURL]; + if (attachmentURL) { + [self loadAttachmentForURL:attachmentURL + completionHandler:^(UNNotificationAttachment *attachment) { + self.bestAttemptContent.attachments = @[ attachment ]; + [self deliverNotification]; + }]; + } else { + FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageInvalidURL, + @"The Image URL provided is invalid %@.", currentImageURL); + [self deliverNotification]; + } +#else + [self deliverNotification]; +#endif +} + +#if TARGET_OS_IOS +- (void)loadAttachmentForURL:(NSURL *)attachmentURL + completionHandler:(void (^)(UNNotificationAttachment *))completionHandler { + __block UNNotificationAttachment *attachment = nil; + + NSURLSession *session = [NSURLSession + sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + [[session + downloadTaskWithURL:attachmentURL + completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) { + if (error != nil) { + FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotDownloaded, + @"Failed to download image given URL %@, error: %@\n", + attachmentURL, error); + completionHandler(attachment); + return; + } + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *fileExtension = + [NSString stringWithFormat:@".%@", [response.suggestedFilename pathExtension]]; + NSURL *localURL = [NSURL + fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExtension]]; + [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error]; + if (error) { + FIRMessagingLoggerError( + kFIRMessagingServiceExtensionLocalFileNotCreated, + @"Failed to move the image file to local location: %@, error: %@\n", localURL, + error); + completionHandler(attachment); + return; + } + + attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" + URL:localURL + options:nil + error:&error]; + if (error) { + FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotAttached, + @"Failed to create attachment with URL %@, error: %@\n", + localURL, error); + completionHandler(attachment); + return; + } + completionHandler(attachment); + }] resume]; +} +#endif + +- (void)deliverNotification { + if (self.contentHandler) { + self.contentHandler(self.bestAttemptContent); + } +} + +@end diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h index d0ccfacf830..a0c6c51fe38 100644 --- a/Firebase/Messaging/Public/FIRMessaging.h +++ b/Firebase/Messaging/Public/FIRMessaging.h @@ -245,6 +245,8 @@ NS_SWIFT_NAME(MessagingRemoteMessage) @end @class FIRMessaging; +@class FIRMessagingExtensionHelper; + /** * A protocol to handle token update or data message delivery from FCM. * @@ -330,6 +332,17 @@ NS_SWIFT_NAME(Messaging) */ + (instancetype)messaging NS_SWIFT_NAME(messaging()); +/** + * FIRMessagingExtensionHelper + * + * Use FIRMessagingExtensionHelper to populate rich UI contents for your notifications. + * e.g. If an image URL is set in your notification payload or on the console, call + * FIRMessagingExtensionHelper API to render it on your notification. + * + * @return An instance of FIRMessagingExtensionHelper that handles the extensions API. + */ ++ (FIRMessagingExtensionHelper *)extensionHelper NS_SWIFT_NAME(serviceExtension()) NS_AVAILABLE_IOS(10.0); + /** * Unavailable. Use +messaging instead. */ diff --git a/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h b/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h new file mode 100644 index 00000000000..51679da24ec --- /dev/null +++ b/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h @@ -0,0 +1,34 @@ +/* + * Copyright 2019 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. + */ +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +__IOS_AVAILABLE(10.0) +@interface FIRMessagingExtensionHelper : NSObject + +/// Call this API to complete your notification content modification. If you like to +/// overwrite some properties of the content instead of using the default payload, +/// make sure to make your customized motification to the content before passing it to +/// this call. +- (void)populateNotificationContent:(UNMutableNotificationContent *)content + withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Messaging/Public/FirebaseMessaging.h b/Firebase/Messaging/Public/FirebaseMessaging.h index ef081c90f55..c5d0bd05046 100755 --- a/Firebase/Messaging/Public/FirebaseMessaging.h +++ b/Firebase/Messaging/Public/FirebaseMessaging.h @@ -15,3 +15,4 @@ */ #import "FIRMessaging.h" +#import "FIRMessagingExtensionHelper.h" From 97ccf374d54b4233167289a747c3c22b0e86c5d2 Mon Sep 17 00:00:00 2001 From: christibbs <43829046+christibbs@users.noreply.github.com> Date: Mon, 11 Mar 2019 15:09:04 -0700 Subject: [PATCH 012/214] Use UIScreen mainScreen bounds rather than UIApplicationDelegate window to size the UIWindow for in-app message display (#2518) --- .../InAppMessagingDisplay/FIDRenderingWindowHelper.m | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Firebase/InAppMessagingDisplay/FIDRenderingWindowHelper.m b/Firebase/InAppMessagingDisplay/FIDRenderingWindowHelper.m index 6714dbd640e..c133efc60a3 100644 --- a/Firebase/InAppMessagingDisplay/FIDRenderingWindowHelper.m +++ b/Firebase/InAppMessagingDisplay/FIDRenderingWindowHelper.m @@ -24,8 +24,7 @@ + (UIWindow *)UIWindowForModalView { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - UIWindow *appWindow = [[[UIApplication sharedApplication] delegate] window]; - UIWindowForModal = [[UIWindow alloc] initWithFrame:[appWindow frame]]; + UIWindowForModal = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIWindowForModal.windowLevel = UIWindowLevelNormal; }); return UIWindowForModal; @@ -36,8 +35,7 @@ + (UIWindow *)UIWindowForBannerView { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - UIWindow *appWindow = [[[UIApplication sharedApplication] delegate] window]; - UIWindowForBanner = [[FIDBannerViewUIWindow alloc] initWithFrame:[appWindow frame]]; + UIWindowForBanner = [[FIDBannerViewUIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIWindowForBanner.windowLevel = UIWindowLevelNormal; }); @@ -49,8 +47,7 @@ + (UIWindow *)UIWindowForImageOnlyView { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - UIWindow *appWindow = [[[UIApplication sharedApplication] delegate] window]; - UIWindowForImageOnly = [[UIWindow alloc] initWithFrame:[appWindow frame]]; + UIWindowForImageOnly = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIWindowForImageOnly.windowLevel = UIWindowLevelNormal; }); From 061a97d26502e43cbd0c28f23bf2162764b20449 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Mon, 11 Mar 2019 17:30:04 -0700 Subject: [PATCH 013/214] make VerifyAssertion is valid with pendingToken (#2521) --- Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m index 25dc4cadea9..3a819d7a8ec 100644 --- a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m +++ b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m @@ -120,9 +120,9 @@ - (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable) value:_providerAccessToken]]; } - if (!_providerIDToken && !_providerAccessToken && !_requestURI) { + if (!_providerIDToken && !_providerAccessToken && !_pendingToken && !_requestURI) { [NSException raise:NSInvalidArgumentException - format:@"Either IDToken or accessToken must be supplied."]; + format:@"One of IDToken, accessToken, pendingToken, or requestURI must be supplied."]; } if (_providerOAuthTokenSecret) { From 1363332ab720c4d65c9d5133d8af3be47d9c2f21 Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 11 Mar 2019 17:40:38 -0700 Subject: [PATCH 014/214] Update CHANGELOG for Firestore v1.1.0 (#2520) --- Firestore/CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 1abc55b329b..e92b663da5b 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,9 +1,11 @@ # Unreleased -- [changed] Improved performance when querying over documents that contain - subcollections. + +# 1.1.0 - [feature] Added `FieldValue.increment()`, which can be used in `updateData(_:)` and `setData(_:merge:)` to increment or decrement numeric field values safely without transactions. +- [changed] Improved performance when querying over documents that contain + subcollections (#2466). - [changed] Prepared the persistence layer to support collection group queries. While this feature is not yet available, all schema changes are included in this release. From e53d552d5e1e6e90d16a2e08ee1596389225afba Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Mon, 11 Mar 2019 21:33:07 -0400 Subject: [PATCH 015/214] Allow Bundle IDs that have a valid prefix - strict validation (#2515) Allow Bundle IDs that have a valid prefix - strict validation --- Example/Core/Tests/FIRBundleUtilTest.m | 44 ++++++++++++++++++++++++-- Firebase/Core/FIRApp.m | 4 +-- Firebase/Core/FIRBundleUtil.m | 22 +++++++++++-- Firebase/Core/Private/FIRBundleUtil.h | 5 +-- FirebaseCore.podspec | 1 + 5 files changed, 67 insertions(+), 9 deletions(-) diff --git a/Example/Core/Tests/FIRBundleUtilTest.m b/Example/Core/Tests/FIRBundleUtilTest.m index 1204a8945fc..ad0669ddf54 100644 --- a/Example/Core/Tests/FIRBundleUtilTest.m +++ b/Example/Core/Tests/FIRBundleUtilTest.m @@ -15,6 +15,7 @@ #import "FIRTestCase.h" #import +#import static NSString *const kResultPath = @"resultPath"; static NSString *const kResourceName = @"resourceName"; @@ -69,16 +70,53 @@ - (void)testFindOptionsDictionaryPath_secondBundle { - (void)testBundleIdentifierExistsInBundles { NSString *bundleID = @"com.google.test"; [OCMStub([self.mockBundle bundleIdentifier]) andReturn:bundleID]; - XCTAssertTrue([FIRBundleUtil hasBundleIdentifier:bundleID inBundles:@[ self.mockBundle ]]); + XCTAssertTrue([FIRBundleUtil hasBundleIdentifierPrefix:bundleID inBundles:@[ self.mockBundle ]]); } - (void)testBundleIdentifierExistsInBundles_notExist { [OCMStub([self.mockBundle bundleIdentifier]) andReturn:@"com.google.test"]; - XCTAssertFalse([FIRBundleUtil hasBundleIdentifier:@"not-exist" inBundles:@[ self.mockBundle ]]); + XCTAssertFalse([FIRBundleUtil hasBundleIdentifierPrefix:@"not-exist" + inBundles:@[ self.mockBundle ]]); } - (void)testBundleIdentifierExistsInBundles_emptyBundlesArray { - XCTAssertFalse([FIRBundleUtil hasBundleIdentifier:@"com.google.test" inBundles:@[]]); + XCTAssertFalse([FIRBundleUtil hasBundleIdentifierPrefix:@"com.google.test" inBundles:@[]]); +} + +- (void)testBundleIdentifierHasPrefixInBundlesForExtension { + id environmentUtilsMock = [OCMockObject mockForClass:[GULAppEnvironmentUtil class]]; + [[[environmentUtilsMock stub] andReturnValue:@(YES)] isAppExtension]; + + [OCMStub([self.mockBundle bundleIdentifier]) andReturn:@"com.google.test"]; + XCTAssertTrue([FIRBundleUtil hasBundleIdentifierPrefix:@"com.google.test.someextension" + inBundles:@[ self.mockBundle ]]); + + [environmentUtilsMock stopMocking]; +} + +- (void)testBundleIdentifierHasPrefixInBundlesNotValidExtension { + id environmentUtilsMock = [OCMockObject mockForClass:[GULAppEnvironmentUtil class]]; + [[[environmentUtilsMock stub] andReturnValue:@(YES)] isAppExtension]; + + [OCMStub([self.mockBundle bundleIdentifier]) andReturn:@"com.google.test"]; + XCTAssertFalse([FIRBundleUtil hasBundleIdentifierPrefix:@"com.google.test.someextension.some" + inBundles:@[ self.mockBundle ]]); + + XCTAssertFalse([FIRBundleUtil hasBundleIdentifierPrefix:@"com.google.testsomeextension" + inBundles:@[ self.mockBundle ]]); + + XCTAssertFalse([FIRBundleUtil hasBundleIdentifierPrefix:@"com.google.testsomeextension.some" + inBundles:@[ self.mockBundle ]]); + + XCTAssertFalse([FIRBundleUtil hasBundleIdentifierPrefix:@"not-exist" + inBundles:@[ self.mockBundle ]]); + + // Should be NO, since if @"com.google.tests" is an app extension identifier, then the app bundle + // identifier is @"com.google" + XCTAssertFalse([FIRBundleUtil hasBundleIdentifierPrefix:@"com.google.tests" + inBundles:@[ self.mockBundle ]]); + + [environmentUtilsMock stopMocking]; } @end diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index 799db26f5c8..300619bbb9f 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -525,8 +525,8 @@ - (void)checkExpectedBundleID { NSString *expectedBundleID = [self expectedBundleID]; // The checking is only done when the bundle ID is provided in the serviceInfo dictionary for // backward compatibility. - if (expectedBundleID != nil && ![FIRBundleUtil hasBundleIdentifier:expectedBundleID - inBundles:bundles]) { + if (expectedBundleID != nil && ![FIRBundleUtil hasBundleIdentifierPrefix:expectedBundleID + inBundles:bundles]) { FIRLogError(kFIRLoggerCore, @"I-COR000008", @"The project's Bundle ID is inconsistent with " @"either the Bundle ID in '%@.%@', or the Bundle ID in the options if you are " diff --git a/Firebase/Core/FIRBundleUtil.m b/Firebase/Core/FIRBundleUtil.m index 93ee02e97db..841833a8519 100644 --- a/Firebase/Core/FIRBundleUtil.m +++ b/Firebase/Core/FIRBundleUtil.m @@ -14,6 +14,8 @@ #import "Private/FIRBundleUtil.h" +#import + @implementation FIRBundleUtil + (NSArray *)relevantBundles { @@ -45,13 +47,29 @@ + (NSArray *)relevantURLSchemes { return result; } -+ (BOOL)hasBundleIdentifier:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles { ++ (BOOL)hasBundleIdentifierPrefix:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles { for (NSBundle *bundle in bundles) { - if ([bundle.bundleIdentifier isEqualToString:bundleIdentifier]) { + // This allows app extensions that have the app's bundle as their prefix to pass this test. + NSString *applicationBundleIdentifier = + [GULAppEnvironmentUtil isAppExtension] + ? [self bundleIdentifierByRemovingLastPartFrom:bundleIdentifier] + : bundleIdentifier; + + if ([applicationBundleIdentifier isEqualToString:bundle.bundleIdentifier]) { return YES; } } return NO; } ++ (NSString *)bundleIdentifierByRemovingLastPartFrom:(NSString *)bundleIdentifier { + NSString *bundleIDComponentsSeparator = @"."; + + NSMutableArray *bundleIDComponents = + [[bundleIdentifier componentsSeparatedByString:bundleIDComponentsSeparator] mutableCopy]; + [bundleIDComponents removeLastObject]; + + return [bundleIDComponents componentsJoinedByString:bundleIDComponentsSeparator]; +} + @end diff --git a/Firebase/Core/Private/FIRBundleUtil.h b/Firebase/Core/Private/FIRBundleUtil.h index c458a2c4c6d..d9475dd29e0 100644 --- a/Firebase/Core/Private/FIRBundleUtil.h +++ b/Firebase/Core/Private/FIRBundleUtil.h @@ -45,8 +45,9 @@ + (NSArray *)relevantURLSchemes; /** - * Checks if the bundle identifier exists in the given bundles. + * Checks if any of the given bundles have a matching bundle identifier prefix (removing extension + * suffixes). */ -+ (BOOL)hasBundleIdentifier:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles; ++ (BOOL)hasBundleIdentifierPrefix:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles; @end diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 865632719cc..7a390876349 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -28,6 +28,7 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration s.public_header_files = 'Firebase/Core/Public/*.h', 'Firebase/Core/Private/*.h' s.private_header_files = 'Firebase/Core/Private/*.h' s.framework = 'Foundation' + s.dependency 'GoogleUtilities/Environment', '~> 5.2' s.dependency 'GoogleUtilities/Logger', '~> 5.2' s.pod_target_xcconfig = { 'GCC_C_LANGUAGE_STANDARD' => 'c99', From dc31021a98876505e91f2d1b38530214d89b6f23 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Tue, 12 Mar 2019 10:54:12 -0400 Subject: [PATCH 016/214] Only assign message validation regex once (#2523) * Only assign regex helper once. This fixes an internal error (b/128359839) that causes tests to crash. * Re-add unused param. --- GoogleUtilities/Logger/GULASLLogger.m | 4 +-- GoogleUtilities/Logger/GULLogger.m | 40 ++++++++++++--------------- GoogleUtilities/Logger/GULOSLogger.m | 2 +- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/GoogleUtilities/Logger/GULASLLogger.m b/GoogleUtilities/Logger/GULASLLogger.m index fe8e480e335..cab144e87c3 100644 --- a/GoogleUtilities/Logger/GULASLLogger.m +++ b/GoogleUtilities/Logger/GULASLLogger.m @@ -119,9 +119,9 @@ - (void)printToSTDERR { } - (void)logWithLevel:(GULLoggerLevel)level - withService:(GULLoggerService)service + withService:(GULLoggerService)__unused service isForced:(BOOL)forced - withCode:(NSString *)messageCode + withCode:(NSString *)__unused messageCode withMessage:(NSString *)message { // Skip logging this if the level isn't to be logged unless it's forced. if (![self isLoggableLevel:level] && !forced) { diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index 412d4063d82..f52c4b54637 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -26,8 +26,16 @@ #ifdef DEBUG /// The regex pattern for the message code. -static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$"; -static NSRegularExpression *sMessageCodeRegex; +NSRegularExpression *GULMessageCodeRegex() { + static dispatch_once_t onceToken; + static NSRegularExpression *messageCodeRegex; + dispatch_once(&onceToken, ^{ + messageCodeRegex = [NSRegularExpression regularExpressionWithPattern:@"^I-[A-Z]{3}[0-9]{6}$" + options:0 + error:NULL]; + }); + return messageCodeRegex; +} #endif @implementation GULLogger (Internal) @@ -47,16 +55,11 @@ + (NSString *)messageFromLogger:(id)logger message:(NSString *)message { #ifdef DEBUG NSCAssert(code.length == 11, @"Incorrect message code length."); - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sMessageCodeRegex = [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern - options:0 - error:NULL]; - }); + NSRegularExpression *messageCodeRegex = GULMessageCodeRegex(); NSRange messageCodeRange = NSMakeRange(0, code.length); - NSUInteger numberOfMatches = [sMessageCodeRegex numberOfMatchesInString:code - options:0 - range:messageCodeRange]; + NSUInteger numberOfMatches = [messageCodeRegex numberOfMatchesInString:code + options:0 + range:messageCodeRange]; NSCAssert(numberOfMatches == 1, @"Incorrect message code format."); #endif return [NSString stringWithFormat:@"%@ - %@[%@] %@", logger.version, service, code, message]; @@ -68,11 +71,6 @@ + (NSString *)messageFromLogger:(id)logger void GULLoggerInitialize(void) { [GULLogger.logger initializeLogger]; -#ifdef DEBUG - sMessageCodeRegex = [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern - options:0 - error:NULL]; -#endif } void GULLoggerInitializeASL(void) { @@ -106,14 +104,12 @@ void GULLogBasic(GULLoggerLevel level, NSString *message, va_list args_ptr) { #ifdef DEBUG - sMessageCodeRegex = [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern - options:0 - error:NULL]; + NSRegularExpression *messageCodeRegex = GULMessageCodeRegex(); NSCAssert(messageCode.length == 11, @"Incorrect message code length."); NSRange messageCodeRange = NSMakeRange(0, messageCode.length); - NSUInteger numberOfMatches = [sMessageCodeRegex numberOfMatchesInString:messageCode - options:0 - range:messageCodeRange]; + NSUInteger numberOfMatches = [messageCodeRegex numberOfMatchesInString:messageCode + options:0 + range:messageCodeRange]; NSCAssert(numberOfMatches == 1, @"Incorrect message code format."); #endif NSString *logMsg = [[NSString alloc] initWithFormat:message arguments:args_ptr]; diff --git a/GoogleUtilities/Logger/GULOSLogger.m b/GoogleUtilities/Logger/GULOSLogger.m index cf6de63c8d5..c6ee878358e 100644 --- a/GoogleUtilities/Logger/GULOSLogger.m +++ b/GoogleUtilities/Logger/GULOSLogger.m @@ -152,7 +152,7 @@ - (BOOL)isLoggableLevel:(GULLoggerLevel)logLevel { - (void)logWithLevel:(GULLoggerLevel)level withService:(GULLoggerService)service isForced:(BOOL)forced - withCode:(NSString *)messageCode + withCode:(NSString *)__unused messageCode withMessage:(NSString *)message { // Skip logging this if the level isn't to be logged unless it's forced. if (![self isLoggableLevel:level] && !forced) { From 8c16157508075fba13fa0e9d288c3972b586ca63 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 12 Mar 2019 11:33:49 -0700 Subject: [PATCH 017/214] Remove unused loggerWithLevel parameter (#2524) --- GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m | 6 ------ GoogleUtilities/Logger/GULASLLogger.m | 1 - GoogleUtilities/Logger/GULLogger.m | 1 - GoogleUtilities/Logger/GULOSLogger.m | 1 - GoogleUtilities/Logger/Private/GULLoggerSystem.h | 1 - 5 files changed, 10 deletions(-) diff --git a/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m index 1fd6a5da231..829c3d3859c 100644 --- a/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m +++ b/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m @@ -26,7 +26,6 @@ NS_ASSUME_NONNULL_BEGIN static NSString *const kService = @"my service"; -static NSString *const kCode = @"I-COR000001"; static NSTimeInterval const kTimeout = 1.0; // Expectation that contains the information needed to see if the correct parameters were used in an @@ -166,7 +165,6 @@ - (void)testSetLogLevelValid { OCMReject([self.mock logWithLevel:GULLoggerLevelError withService:OCMOCK_ANY isForced:NO - withCode:OCMOCK_ANY withMessage:OCMOCK_ANY]); #pragma clang diagnostic pop self.osLogger.logLevel = GULLoggerLevelWarning; @@ -180,14 +178,12 @@ - (void)testSetLogLevelInvalid { OCMExpect([[self.mock stub] logWithLevel:GULLoggerLevelError withService:OCMOCK_ANY isForced:YES - withCode:OCMOCK_ANY withMessage:OCMOCK_ANY]); self.osLogger.logLevel = GULLoggerLevelMin - 1; OCMExpect([[self.mock stub] logWithLevel:GULLoggerLevelError withService:OCMOCK_ANY isForced:YES - withCode:OCMOCK_ANY withMessage:OCMOCK_ANY]); #pragma clang diagnostic push self.osLogger.logLevel = GULLoggerLevelMax + 1; @@ -245,7 +241,6 @@ - (void)testLoggingValidNoVarArgs { [self.osLogger logWithLevel:GULLoggerLevelNotice withService:kService isForced:NO - withCode:kCode withMessage:message]; [self waitForExpectations:sExpectations timeout:kTimeout]; } @@ -260,7 +255,6 @@ - (void)testLoggingValidWithVarArgs { [self.osLogger logWithLevel:GULLoggerLevelNotice withService:kService isForced:NO - withCode:kCode withMessage:message]; [self waitForExpectations:sExpectations timeout:kTimeout]; } diff --git a/GoogleUtilities/Logger/GULASLLogger.m b/GoogleUtilities/Logger/GULASLLogger.m index cab144e87c3..d900d58b835 100644 --- a/GoogleUtilities/Logger/GULASLLogger.m +++ b/GoogleUtilities/Logger/GULASLLogger.m @@ -121,7 +121,6 @@ - (void)printToSTDERR { - (void)logWithLevel:(GULLoggerLevel)level withService:(GULLoggerService)__unused service isForced:(BOOL)forced - withCode:(NSString *)__unused messageCode withMessage:(NSString *)message { // Skip logging this if the level isn't to be logged unless it's forced. if (![self isLoggableLevel:level] && !forced) { diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index f52c4b54637..19cc5479596 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -118,7 +118,6 @@ void GULLogBasic(GULLoggerLevel level, [GULLogger.logger logWithLevel:level withService:service isForced:forceLog - withCode:messageCode withMessage:formattedMsg]; } diff --git a/GoogleUtilities/Logger/GULOSLogger.m b/GoogleUtilities/Logger/GULOSLogger.m index c6ee878358e..39b6061d073 100644 --- a/GoogleUtilities/Logger/GULOSLogger.m +++ b/GoogleUtilities/Logger/GULOSLogger.m @@ -152,7 +152,6 @@ - (BOOL)isLoggableLevel:(GULLoggerLevel)logLevel { - (void)logWithLevel:(GULLoggerLevel)level withService:(GULLoggerService)service isForced:(BOOL)forced - withCode:(NSString *)__unused messageCode withMessage:(NSString *)message { // Skip logging this if the level isn't to be logged unless it's forced. if (![self isLoggableLevel:level] && !forced) { diff --git a/GoogleUtilities/Logger/Private/GULLoggerSystem.h b/GoogleUtilities/Logger/Private/GULLoggerSystem.h index ceff5b125fa..358faca4ccb 100644 --- a/GoogleUtilities/Logger/Private/GULLoggerSystem.h +++ b/GoogleUtilities/Logger/Private/GULLoggerSystem.h @@ -44,7 +44,6 @@ typedef NSString *const GULLoggerService; - (void)logWithLevel:(GULLoggerLevel)level withService:(GULLoggerService)service isForced:(BOOL)forced - withCode:(NSString *)messageCode withMessage:(NSString *)message; @end From 7358e90fc7e7652ebeacda89162f1e21185efbb2 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Tue, 12 Mar 2019 16:33:34 -0400 Subject: [PATCH 018/214] Fix build (unused parameter) under mac/cmake/clang (#2526) Only seems to impact the mac cmake build (not the xcode build). Linux build is unaffected since there's an ifdef objc at the top of the file. --- .../core/src/firebase/firestore/model/transform_operations.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/model/transform_operations.h b/Firestore/core/src/firebase/firestore/model/transform_operations.h index c4ac9849a48..8002907037f 100644 --- a/Firestore/core/src/firebase/firestore/model/transform_operations.h +++ b/Firestore/core/src/firebase/firestore/model/transform_operations.h @@ -282,8 +282,7 @@ class NumericIncrementTransform : public TransformOperation { } FSTFieldValue* ApplyToRemoteDocument( - FSTFieldValue* previousValue, - FSTFieldValue* transformResult) const override { + FSTFieldValue*, FSTFieldValue* transformResult) const override { return transformResult; } From b67bc10d6cc99273196bd8b0a63f675759910660 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Tue, 12 Mar 2019 14:40:26 -0700 Subject: [PATCH 019/214] deprecate Microsoft and Yahoo Auth Provider ID (#2517) --- Firebase/Auth/Source/Public/FIROAuthProvider.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Firebase/Auth/Source/Public/FIROAuthProvider.h b/Firebase/Auth/Source/Public/FIROAuthProvider.h index 29531012108..1ddf1f834df 100644 --- a/Firebase/Auth/Source/Public/FIROAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIROAuthProvider.h @@ -23,17 +23,17 @@ NS_ASSUME_NONNULL_BEGIN - /** @brief A string constant identifying the Microsoft identity provider. */ -extern NSString *const FIRMicrosoftAuthProviderID NS_SWIFT_NAME(MicrosoftAuthProviderID); +extern NSString *const FIRMicrosoftAuthProviderID NS_SWIFT_NAME(MicrosoftAuthProviderID) + DEPRECATED_MSG_ATTRIBUTE("Please use \"microsoft.com\" instead."); /** @brief A string constant identifying the Yahoo identity provider. */ -extern NSString *const FIRYahooAuthProviderID NS_SWIFT_NAME(YahooAuthProviderID); - +extern NSString *const FIRYahooAuthProviderID NS_SWIFT_NAME(YahooAuthProviderID) + DEPRECATED_MSG_ATTRIBUTE("Please use \"yahoo.com\" instead."); /** @class FIROAuthProvider @brief A concrete implementation of `FIRAuthProvider` for generic OAuth Providers. From 6a38e0851822b48a17a79928fa8cd6d3d5f162a2 Mon Sep 17 00:00:00 2001 From: christibbs <43829046+christibbs@users.noreply.github.com> Date: Tue, 12 Mar 2019 15:13:51 -0700 Subject: [PATCH 020/214] FIAM release notes for M45 (#2532) * Release notes for M45 * Better words in change log * Add issue number --- Firebase/InAppMessagingDisplay/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firebase/InAppMessagingDisplay/CHANGELOG.md b/Firebase/InAppMessagingDisplay/CHANGELOG.md index 7189f327252..ece3d26671f 100644 --- a/Firebase/InAppMessagingDisplay/CHANGELOG.md +++ b/Firebase/InAppMessagingDisplay/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2019-03-19 -- v0.13.1 +- Fixed a crash (#2498) that occurred when integrating In-App Messaging into NativeScript apps. + # 2019-03-05 -- v0.13.0 - Added a feature allowing developers to programmatically register a delegate for updates on in-app engagement (impression, click, display errors). From 9c95d61304db50fa73acce5c286f85c383cb63c4 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 12 Mar 2019 15:58:52 -0700 Subject: [PATCH 021/214] Update project and fix warnings (#2531) * Update Firestore project to Xcode 10.1 defaults * Fix NSUInteger in format strings warning * Fix implicit conversion warning in FSTLevelDB * Fix BOOL/bool mismatch * Fix erroneous comparison This failure shows up on 32-bit targets. I have no idea why this succeeds on 64-bit targets. * Fix incorrect type inference with explicit cast clang is inferring the type of the key in these maps is `NSString *` and then complaining that `FIRFieldValue *` doesn't match that type, despite the underlying method taking `NSDictionary`. Explicitly casting to `id` prevents the warning. * Remove reliance on representation in FSTFieldValue tests On *some* platforms [NSNumber numberWithChar:1] ends up with a "c" (signed char) objcType, but apparently on 32-bit platforms this ends up as "i" (signed int). Rather than relying on this working properly just test that [NSNumber numberWithBool:] does the right thing. * Fix field_value_test on 32-bit targets When sizeof(void*) is 4 the test failed :-(. * Rephrase testWritesWithLargeNumbersFail in terms of fixed size types The prior definition only worked on LP64. Switching to LLONG_MAX would have also fixed this, but Firestore's data model is independent of C's data model, so rewriting this in terms of fixed size types more closely models what's actually an invalid value. * Fix implicit retain self warnings --- .../Firestore.xcodeproj/project.pbxproj | 29 +++++++++++++++- .../xcschemes/AllTests_iOS.xcscheme | 2 +- .../xcschemes/Firestore_Example_iOS.xcscheme | 2 +- .../Firestore_FuzzTests_iOS.xcscheme | 34 +++++++++++++++++-- .../Firestore_IntegrationTests_iOS.xcscheme | 2 +- .../Firestore_SwiftTests_iOS.xcscheme | 2 +- .../xcschemes/Firestore_Tests_iOS.xcscheme | 2 +- .../Tests/API/FIRQuerySnapshotTests.mm | 13 +++---- Firestore/Example/Tests/API/FSTAPIHelpers.h | 4 +-- Firestore/Example/Tests/API/FSTAPIHelpers.mm | 4 +-- .../Tests/Core/FSTQueryListenerTests.mm | 16 ++++----- .../Example/Tests/Core/FSTViewSnapshotTest.mm | 4 +-- .../Tests/Integration/API/FIRDatabaseTests.mm | 7 ++-- .../Tests/Integration/API/FIRFieldsTests.mm | 4 +-- .../API/FIRNumericTransformTests.mm | 4 +-- .../API/FIRServerTimestampTests.mm | 6 ++-- .../Integration/API/FIRValidationTests.mm | 4 ++- .../Example/Tests/Model/FSTFieldValueTests.mm | 2 +- Firestore/Example/Tests/Util/FSTHelpers.h | 4 +-- Firestore/Source/Local/FSTLevelDB.mm | 4 +-- .../firestore/model/field_value_test.cc | 2 +- 21 files changed, 106 insertions(+), 45 deletions(-) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index eabf0f811e0..506f7b881dd 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -1486,7 +1486,7 @@ attributes = { CLASSPREFIX = FIR; LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0720; + LastUpgradeCheck = 1010; ORGANIZATIONNAME = Google; TargetAttributes = { 54C9EDF02040E16300A969CD = { @@ -2501,19 +2501,32 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = c99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -2543,18 +2556,31 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -2565,6 +2591,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; OTHER_CFLAGS = ""; SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/AllTests_iOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/AllTests_iOS.xcscheme index 69c49567444..325de9bfba8 100644 --- a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/AllTests_iOS.xcscheme +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/AllTests_iOS.xcscheme @@ -1,6 +1,6 @@ + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + + + + @@ -50,6 +62,15 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> + + + + + + + + diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_iOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_iOS.xcscheme index 4e6f82845e2..03e9133eefe 100644 --- a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_iOS.xcscheme +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_iOS.xcscheme @@ -1,6 +1,6 @@ *> *oldDocs, NSDictionary *> *docsToAdd, - BOOL hasPendingWrites, - BOOL fromCache); + bool hasPendingWrites, + bool fromCache); #if __cplusplus } // extern "C" diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.mm b/Firestore/Example/Tests/API/FSTAPIHelpers.mm index 3e8d263f0e8..61332b50744 100644 --- a/Firestore/Example/Tests/API/FSTAPIHelpers.mm +++ b/Firestore/Example/Tests/API/FSTAPIHelpers.mm @@ -95,8 +95,8 @@ const absl::string_view path, NSDictionary *> *oldDocs, NSDictionary *> *docsToAdd, - BOOL hasPendingWrites, - BOOL fromCache) { + bool hasPendingWrites, + bool fromCache) { FIRSnapshotMetadata *metadata = [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:hasPendingWrites fromCache:fromCache]; FSTDocumentSet *oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[]); diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index f4b700c17eb..064ef9caee0 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -69,7 +69,7 @@ - (void)setUp { waitForSyncWhenOnline:NO]; } -- (ViewSnapshot)setExcludesMetadataChanges:(BOOL)excludesMetadataChanges +- (ViewSnapshot)setExcludesMetadataChanges:(bool)excludesMetadataChanges snapshot:(const ViewSnapshot &)snapshot { return ViewSnapshot{ snapshot.query(), @@ -228,8 +228,8 @@ - (void)testDoesNotRaiseEventsForMetadataChangesUnlessSpecified { [fullListener queryDidChangeViewSnapshot:snap3]; // doc2 update XCTAssertTrue((filteredAccum == - std::vector{[self setExcludesMetadataChanges:YES snapshot:snap1], - [self setExcludesMetadataChanges:YES snapshot:snap3]})); + std::vector{[self setExcludesMetadataChanges:true snapshot:snap1], + [self setExcludesMetadataChanges:true snapshot:snap3]})); XCTAssertTrue((fullAccum == std::vector{snap1, snap2, snap3})); } @@ -273,8 +273,8 @@ - (void)testRaisesDocumentMetadataEventsOnlyWhenSpecified { [fullListener queryDidChangeViewSnapshot:snap3]; XCTAssertTrue((filteredAccum == - std::vector{[self setExcludesMetadataChanges:YES snapshot:snap1], - [self setExcludesMetadataChanges:YES snapshot:snap3]})); + std::vector{[self setExcludesMetadataChanges:true snapshot:snap1], + [self setExcludesMetadataChanges:true snapshot:snap3]})); XCTAssertTrue( (filteredAccum[0].document_changes() == std::vector{change1, change2})); XCTAssertTrue((filteredAccum[1].document_changes() == std::vector{change4})); @@ -329,8 +329,8 @@ - (void)testRaisesQueryMetadataEventsOnlyWhenHasPendingWritesOnTheQueryChanges { /*excludes_metadata_changes=*/true // This test excludes document metadata changes }; XCTAssertTrue( - (fullAccum == std::vector{[self setExcludesMetadataChanges:YES snapshot:snap1], - [self setExcludesMetadataChanges:YES snapshot:snap3], + (fullAccum == std::vector{[self setExcludesMetadataChanges:true snapshot:snap1], + [self setExcludesMetadataChanges:true snapshot:snap3], expectedSnap4})); } @@ -365,7 +365,7 @@ - (void)testMetadataOnlyDocumentChangesAreFilteredOutWhenIncludeDocumentMetadata snap2.from_cache(), snap2.sync_state_changed(), /*excludes_metadata_changes=*/true}; - XCTAssertTrue((filteredAccum == std::vector{[self setExcludesMetadataChanges:YES + XCTAssertTrue((filteredAccum == std::vector{[self setExcludesMetadataChanges:true snapshot:snap1], expectedSnap2})); } diff --git a/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm b/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm index 7b312ee51dd..6dbdd805b4e 100644 --- a/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm +++ b/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm @@ -107,9 +107,9 @@ - (void)testViewSnapshotConstructor { std::vector documentChanges{DocumentViewChange{ FSTTestDoc("c/a", 1, @{}, FSTDocumentStateSynced), DocumentViewChange::Type::kAdded}}; - BOOL fromCache = YES; + bool fromCache = true; DocumentKeySet mutatedKeys; - BOOL syncStateChanged = YES; + bool syncStateChanged = true; ViewSnapshot snapshot{query, documents, diff --git a/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm b/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm index 6782563908e..89f6225c621 100644 --- a/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm @@ -1040,7 +1040,8 @@ - (void)testUpdateFieldsWithDots { [self writeDocumentRef:doc data:@{@"a.b" : @"old", @"c.d" : @"old"}]; - [self updateDocumentRef:doc data:@{[[FIRFieldPath alloc] initWithFields:@[ @"a.b" ]] : @"new"}]; + [self updateDocumentRef:doc + data:@{(id)[[FIRFieldPath alloc] initWithFields:@[ @"a.b" ]] : @"new"}]; XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateFieldsWithDots"]; @@ -1065,8 +1066,8 @@ - (void)testUpdateNestedFields { [self updateDocumentRef:doc data:@{ - @"a.b" : @"new", - [[FIRFieldPath alloc] initWithFields:@[ @"c", @"d" ]] : @"new" + (id) @"a.b" : @"new", + (id)[[FIRFieldPath alloc] initWithFields:@[ @"c", @"d" ]] : @"new" }]; XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateNestedFields"]; diff --git a/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm b/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm index be27c1857af..359dd412697 100644 --- a/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm @@ -163,8 +163,8 @@ - (void)testFieldsWithSpecialCharsCanBeUpdated { [self writeDocumentRef:doc data:testData]; [self updateDocumentRef:doc data:@{ - [[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]] : @100, - @"c\\slash" : @200 + (id)[[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]] : @100, + (id) @"c\\slash" : @200 }]; FIRDocumentSnapshot *result = [self readDocumentForRef:doc]; diff --git a/Firestore/Example/Tests/Integration/API/FIRNumericTransformTests.mm b/Firestore/Example/Tests/Integration/API/FIRNumericTransformTests.mm index 8cd1fa1e54b..fa742025cd0 100644 --- a/Firestore/Example/Tests/Integration/API/FIRNumericTransformTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRNumericTransformTests.mm @@ -70,9 +70,9 @@ - (void)writeInitialData:(NSDictionary *)data { - (void)expectLocalAndRemoteValue:(int64_t)expectedSum { FIRDocumentSnapshot *snap = [_accumulator awaitLocalEvent]; - XCTAssertEqual(@(expectedSum), snap[@"sum"]); + XCTAssertEqualObjects(@(expectedSum), snap[@"sum"]); snap = [_accumulator awaitRemoteEvent]; - XCTAssertEqual(@(expectedSum), snap[@"sum"]); + XCTAssertEqualObjects(@(expectedSum), snap[@"sum"]); } - (void)expectApproximateLocalAndRemoteValue:(double)expectedSum { diff --git a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm index ba5fa55a2db..4af81d26556 100644 --- a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm @@ -264,7 +264,7 @@ - (void)testServerTimestampsPreviousValueFromLocalMutation { - (void)testServerTimestampsWorkViaTransactionSet { [self runTransactionBlock:^(FIRTransaction *transaction) { - [transaction setData:_setData forDocument:_docRef]; + [transaction setData:self->_setData forDocument:self->_docRef]; }]; [self verifySnapshotWithResolvedTimestamps:[_accumulator awaitRemoteEvent]]; @@ -273,7 +273,7 @@ - (void)testServerTimestampsWorkViaTransactionSet { - (void)testServerTimestampsWorkViaTransactionUpdate { [self writeInitialData]; [self runTransactionBlock:^(FIRTransaction *transaction) { - [transaction updateData:_updateData forDocument:_docRef]; + [transaction updateData:self->_updateData forDocument:self->_docRef]; }]; [self verifySnapshotWithResolvedTimestamps:[_accumulator awaitRemoteEvent]]; } @@ -294,7 +294,7 @@ - (void)testServerTimestampsFailViaTransactionUpdateOnNonexistentDocument { XCTestExpectation *expectation = [self expectationWithDescription:@"transaction complete"]; [_docRef.firestore runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **pError) { - [transaction updateData:_updateData forDocument:_docRef]; + [transaction updateData:self->_updateData forDocument:self->_docRef]; return nil; } completion:^(id result, NSError *error) { diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index 1cd345026f4..15cfc004238 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -18,6 +18,8 @@ #import +#include + #import "Firestore/Source/API/FIRFieldValue+Internal.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" @@ -228,7 +230,7 @@ - (void)testWritesWithInvalidTypesFail { } - (void)testWritesWithLargeNumbersFail { - NSNumber *num = @((unsigned long long)LONG_MAX + 1); + NSNumber *num = @(static_cast(std::numeric_limits::max()) + 1); NSString *reason = [NSString stringWithFormat:@"NSNumber (%@) is too large (found in field num)", num]; [self expectWrite:@{@"num" : num} toFailWithReason:reason]; diff --git a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm index 45a07f2c194..76ff572c2c5 100644 --- a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm +++ b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm @@ -116,7 +116,7 @@ - (void)testWrapsNilAndNSNull { } - (void)testWrapsBooleans { - NSArray *values = @[ @YES, @NO, [NSNumber numberWithChar:1], [NSNumber numberWithChar:0] ]; + NSArray *values = @[ @YES, @NO ]; for (id value in values) { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTBooleanValue class]); diff --git a/Firestore/Example/Tests/Util/FSTHelpers.h b/Firestore/Example/Tests/Util/FSTHelpers.h index 1209b6a50e4..8a485f2dca8 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.h +++ b/Firestore/Example/Tests/Util/FSTHelpers.h @@ -82,9 +82,9 @@ NS_ASSUME_NONNULL_BEGIN NSComparisonResult result = [left compare:right]; \ NSComparisonResult inverseResult = [right compare:left]; \ XCTAssertEqual(result, expected, @"comparing %@ with %@ at (%lu, %lu)", left, right, \ - i, j); \ + (unsigned long)i, (unsigned long)j); \ XCTAssertEqual(inverseResult, -expected, @"comparing %@ with %@ at (%lu, %lu)", right, \ - left, j, i); \ + left, (unsigned long)j, (unsigned long)i); \ } \ } \ } \ diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index 94b1c47958e..89a719fbb2f 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -377,8 +377,8 @@ - (size_t)byteSize { } HARD_ASSERT(iter->status().ok(), "Failed to iterate leveldb directory: %s", iter->status().error_message().c_str()); - HARD_ASSERT(count <= SIZE_MAX, "Overflowed counting bytes cached"); - return count; + HARD_ASSERT(count >= 0 && count <= SIZE_MAX, "Overflowed counting bytes cached"); + return static_cast(count); } - (const std::set &)users { 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 fc7d0177a9d..fa3902d52f4 100644 --- a/Firestore/core/test/firebase/firestore/model/field_value_test.cc +++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc @@ -578,7 +578,7 @@ TEST(FieldValue, IsSmallish) { // We expect the FV to use 4 bytes to track the type of the union, plus 8 // bytes for the union contents themselves. The other 4 is for padding. We // want to keep FV as small as possible. - EXPECT_LE(sizeof(FieldValue), 2 * sizeof(void*)); + EXPECT_LE(sizeof(FieldValue), 2 * sizeof(int64_t)); } } // namespace model From 8328f781f6e3a5152261e79ddfaa3dd13ff65a74 Mon Sep 17 00:00:00 2001 From: dmandar Date: Tue, 12 Mar 2019 16:32:17 -0700 Subject: [PATCH 022/214] Update CHANGELOG.md (#2536) --- Firebase/DynamicLinks/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firebase/DynamicLinks/CHANGELOG.md b/Firebase/DynamicLinks/CHANGELOG.md index 5cd5dc4af2a..1a24eb88195 100644 --- a/Firebase/DynamicLinks/CHANGELOG.md +++ b/Firebase/DynamicLinks/CHANGELOG.md @@ -1,3 +1,6 @@ +# v3.4.2 +- Fixes an issue with certain analytics attribution parameters not being recorded on an app install. (#2462) + # v3.4.1 - Return call validation for sysctlbyname. (#2394) From b1f909ea2d0de8f2c9435b0a60e91eaa2c80156b Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Tue, 12 Mar 2019 18:29:35 -0700 Subject: [PATCH 023/214] Don't create credential when VerifyAssertionResponse is empty (#2530) Fix #2522 --- Firebase/Auth/Source/RPCs/FIRAuthBackend.m | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m index 2ec672696aa..d4567daaec7 100644 --- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m +++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m @@ -1058,11 +1058,13 @@ + (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorM NSString *email; if ([response isKindOfClass:[FIRVerifyAssertionResponse class]]) { FIRVerifyAssertionResponse *verifyAssertion = (FIRVerifyAssertionResponse *)response; - credential = - [[FIROAuthCredential alloc] initWithProviderID:verifyAssertion.providerID - IDToken:verifyAssertion.oauthIDToken - accessToken:verifyAssertion.oauthAccessToken - pendingToken:verifyAssertion.pendingToken]; + if (verifyAssertion.oauthIDToken.length || verifyAssertion.oauthAccessToken.length) { + credential = + [[FIROAuthCredential alloc] initWithProviderID:verifyAssertion.providerID + IDToken:verifyAssertion.oauthIDToken + accessToken:verifyAssertion.oauthAccessToken + pendingToken:verifyAssertion.pendingToken]; + } email = verifyAssertion.email; } return [FIRAuthErrorUtils credentialAlreadyInUseErrorWithMessage:serverDetailErrorMessage From 216c77b99fe269697c934b20255821dbe137fc94 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 12 Mar 2019 22:22:56 -0700 Subject: [PATCH 024/214] Adding deprecation warning and update CHANGELOG (#2535) --- Firebase/InstanceID/CHANGELOG.md | 147 +++++++++++++++++++++++ Firebase/Messaging/CHANGELOG.md | 4 + Firebase/Messaging/Public/FIRMessaging.h | 3 +- 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 Firebase/InstanceID/CHANGELOG.md diff --git a/Firebase/InstanceID/CHANGELOG.md b/Firebase/InstanceID/CHANGELOG.md new file mode 100644 index 00000000000..248cc813f0e --- /dev/null +++ b/Firebase/InstanceID/CHANGELOG.md @@ -0,0 +1,147 @@ +# 2019-03-19 -- v3.8.0 +- Adding community support for tvOS. (#2428) +- Adding Firebase info to checkin. (#2509) + +# 2019-03-05 -- v3.7.0 +- Open source Firebase InstanceID. (#186) + +# 2019-02-20 -- v3.5.0 +- Always update keychain access control when adding new keychain to ensure it won't be blocked when device is locked. (#1399) + +# 2019-01-22 -- v3.4.0 +- Move all keychain write operations off the main thread. (#1399) +- Make keychain operations asynchronous where possible (given the current APIs) +- Avoid redundant keychain operations when it's been queried and cached before. + +# 2018-10-25 -- v3.3.0 +- Fixed a crash caused by keychain operation when accessing default access group. (#1399, #1393) +- Remove internal APIs that are no longer used. + +# 2018-09-25 -- v3.2.2 +- Fixed a crash caused by NSUserDefaults being called on background thread. + +# 2018-08-14 -- v3.2.1 +- Fixed an issue that checkin is not cached properly when app first started. (#1561) + +# 2018-07-31 -- v3.2.0 +- Added support for global Firebase data collection flag. (#1219) +- Improved message tracking sent by server API. +- Fixed an issue that InstanceID doesn't compile in app extensions, allowing its +dependents like remote config to be working inside the app extensions. + +# 2018-06-19 -- v3.1.1 +- Ensure the checkin and tokens are refreshed if firebase project changed. +- Fixed an issue that checkin should be turned off when FCM's autoInitEnabled flag is off. + +# 2018-06-12 -- v3.1.0 +- Added a new API to fetch InstanceID and Token with a completion handler. The completion handler returns a FIRInstanceIDResult with a instanceID and a token properties. +- Deprecated the token method. +- Added support to log a new customized label provided by developer. + +# 2018-05-08 -- v3.0.0 +- Removed deprecated method `setAPNSToken:type` defined in FIRInstanceID, please use `setAPNSToken:type` defined in FIRMessaging instead. +- Removed deprecated enum `FIRInstanceIDAPNSTokenType` defined in FIRInstanceID, please use `FIRMessagingAPNSTokenType` defined in FIRMessaging instead. +- Fixed an issue that FCM scheduled messages were not tracked successfully. + +# 2018-03-06 -- v2.0.10 +- Improved documentation on InstanceID usage for GDPR. +- Improved the keypair handling during GCM to FCM migration. If you are migrating from GCM to FCM, we encourage you to update to this version and above. + +# 2018-02-06 -- v2.0.9 +- Improved support for language targeting for FCM service. Server updates happen more efficiently when language changes. +- Improved support for FCM token auto generation enable/disable functions. + +# 2017-12-11 -- v2.0.8 +- Fixed a crash caused by a reflection call during logging. +- Updating server with the latest parameters and deprecating old ones. + +# 2017-11-27 -- v2.0.7 +- Improve identity reset process, ensuring all information is reset during Identity deletion. + +# 2017-11-06 -- v2.0.6 +- Make token refresh weekly. +- Fixed a crash when performing token operation. + +# 2017-10-11 -- v2.0.5 +- Improved support for working in shared Keychain environments. + +# 2017-09-26 -- v2.0.4 +- Fixed an issue where the FCM token was not associating correctly with an APNs + device token, depending on when the APNs device token was made available. +- Fixed an issue where FCM tokens for different Sender IDs were not associating + correctly with an APNs device token. +- Fixed an issue that was preventing the FCM direct channel from being + established on the first start after 24 hours of being opened. + +# 2017-09-13 -- v2.0.3 +- Fixed a race condition where a token was not being generated on first start, + if Firebase Messaging was included and the app did not register for remote + notifications. + +# 2017-08-25 -- v2.0.2 +- Fixed a startup performance regression, removing a call which was blocking the + main thread. + +# 2017-08-07 -- v2.0.1 +- Fixed issues with token and app identifier being inaccessible when the device + is locked. +- Fixed a crash if bundle identifier is nil, which is possible in some testing + environments. +- Fixed a small memory leak fetching a new token. +- Moved to a new and simplified token storage system. +- Moved to a new queuing system for token fetches and deletes. +- Simplified logic and code around configuration and logging. +- Added clarification about the 'apns_sandbox' parameter, in header comments. + +# 2017-05-08 -- v2.0.0 +- Introduced an improved interface for Swift 3 developers +- Deprecated some methods and properties after moving their logic to the + Firebase Cloud Messaging SDK +- Fixed an intermittent stability issue when a debug build of an app was + replaced with a release build of the same version +- Removed swizzling logic that was sometimes resulting in developers receiving + a validation notice about enabling push notification capabilities, even though + they weren't using push notifications +- Fixed a notification that would sometimes fire twice in quick succession + during the first run of an app + +# 2017-03-31 -- v1.0.10 + +- Improvements to token-fetching logic +- Fixed some warnings in Instance ID +- Improved error messages if Instance ID couldn't be initialized properly +- Improvements to console logging + +# 2017-01-31 -- v1.0.9 + +- Removed an error being mistakenly logged to the console. + +# 2016-07-06 -- v1.0.8 + +- Don't store InstanceID plists in Documents folder. + +# 2016-06-19 -- v1.0.7 + +- Fix remote-notifications warning on app submission. + +# 2016-05-16 -- v1.0.6 + +- Fix CocoaPod linter issues for InstanceID pod. + +# 2016-05-13 -- v1.0.5 + +- Fix Authorization errors for InstanceID tokens. + +# 2016-05-11 -- v1.0.4 + +- Reduce wait for InstanceID token during parallel requests. + +# 2016-04-18 -- v1.0.3 + +- Change flag to disable swizzling to *FirebaseAppDelegateProxyEnabled*. +- Fix incessant Keychain errors while accessing InstanceID. +- Fix max retries for fetching IID token. + +# 2016-04-18 -- v1.0.2 + +- Register for remote notifications on iOS8+ in the SDK itself. diff --git a/Firebase/Messaging/CHANGELOG.md b/Firebase/Messaging/CHANGELOG.md index 4eea22d7eea..2ca86182df8 100644 --- a/Firebase/Messaging/CHANGELOG.md +++ b/Firebase/Messaging/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2019-03-19 -- v3.4.0 +- Adding image support for notification. Enable service extension and use FIRMessagingExtensionHelper API to display image on your notification. (#2491) +- Adding community support for tvOS. (#2428) + # 2019-03-05 -- v3.3.2 - Replaced `NSUserDefaults` with `GULUserDefaults` to avoid potential crashes. (#2443) diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h index a0c6c51fe38..10b84dd4d33 100644 --- a/Firebase/Messaging/Public/FIRMessaging.h +++ b/Firebase/Messaging/Public/FIRMessaging.h @@ -323,7 +323,8 @@ NS_SWIFT_NAME(Messaging) * If true, the data message sent by direct channel will be delivered via * `FIRMessagingDelegate messaging(_:didReceive:)` and across all iOS versions. */ -@property(nonatomic, assign) BOOL useMessagingDelegateForDirectChannel; +@property(nonatomic, assign) BOOL useMessagingDelegateForDirectChannel + __deprecated_msg("This is soon to be deprecated. All direct messages will by default delivered in `FIRMessagingDelegate messaging(_:didReceive:)` across all iOS versions"); /** * FIRMessaging From ee8d90a3d07b58a0b5e703a389c31c40594e6fe5 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Tue, 12 Mar 2019 22:24:30 -0700 Subject: [PATCH 025/214] Update Changelog for Auth v5.4.1 (#2538) --- Firebase/Auth/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md index aa69d070463..33ba983745a 100644 --- a/Firebase/Auth/CHANGELOG.md +++ b/Firebase/Auth/CHANGELOG.md @@ -1,3 +1,8 @@ +# v5.4.1 +- Deprecate Microsoft and Yahoo OAuth Provider ID (#2517) +- Fix an issue where an exception was thrown when linking OAuth credentials. (#2521) +- Fix an issue where a wrong error was thrown when upgrading anonymous user. (#2530) + # v5.4.0 - Add support of Generic IDP (#2405). From bf0a2cc421ed5a93239f5e84ad9085e95f67ef29 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 13 Mar 2019 06:46:32 -0700 Subject: [PATCH 026/214] M45 Core release notes (#2537) --- Firebase/Core/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index 775c7de2905..9330479f545 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,5 +1,10 @@ # Unreleased +# 2019-03-19 -- v5.4.0 -- M45 +- [changed] Allow Bundle IDs that have a valid prefix to enable richer extension support. (#2515) +- [changed] Convert FIRLogger to use the recommended os_log for logging via GoogleUtilities. + See https://developer.apple.com/documentation/os/logging?language=objc. + # 2019-01-22 -- v5.2.0 -- M41 - [changed] Added a registerInternalLibrary API. Now other Firebase libraries register with FirebaseCore instead of FirebaseCore needing all of its clients' versions built in. From 7cbd945affd6d8e3576a118f273f159230b11fdf Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Wed, 13 Mar 2019 10:15:24 -0400 Subject: [PATCH 027/214] Moving FIRAnalyticsConfiguration calls to Analytics. (#2516) * Moving FIRAnalyticsConfiguration calls to Analytics. These APIS are specific to the Analytics SDK and have been moved there. The instance methods have become static methods on the `FIRAnalytics` class. * Ignore more clang warnings. * Added more context in the changelog. * Updated CHANGELOG. --- Firebase/Core/CHANGELOG.md | 6 ++++-- Firebase/Core/FIRAnalyticsConfiguration.m | 3 +++ Firebase/Core/FIRApp.m | 4 ++++ Firebase/Core/FIRConfiguration.m | 3 +++ Firebase/Core/Public/FIRAnalyticsConfiguration.h | 1 + Firebase/Core/Public/FIRConfiguration.h | 4 +++- 6 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index 9330479f545..250fd0fd67f 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,9 +1,11 @@ -# Unreleased - # 2019-03-19 -- v5.4.0 -- M45 - [changed] Allow Bundle IDs that have a valid prefix to enable richer extension support. (#2515) - [changed] Convert FIRLogger to use the recommended os_log for logging via GoogleUtilities. See https://developer.apple.com/documentation/os/logging?language=objc. +- [changed] Deprecated `FIRAnalyticsConfiguration` API in favor of new methods on the Analytics SDK. + Please call the new APIs directly: Enable/disable Analytics with `Analytics.setAnalyticsEnabled(_)` + and modify the session timeout interval with `Analytics.setAnalyticsCollectionEnabled(_)`. + # 2019-01-22 -- v5.2.0 -- M41 - [changed] Added a registerInternalLibrary API. Now other Firebase libraries register with FirebaseCore diff --git a/Firebase/Core/FIRAnalyticsConfiguration.m b/Firebase/Core/FIRAnalyticsConfiguration.m index 33aa1687f52..e584839efca 100644 --- a/Firebase/Core/FIRAnalyticsConfiguration.m +++ b/Firebase/Core/FIRAnalyticsConfiguration.m @@ -16,7 +16,10 @@ #import "Private/FIRAnalyticsConfiguration+Internal.h" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation FIRAnalyticsConfiguration +#pragma clang diagnostic pop + (FIRAnalyticsConfiguration *)sharedInstance { static FIRAnalyticsConfiguration *sharedInstance = nil; diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index 300619bbb9f..6d82ab0b340 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -320,6 +320,7 @@ - (BOOL)configureCore { if ([firAnalyticsClass respondsToSelector:startWithConfigurationSelector]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [firAnalyticsClass performSelector:startWithConfigurationSelector withObject:[FIRConfiguration sharedInstance].analyticsConfiguration withObject:_options]; @@ -360,9 +361,12 @@ - (void)setDataCollectionDefaultEnabled:(BOOL)dataCollectionDefaultEnabled { } // The Analytics flag has not been explicitly set, so update with the value being set. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [[FIRAnalyticsConfiguration sharedInstance] setAnalyticsCollectionEnabled:dataCollectionDefaultEnabled persistSetting:NO]; +#pragma clang diagnostic pop } - (BOOL)isDataCollectionDefaultEnabled { diff --git a/Firebase/Core/FIRConfiguration.m b/Firebase/Core/FIRConfiguration.m index cd6486257ef..f8378d77790 100644 --- a/Firebase/Core/FIRConfiguration.m +++ b/Firebase/Core/FIRConfiguration.m @@ -30,7 +30,10 @@ + (instancetype)sharedInstance { - (instancetype)init { self = [super init]; if (self) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" _analyticsConfiguration = [FIRAnalyticsConfiguration sharedInstance]; +#pragma clang diagnostic pop } return self; } diff --git a/Firebase/Core/Public/FIRAnalyticsConfiguration.h b/Firebase/Core/Public/FIRAnalyticsConfiguration.h index 7f62c1450a3..add4a38ee7b 100644 --- a/Firebase/Core/Public/FIRAnalyticsConfiguration.h +++ b/Firebase/Core/Public/FIRAnalyticsConfiguration.h @@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN * This class provides configuration fields for Firebase Analytics. */ NS_SWIFT_NAME(AnalyticsConfiguration) +DEPRECATED_MSG_ATTRIBUTE("Use these methods directly on the `Analytics` class.") @interface FIRAnalyticsConfiguration : NSObject /** diff --git a/Firebase/Core/Public/FIRConfiguration.h b/Firebase/Core/Public/FIRConfiguration.h index 95bba5e7b39..b88fcaf9f8d 100644 --- a/Firebase/Core/Public/FIRConfiguration.h +++ b/Firebase/Core/Public/FIRConfiguration.h @@ -32,7 +32,9 @@ NS_SWIFT_NAME(FirebaseConfiguration) @property(class, nonatomic, readonly) FIRConfiguration *sharedInstance NS_SWIFT_NAME(shared); /** The configuration class for Firebase Analytics. */ -@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration; +@property(nonatomic, readwrite) + FIRAnalyticsConfiguration *analyticsConfiguration DEPRECATED_MSG_ATTRIBUTE( + "Use the methods available here directly on the `Analytics` class."); /** * Sets the logging level for internal Firebase logging. Firebase will only log messages From 45d2fb2316ec0662821fb53c34cb962787251f99 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Wed, 13 Mar 2019 07:38:13 -0700 Subject: [PATCH 028/214] extract errorMessage directly from response when server returns 200 (#2542) * extract errorMessage directly from response in case of returnIDPCredential=YES Fix #2522. In this case, the server returns an 200 without an error like the following: ``` [response data:] { "error": { "code": 400, "message": "FEDERATED_USER_ID_ALREADY_LINKED", "errors": [ { "message": "FEDERATED_USER_ID_ALREADY_LINKED", "domain": "global", "reason": "invalid" } ] } } ``` Rather, it is directly enclosed in the response with an errorMessage without a structure: ``` [response data:] { key: value ... "errorMessage": "FEDERATED_USER_ID_ALREADY_LINKED", "kind": "identitytoolkit#VerifyAssertionResponse" } ``` * Update Changelog --- Firebase/Auth/CHANGELOG.md | 3 ++- Firebase/Auth/Source/RPCs/FIRAuthBackend.m | 27 ++++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md index 33ba983745a..8c1f3032e3e 100644 --- a/Firebase/Auth/CHANGELOG.md +++ b/Firebase/Auth/CHANGELOG.md @@ -1,7 +1,8 @@ # v5.4.1 - Deprecate Microsoft and Yahoo OAuth Provider ID (#2517) - Fix an issue where an exception was thrown when linking OAuth credentials. (#2521) -- Fix an issue where a wrong error was thrown when upgrading anonymous user. (#2530) +- Fix an issue where a wrong error was thrown when handling error with + FEDERATED_USER_ID_ALREADY_LINKED. (#2530, #2542) # v5.4.0 - Add support of Generic IDP (#2405). diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m index d4567daaec7..188cc9fc04f 100644 --- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m +++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m @@ -128,6 +128,12 @@ */ static NSString *const kErrorMessageKey = @"message"; +/** @var kReturnIDPCredentialErrorMessageKey + @brief The key for "errorMessage" value in JSON responses from the server, In case + returnIDPCredential of a verifyAssertion request is set to @YES. + */ +static NSString *const kReturnIDPCredentialErrorMessageKey = @"errorMessage"; + /** @var kUserNotFoundErrorMessage @brief This is the error message returned when the user is not found, which means the user account has been deleted given the token was once valid. @@ -925,18 +931,15 @@ - (void)postWithRequest:(id)request if ([request isKindOfClass:[FIRVerifyAssertionRequest class]]) { FIRVerifyAssertionRequest *verifyAssertionRequest = (FIRVerifyAssertionRequest *)request; if (verifyAssertionRequest.returnIDPCredential) { - NSDictionary *errorDictionary = dictionary[kErrorKey]; - if ([errorDictionary isKindOfClass:[NSDictionary class]]) { - id errorMessage = errorDictionary[kErrorMessageKey]; - if ([errorMessage isKindOfClass:[NSString class]]) { - NSString *errorString = (NSString *)errorMessage; - NSError *clientError = [[self class] clientErrorWithServerErrorMessage:errorString - errorDictionary:errorDictionary - response:response]; - if (clientError) { - callback(clientError); - return; - } + NSString *errorMessage = dictionary[kReturnIDPCredentialErrorMessageKey]; + if ([errorMessage isKindOfClass:[NSString class]]) { + NSString *errorString = (NSString *)errorMessage; + NSError *clientError = [[self class] clientErrorWithServerErrorMessage:errorString + errorDictionary:@{} + response:response]; + if (clientError) { + callback(clientError); + return; } } } From 9f62410f72499a6cf7f11d12e85457988325d497 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Wed, 13 Mar 2019 11:21:53 -0400 Subject: [PATCH 029/214] Implement PatchingDeletedDocumentsDoesNothing test (#2528) ...and add placeholders for numeric transform tests --- .../firebase/firestore/model/mutation_test.cc | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Firestore/core/test/firebase/firestore/model/mutation_test.cc b/Firestore/core/test/firebase/firestore/model/mutation_test.cc index 92cce3d29f0..b688511d134 100644 --- a/Firestore/core/test/firebase/firestore/model/mutation_test.cc +++ b/Firestore/core/test/firebase/firestore/model/mutation_test.cc @@ -148,13 +148,43 @@ TEST(Mutation, PatchesPrimitiveValue) { } TEST(Mutation, PatchingDeletedDocumentsDoesNothing) { - // TODO(rsgowman) + auto base_doc = + std::make_shared(testutil::DeletedDoc("collection/key", 0)); + std::unique_ptr patch = + PatchMutation("collection/key", {{"foo", FieldValue::FromString("bar")}}); + MaybeDocumentPtr patched_doc = + patch->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); + EXPECT_EQ(base_doc, patched_doc); } TEST(Mutation, AppliesLocalServerTimestampTransformsToDocuments) { // TODO(rsgowman) } +TEST(Mutation, AppliesIncrementTransformToDocument) { + // TODO(rsgowman) +} + +TEST(Mutation, AppliesIncrementTransformToUnexpectedType) { + // TODO(rsgowman) +} + +TEST(Mutation, AppliesIncrementTransformToMissingField) { + // TODO(rsgowman) +} + +TEST(Mutation, AppliesIncrementTransformsConsecutively) { + // TODO(rsgowman) +} + +TEST(Mutation, AppliesIncrementWithoutOverflow) { + // TODO(rsgowman) +} + +TEST(Mutation, AppliesIncrementWithoutUnderflow) { + // TODO(rsgowman) +} + TEST(Mutation, CreatesArrayUnionTransform) { // TODO(rsgowman) } From 37842faf29b5ab1aa04702991eb696e63d0c200f Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 13 Mar 2019 08:54:03 -0700 Subject: [PATCH 030/214] GoogleUtilities 5.4.0 (#2519) --- GoogleUtilities.podspec | 2 +- GoogleUtilities/CHANGELOG.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/GoogleUtilities.podspec b/GoogleUtilities.podspec index b88ba1b7273..7a61dc4db9a 100644 --- a/GoogleUtilities.podspec +++ b/GoogleUtilities.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleUtilities' - s.version = '5.3.7' + s.version = '5.4.0' s.summary = 'Google Utilities for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/GoogleUtilities/CHANGELOG.md b/GoogleUtilities/CHANGELOG.md index 564ac5a12cc..1286f236d80 100644 --- a/GoogleUtilities/CHANGELOG.md +++ b/GoogleUtilities/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +# 5.4.0 +- Update GULLogger to use os_log instead of asl_log on iOS 9 and later. (#2374, #2504) + # 5.3.7 - Fixed `pod lib lint GoogleUtilities.podspec --use-libraries` regression. (#2130) - Fixed macOS conditional check in UserDefaults. (#2245) From 8061301cb96997ed5a326dc922494e5563328813 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 13 Mar 2019 08:57:47 -0700 Subject: [PATCH 031/214] File directory for tvOS should change from NSApplicationSupportDirector to NSCachesDirectory (#2496) --- Example/Firebase.xcodeproj/project.pbxproj | 23 +++-------- .../FIRInstanceIDBackupExcludedPlistTest.m | 28 ++++++++----- .../Tests/FIRInstanceIDCheckinStoreTest.m | 17 ++++---- .../Tests/FIRInstanceIDKeyPairStoreTest.m | 5 +++ .../InstanceID/Tests/FIRInstanceIDStoreTest.m | 9 ++-- .../Tests/FIRInstanceIDTokenManagerTest.m | 9 ++-- .../Tests/FIRMessagingExtensionHelperTest.m | 10 +++-- Firebase/InstanceID/FIRInstanceID.m | 5 +-- .../FIRInstanceIDBackupExcludedPlist.h | 12 +----- .../FIRInstanceIDBackupExcludedPlist.m | 41 +++++++++++-------- .../InstanceID/FIRInstanceIDCheckinStore.h | 4 +- .../InstanceID/FIRInstanceIDCheckinStore.m | 4 +- Firebase/InstanceID/FIRInstanceIDConstants.h | 4 +- Firebase/InstanceID/FIRInstanceIDConstants.m | 2 +- .../InstanceID/FIRInstanceIDKeyPairStore.m | 6 +-- Firebase/InstanceID/FIRInstanceIDStore.h | 10 ++--- Firebase/InstanceID/FIRInstanceIDStore.m | 34 +++++++++------ Firebase/Messaging/FIRMessaging.m | 21 +++++----- Firebase/Messaging/FIRMessagingConstants.h | 2 +- Firebase/Messaging/FIRMessagingConstants.m | 2 +- .../FIRMessagingRmq2PersistentStore.m | 16 +++++--- Firebase/Messaging/FIRMessagingUtilities.h | 2 + Firebase/Messaging/FIRMessagingUtilities.m | 8 ++++ 23 files changed, 147 insertions(+), 127 deletions(-) diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index ce9f674d0db..47a10da20fb 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -124,9 +124,6 @@ 511DD27D2225C4D20094D78D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 511DD27C2225C4D20094D78D /* Assets.xcassets */; }; 511DD2802225C4D20094D78D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 511DD27F2225C4D20094D78D /* main.m */; }; 511DD2922225C8C40094D78D /* FIRInstanceIDWithFCMTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8DB550221F5B470068BB0E /* FIRInstanceIDWithFCMTest.m */; }; - 511DD2932225C8C40094D78D /* FIRMessagingFakeConnection.h in Sources */ = {isa = PBXBuildFile; fileRef = DE9315C81E8738B70083EDBF /* FIRMessagingFakeConnection.h */; }; - 511DD2942225C8C40094D78D /* FIRMessagingFakeSocket.h in Sources */ = {isa = PBXBuildFile; fileRef = DE9315CA1E8738B70083EDBF /* FIRMessagingFakeSocket.h */; }; - 511DD2952225C8C40094D78D /* FIRMessagingTestNotificationUtilities.h in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D61E8738B70083EDBF /* FIRMessagingTestNotificationUtilities.h */; }; 511DD2962225C8C40094D78D /* FIRMessagingClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315C31E8738B70083EDBF /* FIRMessagingClientTest.m */; }; 511DD2972225C8C40094D78D /* FIRMessagingCodedInputStreamTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315C41E8738B70083EDBF /* FIRMessagingCodedInputStreamTest.m */; }; 511DD2982225C8C40094D78D /* FIRMessagingConnectionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315C51E8738B70083EDBF /* FIRMessagingConnectionTest.m */; }; @@ -147,8 +144,8 @@ 511DD2A72225C8C40094D78D /* FIRMessagingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D51E8738B70083EDBF /* FIRMessagingTest.m */; }; 511DD2A82225C8C40094D78D /* FIRMessagingTestNotificationUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D71E8738B70083EDBF /* FIRMessagingTestNotificationUtilities.m */; }; 511DD2A92225C8C40094D78D /* FIRMessagingAnalyticsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE37C63A2163D5F30025D03E /* FIRMessagingAnalyticsTest.m */; }; - 511DD2AA2225C8D50094D78D /* FIRMessagingTestUtilities.h in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242A21EA364600BB24C6 /* FIRMessagingTestUtilities.h */; }; 511DD2AB2225C8D50094D78D /* FIRMessagingTestUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242B21EA364600BB24C6 /* FIRMessagingTestUtilities.m */; }; + 51559F8A2238B8DB00CFC32C /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */; }; 518854D92230652900CA4141 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 518854D82230652900CA4141 /* AppDelegate.m */; }; 518854DC2230652900CA4141 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 518854DB2230652900CA4141 /* ViewController.m */; }; 518854DF2230652900CA4141 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 518854DD2230652900CA4141 /* Main.storyboard */; }; @@ -177,7 +174,6 @@ 5188550A223067E900CA4141 /* FIRInstanceIDUtilitiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE958BE121F7DF0C00E6C1C5 /* FIRInstanceIDUtilitiesTest.m */; }; 5188550C2230873000CA4141 /* FIRInstanceIDAPNSInfoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE958BDA21F7DF0B00E6C1C5 /* FIRInstanceIDAPNSInfoTest.m */; }; 5188550E2231E02400CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */; }; - 518855112231E09300CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 518855102231E09300CA4141 /* FIRMessagingExtensionHelperTest.m */; }; 7E21E0731F857DFC00D0AC1C /* FIROAuthProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E21E0721F857DFC00D0AC1C /* FIROAuthProviderTests.m */; }; 7E9485421F578AC4005A3939 /* FIRAuthURLPresenterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */; }; 7EE21F7A1FE89193009B1370 /* FIREmailLinkRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */; }; @@ -1080,9 +1076,7 @@ 518854E22230652B00CA4141 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 518854E32230652B00CA4141 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 518854EC223066BE00CA4141 /* InstanceID_Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InstanceID_Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingExtensionHelperTest.m; path = Messaging/Tests/FIRMessagingExtensionHelperTest.m; sourceTree = ""; }; - 5188550F2231E04C00CA4141 /* FIRMessagingClientTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingClientTest.m; path = Messaging/Tests/FIRMessagingClientTest.m; sourceTree = ""; }; - 518855102231E09300CA4141 /* FIRMessagingExtensionHelperTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingExtensionHelperTest.m; path = Messaging/Tests/FIRMessagingExtensionHelperTest.m; sourceTree = ""; }; + 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRMessagingExtensionHelperTest.m; sourceTree = ""; }; 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -1919,9 +1913,6 @@ 6003F581195388D10070C39A = { isa = PBXGroup; children = ( - 518855102231E09300CA4141 /* FIRMessagingExtensionHelperTest.m */, - 5188550F2231E04C00CA4141 /* FIRMessagingClientTest.m */, - 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */, 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, DE9314EB1E86C6FF0083EDBF /* Auth */, DEE14D661E844677006FA992 /* Core */, @@ -2547,6 +2538,7 @@ DE9315C21E8738B70083EDBF /* Tests */ = { isa = PBXGroup; children = ( + 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */, DE8DB550221F5B470068BB0E /* FIRInstanceIDWithFCMTest.m */, DE9315C81E8738B70083EDBF /* FIRMessagingFakeConnection.h */, DE9315CA1E8738B70083EDBF /* FIRMessagingFakeSocket.h */, @@ -3798,6 +3790,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, "es-MX", @@ -4305,14 +4298,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 518855112231E09300CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */, - 511DD2AA2225C8D50094D78D /* FIRMessagingTestUtilities.h in Sources */, + 511DD2962225C8C40094D78D /* FIRMessagingClientTest.m in Sources */, + 51559F8A2238B8DB00CFC32C /* FIRMessagingExtensionHelperTest.m in Sources */, 511DD2AB2225C8D50094D78D /* FIRMessagingTestUtilities.m in Sources */, 511DD2922225C8C40094D78D /* FIRInstanceIDWithFCMTest.m in Sources */, - 511DD2932225C8C40094D78D /* FIRMessagingFakeConnection.h in Sources */, - 511DD2942225C8C40094D78D /* FIRMessagingFakeSocket.h in Sources */, - 511DD2952225C8C40094D78D /* FIRMessagingTestNotificationUtilities.h in Sources */, - 511DD2962225C8C40094D78D /* FIRMessagingClientTest.m in Sources */, 511DD2972225C8C40094D78D /* FIRMessagingCodedInputStreamTest.m in Sources */, 511DD2982225C8C40094D78D /* FIRMessagingConnectionTest.m in Sources */, 511DD2992225C8C40094D78D /* FIRMessagingContextManagerServiceTest.m in Sources */, diff --git a/Example/InstanceID/Tests/FIRInstanceIDBackupExcludedPlistTest.m b/Example/InstanceID/Tests/FIRInstanceIDBackupExcludedPlistTest.m index c42625070ce..b6321ce940c 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDBackupExcludedPlistTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDBackupExcludedPlistTest.m @@ -38,16 +38,15 @@ @implementation FIRInstanceIDBackupExcludedPlistTest - (void)setUp { [super setUp]; - [FIRInstanceIDStore createApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + [FIRInstanceIDStore createSubDirectory:kApplicationSupportSubDirectoryName]; self.plist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kTestPlistFileName - applicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + initWithFileName:kTestPlistFileName + subDirectory:kApplicationSupportSubDirectoryName]; } - (void)tearDown { [self.plist deleteFile:nil]; - [FIRInstanceIDStore removeApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName - error:nil]; + [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; [super tearDown]; } @@ -84,7 +83,7 @@ - (void)testWriteToPlistInApplicationSupportFolder { - (void)testMovePlistToApplicationSupportDirectorySuccess { NSDictionary *plistContents = @{@"hello" : @"world", @"id" : @123}; [self.plist writeDictionary:plistContents error:nil]; - [self.plist moveToApplicationSupportSubDirectory]; + [self.plist moveToApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; XCTAssertTrue([self isPlistInApplicationSupportDirectory]); XCTAssertFalse([self isPlistInDocumentsDirectory]); @@ -94,16 +93,18 @@ - (void)testMovePlistToApplicationSupportDirectorySuccess { } - (void)testMovePlistToApplicationSupportDirectoryFailure { + // This is to test moving data from deprecated document folder to application folder + // which should only apply to iOS. +#if TARGET_OS_IOS // Delete the subdirectory - [FIRInstanceIDStore removeApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName - error:nil]; + [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; // Create a new plistl This would try to move or write to the ApplicationSupport directory // but since the subdirectory is not there anymore it will fail and rather write to the // Documents folder. self.plist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kTestPlistFileName - applicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + initWithFileName:kTestPlistFileName + subDirectory:kApplicationSupportSubDirectoryName]; NSDictionary *plistContents = @{@"hello" : @"world", @"id" : @123}; [self.plist writeDictionary:plistContents error:nil]; @@ -113,18 +114,25 @@ - (void)testMovePlistToApplicationSupportDirectoryFailure { NSDictionary *newPlistContents = @{@"world" : @"hello"}; [self.plist writeDictionary:newPlistContents error:nil]; + XCTAssertEqualObjects(newPlistContents, [self.plist contentAsDictionary]); // The new file should still be written to the Documents folder. XCTAssertFalse([self isPlistInApplicationSupportDirectory]); XCTAssertTrue([self isPlistInDocumentsDirectory]); +#endif } #pragma mark - Private Helpers - (BOOL)isPlistInApplicationSupportDirectory { +#if TARGET_OS_TV + NSArray *directoryPaths = + NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); +#else NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); +#endif NSString *applicationSupportDirPath = directoryPaths.lastObject; NSArray *components = @[ applicationSupportDirPath, kApplicationSupportSubDirectoryName, diff --git a/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m b/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m index b052299a741..a1b903c4a6e 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m @@ -56,7 +56,7 @@ @implementation FIRInstanceIDCheckinStoreTest - (void)setUp { [super setUp]; - [FIRInstanceIDStore createApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + [FIRInstanceIDStore createSubDirectory:kApplicationSupportSubDirectoryName]; } - (void)tearDown { @@ -65,8 +65,7 @@ - (void)tearDown { NSError *error; [[NSFileManager defaultManager] removeItemAtPath:path error:&error]; } - [FIRInstanceIDStore removeApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName - error:nil]; + [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; [super tearDown]; } @@ -77,8 +76,8 @@ - (void)testInvalidCheckinPreferencesOnKeychainFail { XCTestExpectation *checkinInvalidExpectation = [self expectationWithDescription:@"Checkin preference should be invalid after keychain failure"]; FIRInstanceIDBackupExcludedPlist *checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kFakeCheckinPlistName - applicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + initWithFileName:kFakeCheckinPlistName + subDirectory:kApplicationSupportSubDirectoryName]; FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; @@ -110,8 +109,8 @@ - (void)testCheckinSaveFailsOnKeychainWriteFailure { XCTestExpectation *checkinSaveFailsExpectation = [self expectationWithDescription:@"Checkin save should fail after keychain write failure"]; FIRInstanceIDBackupExcludedPlist *checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kFakeCheckinPlistName - applicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + initWithFileName:kFakeCheckinPlistName + subDirectory:kApplicationSupportSubDirectoryName]; FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; fakeKeychain.cannotWriteToKeychain = YES; @@ -141,8 +140,8 @@ - (void)testCheckinMigrationMovesToNewLocationInKeychain { [self expectationWithDescription:@"checkin migration should move to the new location"]; // Create checkin store class. FIRInstanceIDBackupExcludedPlist *checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kFakeCheckinPlistName - applicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + initWithFileName:kFakeCheckinPlistName + subDirectory:kApplicationSupportSubDirectoryName]; FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; FIRInstanceIDFakeKeychain *weakKeychain = fakeKeychain; diff --git a/Example/InstanceID/Tests/FIRInstanceIDKeyPairStoreTest.m b/Example/InstanceID/Tests/FIRInstanceIDKeyPairStoreTest.m index a8d98d549c2..ad5f6913875 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDKeyPairStoreTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDKeyPairStoreTest.m @@ -20,6 +20,7 @@ #import "Firebase/InstanceID/FIRInstanceIDConstants.h" #import "Firebase/InstanceID/FIRInstanceIDKeyPair.h" #import "Firebase/InstanceID/FIRInstanceIDKeychain.h" +#import "Firebase/InstanceID/FIRInstanceIDStore.h" #import #import "Firebase/InstanceID/FIRInstanceIDKeyPair.h" @@ -53,6 +54,10 @@ - (void)setUp { [super setUp]; id mockStoreClass = OCMClassMock([FIRInstanceIDKeyPairStore class]); [[[mockStoreClass stub] andReturn:@"com.google.iid-keypairmanager-test"] keyStoreFileName]; + // Should make sure the standard directory is created. + if (![FIRInstanceIDStore hasSubDirectory:kFIRInstanceIDSubDirectoryName]) { + [FIRInstanceIDStore createSubDirectory:kFIRInstanceIDSubDirectoryName]; + } _keyPairStore = [[FIRInstanceIDKeyPairStore alloc] init]; } diff --git a/Example/InstanceID/Tests/FIRInstanceIDStoreTest.m b/Example/InstanceID/Tests/FIRInstanceIDStoreTest.m index d414c48258b..ba197641819 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDStoreTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDStoreTest.m @@ -74,12 +74,12 @@ @implementation FIRInstanceIDStoreTest - (void)setUp { [super setUp]; - [FIRInstanceIDStore createApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + [FIRInstanceIDStore createSubDirectory:kApplicationSupportSubDirectoryName]; NSString *checkinPlistName = @"com.google.test.IIDStoreTestCheckin"; self.checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:checkinPlistName - applicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + initWithFileName:checkinPlistName + subDirectory:kApplicationSupportSubDirectoryName]; // checkin store FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; @@ -103,8 +103,7 @@ - (void)setUp { - (void)tearDown { [self.instanceIDStore removeAllCachedTokensWithHandler:nil]; [self.instanceIDStore removeCheckinPreferencesWithHandler:nil]; - [FIRInstanceIDStore removeApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName - error:nil]; + [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; [_mockCheckinStore stopMocking]; [_mockTokenStore stopMocking]; [_mockInstanceIDStore stopMocking]; diff --git a/Example/InstanceID/Tests/FIRInstanceIDTokenManagerTest.m b/Example/InstanceID/Tests/FIRInstanceIDTokenManagerTest.m index 8f52a83ab76..e3bdc8e18f7 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDTokenManagerTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDTokenManagerTest.m @@ -91,12 +91,12 @@ @implementation FIRInstanceIDTokenManagerTest - (void)setUp { [super setUp]; - [FIRInstanceIDStore createApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + [FIRInstanceIDStore createSubDirectory:kApplicationSupportSubDirectoryName]; NSString *checkinPlistFilename = @"com.google.test.IIDCheckinTest"; self.checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:checkinPlistFilename - applicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; + initWithFileName:checkinPlistFilename + subDirectory:kApplicationSupportSubDirectoryName]; // checkin store FIRInstanceIDFakeKeychain *fakeCheckinKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; @@ -123,8 +123,7 @@ - (void)tearDown { } self.tokenManager = nil; - [FIRInstanceIDStore removeApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName - error:nil]; + [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; [super tearDown]; } diff --git a/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m index a58efcd0810..e2275660bb6 100644 --- a/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m +++ b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m @@ -47,7 +47,7 @@ @implementation FIRMessagingExtensionHelperTest - (void)setUp { [super setUp]; - FIRMessagingExtensionHelper *extensionHelper = [FIRMessaging extensionHelper]; + FIRMessagingExtensionHelper *extensionHelper = [[FIRMessagingExtensionHelper alloc] init]; _mockExtensionHelper = OCMPartialMock(extensionHelper); } @@ -55,8 +55,8 @@ - (void)tearDown { [_mockExtensionHelper stopMocking]; } -#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - (void)testModifyNotificationWithValidPayloadData { +#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 XCTestExpectation *validPayloadExpectation = [self expectationWithDescription:@"Test payload is valid."]; @@ -70,9 +70,11 @@ - (void)testModifyNotificationWithValidPayloadData { OCMVerify([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] completionHandler:[OCMArg any]]); [self waitForExpectationsWithTimeout:1.0 handler:nil]; +#endif } - (void)testModifyNotificationWithInvalidPayloadData { +#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 XCTestExpectation *validPayloadExpectation = [self expectationWithDescription:@"Test payload is valid."]; @@ -87,9 +89,11 @@ - (void)testModifyNotificationWithInvalidPayloadData { OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] completionHandler:[OCMArg any]]); [self waitForExpectationsWithTimeout:1.0 handler:nil]; +#endif } - (void)testModifyNotificationWithEmptyPayloadData { +#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 XCTestExpectation *validPayloadExpectation = [self expectationWithDescription:@"Test payload is valid."]; @@ -104,7 +108,7 @@ - (void)testModifyNotificationWithEmptyPayloadData { OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] completionHandler:[OCMArg any]]); [self waitForExpectationsWithTimeout:1.0 handler:nil]; -} #endif +} @end diff --git a/Firebase/InstanceID/FIRInstanceID.m b/Firebase/InstanceID/FIRInstanceID.m index 20919b65cff..795b70b312d 100644 --- a/Firebase/InstanceID/FIRInstanceID.m +++ b/Firebase/InstanceID/FIRInstanceID.m @@ -789,9 +789,8 @@ - (BOOL)isFCMAutoInitEnabled { // Actually makes InstanceID instantiate both the IID and Token-related subsystems. - (void)start { - NSString *instanceIDSubDirectory = kFIRInstanceIDApplicationSupportSubDirectory; - if (![FIRInstanceIDStore hasApplicationSupportSubDirectory:instanceIDSubDirectory]) { - [FIRInstanceIDStore createApplicationSupportSubDirectory:instanceIDSubDirectory]; + if (![FIRInstanceIDStore hasSubDirectory:kFIRInstanceIDSubDirectoryName]) { + [FIRInstanceIDStore createSubDirectory:kFIRInstanceIDSubDirectoryName]; } [self setupTokenManager]; diff --git a/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h b/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h index 810134db8ca..bccaced8903 100644 --- a/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h +++ b/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h @@ -35,12 +35,11 @@ * Init a backup excluded plist file. * * @param fileName The filename for the plist file. - * @param applicationSupportSubDirectory The subdirectory in Application Support to save the plist. + * @param subDirectory The subdirectory in Application Support to save the plist. * * @return Helper which allows to read write data to a backup excluded plist. */ -- (instancetype)initWithFileName:(NSString *)fileName - applicationSupportSubDirectory:(NSString *)applicationSupportSubDirectory; +- (instancetype)initWithFileName:(NSString *)fileName subDirectory:(NSString *)subDirectory; /** * Write dictionary data to the backup excluded plist file. If the file does not exist @@ -79,11 +78,4 @@ */ - (BOOL)doesFileExist; -/** - * Move the plist to Application Support subdirectory. If the plist is already in the correct - * subdirectory this will be a no-op. If the plist was never written to before this will remember - * to write this plist to the Application Support subfolder if it's written to in future. - */ -- (void)moveToApplicationSupportSubDirectory; - @end diff --git a/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m b/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m index 487e3f23501..866c8a86082 100644 --- a/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m +++ b/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m @@ -28,8 +28,8 @@ @interface FIRInstanceIDBackupExcludedPlist () @property(nonatomic, readwrite, copy) NSString *fileName; -@property(nonatomic, readwrite, copy) NSString *applicationSupportSubDirectory; -@property(nonatomic, readwrite, assign) BOOL fileInApplicationSupport; +@property(nonatomic, readwrite, copy) NSString *subDirectoryName; +@property(nonatomic, readwrite, assign) BOOL fileInStandardDirectory; @property(nonatomic, readwrite, strong) NSDictionary *cachedPlistContents; @@ -37,14 +37,18 @@ @interface FIRInstanceIDBackupExcludedPlist () @implementation FIRInstanceIDBackupExcludedPlist -- (instancetype)initWithFileName:(NSString *)fileName - applicationSupportSubDirectory:(NSString *)applicationSupportSubDirectory { +- (instancetype)initWithFileName:(NSString *)fileName subDirectory:(NSString *)subDirectory { self = [super init]; if (self) { _fileName = [fileName copy]; - _applicationSupportSubDirectory = [applicationSupportSubDirectory copy]; - _fileInApplicationSupport = - [self moveToApplicationSupportSubDirectory:applicationSupportSubDirectory]; + _subDirectoryName = [subDirectory copy]; +#if TARGET_OS_IOS + _fileInStandardDirectory = [self moveToApplicationSupportSubDirectory:subDirectory]; +#else + // For tvOS and macOS, we never store the content in document folder, so + // the migration is unnecessary. + _fileInStandardDirectory = YES; +#endif } return self; } @@ -104,14 +108,9 @@ - (NSDictionary *)contentAsDictionary { return self.cachedPlistContents; } -- (void)moveToApplicationSupportSubDirectory { - self.fileInApplicationSupport = - [self moveToApplicationSupportSubDirectory:self.applicationSupportSubDirectory]; -} - - (BOOL)moveToApplicationSupportSubDirectory:(NSString *)subDirectoryName { NSArray *directoryPaths = - NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES); NSString *applicationSupportDirPath = directoryPaths.lastObject; NSArray *components = @[ applicationSupportDirPath, subDirectoryName ]; NSString *subDirectoryPath = [NSString pathWithComponents:components]; @@ -151,7 +150,7 @@ - (BOOL)doesFileExist { #pragma mark - Private - (FIRInstanceIDPlistDirectory)plistDirectory { - if (self.fileInApplicationSupport) { + if (_fileInStandardDirectory) { return FIRInstanceIDPlistDirectoryApplicationSupport; } else { return FIRInstanceIDPlistDirectoryDocuments; @@ -176,10 +175,8 @@ - (NSString *)pathWithName:(NSString *)plistName case FIRInstanceIDPlistDirectoryApplicationSupport: directoryPaths = - NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - components = @[ - directoryPaths.lastObject, self.applicationSupportSubDirectory, plistNameWithExtension - ]; + NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES); + components = @[ directoryPaths.lastObject, _subDirectoryName, plistNameWithExtension ]; break; default: @@ -197,4 +194,12 @@ - (BOOL)doesFileExistInDirectory:(FIRInstanceIDPlistDirectory)directory { return [[NSFileManager defaultManager] fileExistsAtPath:path]; } +- (NSSearchPathDirectory)supportedDirectory { +#if TARGET_OS_TV + return NSCachesDirectory; +#else + return NSApplicationSupportDirectory; +#endif +} + @end diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinStore.h b/Firebase/InstanceID/FIRInstanceIDCheckinStore.h index 2d223834d55..5e1b1194740 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinStore.h +++ b/Firebase/InstanceID/FIRInstanceIDCheckinStore.h @@ -37,13 +37,13 @@ extern NSString *const kFIRInstanceIDLegacyCheckinKeychainService; * @param checkinFilename The backup excluded plist filename to persist checkin * preferences. * - * @param applicationSupportSubDirectory Sub-directory in Application support where we write + * @param subDirectoryName Sub-directory in standard directory where we write * InstanceID plist. * * @return Store to persist checkin preferences. */ - (instancetype)initWithCheckinPlistFileName:(NSString *)checkinFilename - applicationSupportSubDirectory:(NSString *)applicationSupportSubDirectory; + subDirectoryName:(NSString *)subDirectoryName; /** * Initialize a checkin store with the given backup excluded plist and keychain. diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinStore.m b/Firebase/InstanceID/FIRInstanceIDCheckinStore.m index aafe3fc9516..96f80712625 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinStore.m +++ b/Firebase/InstanceID/FIRInstanceIDCheckinStore.m @@ -51,10 +51,10 @@ @interface FIRInstanceIDCheckinStore () @implementation FIRInstanceIDCheckinStore - (instancetype)initWithCheckinPlistFileName:(NSString *)checkinFilename - applicationSupportSubDirectory:(NSString *)applicationSupportSubDirectory { + subDirectoryName:(NSString *)subDirectoryName { FIRInstanceIDBackupExcludedPlist *plist = [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:checkinFilename - applicationSupportSubDirectory:applicationSupportSubDirectory]; + subDirectory:subDirectoryName]; FIRInstanceIDAuthKeychain *keychain = [[FIRInstanceIDAuthKeychain alloc] initWithIdentifier:kFIRInstanceIDCheckinKeychainGeneric]; diff --git a/Firebase/InstanceID/FIRInstanceIDConstants.h b/Firebase/InstanceID/FIRInstanceIDConstants.h index 97f660f6b19..cd2e131551d 100644 --- a/Firebase/InstanceID/FIRInstanceIDConstants.h +++ b/Firebase/InstanceID/FIRInstanceIDConstants.h @@ -43,8 +43,8 @@ FOUNDATION_EXPORT NSString *const kFIRInstanceIDAllScopeIdentifier; /// The scope used to save the IID "*" scope token. FOUNDATION_EXPORT NSString *const kFIRInstanceIDDefaultTokenScope; -/// Subdirectory in Application Support directory to store InstanceID preferences. -FOUNDATION_EXPORT NSString *const kFIRInstanceIDApplicationSupportSubDirectory; +/// Subdirectory in search path directory to store InstanceID preferences. +FOUNDATION_EXPORT NSString *const kFIRInstanceIDSubDirectoryName; /// The key for APNS token in options dictionary. FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenOptionsAPNSKey; diff --git a/Firebase/InstanceID/FIRInstanceIDConstants.m b/Firebase/InstanceID/FIRInstanceIDConstants.m index b98afcbc753..81f4620e0a4 100644 --- a/Firebase/InstanceID/FIRInstanceIDConstants.m +++ b/Firebase/InstanceID/FIRInstanceIDConstants.m @@ -31,7 +31,7 @@ // Miscellaneous NSString *const kFIRInstanceIDAllScopeIdentifier = @"iid-all"; NSString *const kFIRInstanceIDDefaultTokenScope = @"*"; -NSString *const kFIRInstanceIDApplicationSupportSubDirectory = @"Google/FirebaseInstanceID"; +NSString *const kFIRInstanceIDSubDirectoryName = @"Google/FirebaseInstanceID"; // Registration Options NSString *const kFIRInstanceIDTokenOptionsAPNSKey = @"apns_token"; diff --git a/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m b/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m index 4db201ae7b2..c913daafa14 100644 --- a/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m +++ b/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m @@ -130,9 +130,9 @@ - (instancetype)init { self = [super init]; if (self) { NSString *fileName = [[self class] keyStoreFileName]; - _plist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:fileName - applicationSupportSubDirectory:kFIRInstanceIDApplicationSupportSubDirectory]; + _plist = + [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:fileName + subDirectory:kFIRInstanceIDSubDirectoryName]; } return self; } diff --git a/Firebase/InstanceID/FIRInstanceIDStore.h b/Firebase/InstanceID/FIRInstanceIDStore.h index 0e16ca2e404..2b4a68a510a 100644 --- a/Firebase/InstanceID/FIRInstanceIDStore.h +++ b/Firebase/InstanceID/FIRInstanceIDStore.h @@ -153,21 +153,21 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)removeCheckinPreferencesWithHandler:(nullable void (^)(NSError *error))handler; -#pragma mark - ApplicationSupport sub-directory +#pragma mark - Standard Directory sub-directory /** - * Check if Application Support directory has InstanceID subdirectory + * Check if supported directory has InstanceID subdirectory * * @return YES if the Application Support directory has InstanceID subdirectory else NO. */ -+ (BOOL)hasApplicationSupportSubDirectory:(NSString *)subDirectoryName; ++ (BOOL)hasSubDirectory:(NSString *)subDirectoryName; /** * Create InstanceID subdirectory in Application support directory. * * @return YES if the subdirectory was created successfully else NO. */ -+ (BOOL)createApplicationSupportSubDirectory:(NSString *)subDirectoryName; ++ (BOOL)createSubDirectory:(NSString *)subDirectoryName; /** * Removes Application Support subdirectory for InstanceID. @@ -176,7 +176,7 @@ NS_ASSUME_NONNULL_BEGIN * * @return YES if the deletion was successful else NO. */ -+ (BOOL)removeApplicationSupportSubDirectory:(NSString *)subDirectoryName error:(NSError **)error; ++ (BOOL)removeSubDirectory:(NSString *)subDirectoryName error:(NSError **)error; @end diff --git a/Firebase/InstanceID/FIRInstanceIDStore.m b/Firebase/InstanceID/FIRInstanceIDStore.m index f31b5538a87..7467cdac2d3 100644 --- a/Firebase/InstanceID/FIRInstanceIDStore.m +++ b/Firebase/InstanceID/FIRInstanceIDStore.m @@ -40,8 +40,8 @@ @implementation FIRInstanceIDStore - (instancetype)initWithDelegate:(NSObject *)delegate { FIRInstanceIDCheckinStore *checkinStore = [[FIRInstanceIDCheckinStore alloc] - initWithCheckinPlistFileName:kCheckinFileName - applicationSupportSubDirectory:kFIRInstanceIDApplicationSupportSubDirectory]; + initWithCheckinPlistFileName:kCheckinFileName + subDirectoryName:kFIRInstanceIDSubDirectoryName]; FIRInstanceIDTokenStore *tokenStore = [FIRInstanceIDTokenStore defaultStore]; @@ -63,8 +63,8 @@ - (instancetype)initWithCheckinStore:(FIRInstanceIDCheckinStore *)checkinStore #pragma mark - Upgrades -+ (BOOL)hasApplicationSupportSubDirectory:(NSString *)subDirectoryName { - NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName]; ++ (BOOL)hasSubDirectory:(NSString *)subDirectoryName { + NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; BOOL isDirectory; if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath isDirectory:&isDirectory]) { @@ -75,16 +75,24 @@ + (BOOL)hasApplicationSupportSubDirectory:(NSString *)subDirectoryName { return YES; } -+ (NSString *)pathForApplicationSupportSubDirectory:(NSString *)subDirectoryName { ++ (NSSearchPathDirectory)supportedDirectory { +#if TARGET_OS_TV + return NSCachesDirectory; +#else + return NSApplicationSupportDirectory; +#endif +} + ++ (NSString *)pathForSupportSubDirectory:(NSString *)subDirectoryName { NSArray *directoryPaths = - NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - NSString *applicationSupportDirPath = directoryPaths.lastObject; - NSArray *components = @[ applicationSupportDirPath, subDirectoryName ]; + NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES); + NSString *dirPath = directoryPaths.lastObject; + NSArray *components = @[ dirPath, subDirectoryName ]; return [NSString pathWithComponents:components]; } -+ (BOOL)createApplicationSupportSubDirectory:(NSString *)subDirectoryName { - NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName]; ++ (BOOL)createSubDirectory:(NSString *)subDirectoryName { + NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; BOOL hasSubDirectory; if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath @@ -109,9 +117,9 @@ + (BOOL)createApplicationSupportSubDirectory:(NSString *)subDirectoryName { return YES; } -+ (BOOL)removeApplicationSupportSubDirectory:(NSString *)subDirectoryName error:(NSError **)error { - if ([self hasApplicationSupportSubDirectory:subDirectoryName]) { - NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName]; ++ (BOOL)removeSubDirectory:(NSString *)subDirectoryName error:(NSError **)error { + if ([self hasSubDirectory:subDirectoryName]) { + NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName]; BOOL isDirectory; if ([[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath isDirectory:&isDirectory]) { diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 6aa6ed93197..fd30b9996ca 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -277,7 +277,7 @@ - (void)start { withHost:hostname]; [self.reachability start]; - [self setupApplicationSupportSubDirectory]; + [self setupFileManagerSubDirectory]; // setup FIRMessaging objects [self setupRmqManager]; [self setupClient]; @@ -289,10 +289,9 @@ - (void)start { [self setupNotificationListeners]; } -- (void)setupApplicationSupportSubDirectory { - NSString *messagingSubDirectory = kFIRMessagingApplicationSupportSubDirectory; - if (![[self class] hasApplicationSupportSubDirectory:messagingSubDirectory]) { - [[self class] createApplicationSupportSubDirectory:messagingSubDirectory]; +- (void)setupFileManagerSubDirectory { + if (![[self class] hasSubDirectory:kFIRMessagingSubDirectoryName]) { + [[self class] createSubDirectory:kFIRMessagingSubDirectoryName]; } } @@ -971,8 +970,8 @@ - (void)defaultInstanceIDTokenWasRefreshed:(NSNotification *)notification { #pragma mark - Application Support Directory -+ (BOOL)hasApplicationSupportSubDirectory:(NSString *)subDirectoryName { - NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName]; ++ (BOOL)hasSubDirectory:(NSString *)subDirectoryName { + NSString *subDirectoryPath = [self pathForSubDirectory:subDirectoryName]; BOOL isDirectory; if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath isDirectory:&isDirectory]) { @@ -983,16 +982,16 @@ + (BOOL)hasApplicationSupportSubDirectory:(NSString *)subDirectoryName { return YES; } -+ (NSString *)pathForApplicationSupportSubDirectory:(NSString *)subDirectoryName { - NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, ++ (NSString *)pathForSubDirectory:(NSString *)subDirectoryName { + NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(FIRMessagingSupportedDirectory(), NSUserDomainMask, YES); NSString *applicationSupportDirPath = directoryPaths.lastObject; NSArray *components = @[applicationSupportDirPath, subDirectoryName]; return [NSString pathWithComponents:components]; } -+ (BOOL)createApplicationSupportSubDirectory:(NSString *)subDirectoryName { - NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName]; ++ (BOOL)createSubDirectory:(NSString *)subDirectoryName { + NSString *subDirectoryPath = [self pathForSubDirectory:subDirectoryName]; BOOL hasSubDirectory; if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath diff --git a/Firebase/Messaging/FIRMessagingConstants.h b/Firebase/Messaging/FIRMessagingConstants.h index ad0d6c90e4c..9a3ce04c863 100644 --- a/Firebase/Messaging/FIRMessagingConstants.h +++ b/Firebase/Messaging/FIRMessagingConstants.h @@ -44,7 +44,7 @@ FOUNDATION_EXPORT NSString *const kFIRMessagingMessageLinkKey; FOUNDATION_EXPORT NSString *const kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey; -FOUNDATION_EXPORT NSString *const kFIRMessagingApplicationSupportSubDirectory; +FOUNDATION_EXPORT NSString *const kFIRMessagingSubDirectoryName; // Notifications FOUNDATION_EXPORT NSString *const kFIRMessagingCheckinFetchedNotification; diff --git a/Firebase/Messaging/FIRMessagingConstants.m b/Firebase/Messaging/FIRMessagingConstants.m index 8904cc59d01..e50c923647b 100644 --- a/Firebase/Messaging/FIRMessagingConstants.m +++ b/Firebase/Messaging/FIRMessagingConstants.m @@ -38,7 +38,7 @@ NSString *const kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey = @"FirebaseAppDelegateProxyEnabled"; -NSString *const kFIRMessagingApplicationSupportSubDirectory = @"Google/FirebaseMessaging"; +NSString *const kFIRMessagingSubDirectoryName = @"Google/FirebaseMessaging"; // Notifications NSString *const kFIRMessagingCheckinFetchedNotification = @"com.google.gcm.notif-checkin-fetched"; diff --git a/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m b/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m index dc6e6c9c9dd..a14db5892a6 100644 --- a/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m +++ b/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m @@ -126,12 +126,16 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName { self = [super init]; if (self) { _databaseName = [databaseName copy]; +#if TARGET_OS_iOS BOOL didMoveToApplicationSupport = - [self moveToApplicationSupportSubDirectory:kFIRMessagingApplicationSupportSubDirectory]; + [self moveToApplicationSupportSubDirectory:kFIRMessagingSubDirectoryName]; _currentDirectory = didMoveToApplicationSupport ? FIRMessagingRmqDirectoryApplicationSupport : FIRMessagingRmqDirectoryDocuments; +#else + _currentDirectory = FIRMessagingRmqDirectoryApplicationSupport; +#endif [self openDatabase:_databaseName]; } @@ -143,7 +147,7 @@ - (void)dealloc { } - (BOOL)moveToApplicationSupportSubDirectory:(NSString *)subDirectoryName { - NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, + NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(FIRMessagingSupportedDirectory(), NSUserDomainMask, YES); NSString *applicationSupportDirPath = directoryPaths.lastObject; NSArray *components = @[applicationSupportDirPath, subDirectoryName]; @@ -205,12 +209,12 @@ + (NSString *)pathForDatabase:(NSString *)dbName inDirectory:(FIRMessagingRmqDir break; case FIRMessagingRmqDirectoryApplicationSupport: - paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, + paths = NSSearchPathForDirectoriesInDomains(FIRMessagingSupportedDirectory(), NSUserDomainMask, YES); components = @[ paths.lastObject, - kFIRMessagingApplicationSupportSubDirectory, + kFIRMessagingSubDirectoryName, dbNameWithExtension ]; break; @@ -262,10 +266,10 @@ - (void)removeDatabase { + (void)removeDatabase:(NSString *)dbName { NSString *documentsDirPath = [self pathForDatabase:dbName inDirectory:FIRMessagingRmqDirectoryDocuments]; - NSString *applicationSupportDirPath = + NSString *standardDirPath = [self pathForDatabase:dbName inDirectory:FIRMessagingRmqDirectoryApplicationSupport]; [[NSFileManager defaultManager] removeItemAtPath:documentsDirPath error:nil]; - [[NSFileManager defaultManager] removeItemAtPath:applicationSupportDirPath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:standardDirPath error:nil]; } - (void)openDatabase:(NSString *)dbName { diff --git a/Firebase/Messaging/FIRMessagingUtilities.h b/Firebase/Messaging/FIRMessagingUtilities.h index 206ff073f7e..58475bf8877 100644 --- a/Firebase/Messaging/FIRMessagingUtilities.h +++ b/Firebase/Messaging/FIRMessagingUtilities.h @@ -55,4 +55,6 @@ FOUNDATION_EXPORT NSString *FIRMessagingAppIdentifier(void); FOUNDATION_EXPORT uint64_t FIRMessagingGetFreeDiskSpaceInMB(void); FOUNDATION_EXPORT UIApplication *FIRMessagingUIApplication(void); +FOUNDATION_EXPORT NSSearchPathDirectory FIRMessagingSupportedDirectory(void); + diff --git a/Firebase/Messaging/FIRMessagingUtilities.m b/Firebase/Messaging/FIRMessagingUtilities.m index fa3a2334b05..743caed37a2 100644 --- a/Firebase/Messaging/FIRMessagingUtilities.m +++ b/Firebase/Messaging/FIRMessagingUtilities.m @@ -186,3 +186,11 @@ uint64_t FIRMessagingGetFreeDiskSpaceInMB(void) { } return [applicationClass sharedApplication]; } + +NSSearchPathDirectory FIRMessagingSupportedDirectory(void) { +#if TARGET_OS_TV + return NSCachesDirectory; +#else + return NSApplicationSupportDirectory; +#endif +} From 6d24fa61a178eba31a32c646151c1df63028333a Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Wed, 13 Mar 2019 11:46:38 -0700 Subject: [PATCH 032/214] Update Auth CHANGELOG.md (#2549) --- Firebase/Auth/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md index 8c1f3032e3e..ea901cb5a56 100644 --- a/Firebase/Auth/CHANGELOG.md +++ b/Firebase/Auth/CHANGELOG.md @@ -2,7 +2,7 @@ - Deprecate Microsoft and Yahoo OAuth Provider ID (#2517) - Fix an issue where an exception was thrown when linking OAuth credentials. (#2521) - Fix an issue where a wrong error was thrown when handling error with - FEDERATED_USER_ID_ALREADY_LINKED. (#2530, #2542) + FEDERATED_USER_ID_ALREADY_LINKED. (#2522) # v5.4.0 - Add support of Generic IDP (#2405). From ed2b99a26c740bdc0f268ac3244b94271828a16d Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Wed, 13 Mar 2019 13:08:56 -0700 Subject: [PATCH 033/214] Disable Facebook API tests (#2547) --- Example/Auth/ApiTests/FacebookAuthTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/Auth/ApiTests/FacebookAuthTests.m b/Example/Auth/ApiTests/FacebookAuthTests.m index cb74fe71461..ba865bbb5f0 100644 --- a/Example/Auth/ApiTests/FacebookAuthTests.m +++ b/Example/Auth/ApiTests/FacebookAuthTests.m @@ -36,7 +36,7 @@ @interface FacebookAuthTests : FIRAuthApiTestsBase @implementation FacebookAuthTests -- (void)testSignInWithFaceboook { +- (void)DISABLE_testSignInWithFaceboook { FIRAuth *auth = [FIRAuth auth]; if (!auth) { XCTFail(@"Could not obtain auth object."); @@ -76,7 +76,7 @@ - (void)testSignInWithFaceboook { [self deleteFacebookTestingAccountbyId:facebookAccountId]; } -- (void)testLinkAnonymousAccountToFacebookAccount { +- (void)DISABLE_testLinkAnonymousAccountToFacebookAccount { FIRAuth *auth = [FIRAuth auth]; if (!auth) { XCTFail(@"Could not obtain auth object."); From ff76c8a646be102a695b9fd845432fbcbdd08ce1 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Wed, 13 Mar 2019 21:08:24 -0400 Subject: [PATCH 034/214] FIRInstanceIDCheckinService: make sure tasks are not submitted to an invalidated NSURLSession (#2548) FIRInstanceIDCheckinService: make sure tasks are not submitted to an invalidated NSURLSession (#2548) --- .../Tests/FIRInstanceIDCheckinServiceTest.m | 23 +++++++++++++++++++ Firebase/InstanceID/FIRIMessageCode.h | 1 + .../InstanceID/FIRInstanceIDCheckinService.m | 11 +++++++++ 3 files changed, 35 insertions(+) diff --git a/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m b/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m index b25fac54c49..115ed856ffb 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m @@ -134,6 +134,29 @@ - (void)testCheckinServiceAddsFirebaseUserAgentToHTTPHeader { }]; } +- (void)testCheckinServiceFailsWithErrorAfterStopFetching { + [self.checkinService stopFetching]; + + XCTestExpectation *checkinCompletionExpectation = + [self expectationWithDescription:@"Checkin Completion"]; + + [self.checkinService + checkinWithExistingCheckin:nil + completion:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) { + [checkinCompletionExpectation fulfill]; + XCTAssertNil(preferences); + XCTAssertNotNil(error); + XCTAssertEqual(error.code, kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn); + }]; + + [self waitForExpectationsWithTimeout:5 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Checkin Timeout Error: %@", error); + } + }]; +} + #pragma mark - Stub - (FIRInstanceIDCheckinPreferences *)stubCheckinCacheWithValidData { diff --git a/Firebase/InstanceID/FIRIMessageCode.h b/Firebase/InstanceID/FIRIMessageCode.h index 80cf0134ddc..db653d7cc48 100644 --- a/Firebase/InstanceID/FIRIMessageCode.h +++ b/Firebase/InstanceID/FIRIMessageCode.h @@ -65,6 +65,7 @@ typedef NS_ENUM(NSInteger, FIRInstanceIDMessageCode) { kFIRInstanceIDMessageCodeService004 = 7004, kFIRInstanceIDMessageCodeService005 = 7005, kFIRInstanceIDMessageCodeService006 = 7006, + kFIRIntsanceIDInvalidNetworkSession = 7007, // FIRInstanceIDCheckinStore.m // DO NOT USE 8002, 8004 - 8008 kFIRInstanceIDMessageCodeCheckinStore000 = 8000, diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinService.m b/Firebase/InstanceID/FIRInstanceIDCheckinService.m index 9b79c770c62..8a2711d6231 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinService.m +++ b/Firebase/InstanceID/FIRInstanceIDCheckinService.m @@ -74,6 +74,15 @@ - (void)checkinWithExistingCheckin:(FIRInstanceIDCheckinPreferences *)existingCh completion:(FIRInstanceIDDeviceCheckinCompletion)completion { _FIRInstanceIDDevAssert(completion != nil, @"completion required"); + if (self.session == nil) { + FIRInstanceIDLoggerError(kFIRIntsanceIDInvalidNetworkSession, + @"Inconsistent state: NSURLSession has been invalidated"); + NSError *error = + [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]; + completion(nil, error); + return; + } + NSURL *url = [NSURL URLWithString:kDeviceCheckinURL]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; @@ -179,6 +188,8 @@ - (void)checkinWithExistingCheckin:(FIRInstanceIDCheckinPreferences *)existingCh - (void)stopFetching { [self.session invalidateAndCancel]; + // The session cannot be reused after invalidation. Dispose it to prevent accident reusing. + self.session = nil; } #pragma mark - Private From e524a8dc99f693f31774bacbd326fde28cdfc061 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 14 Mar 2019 04:47:40 -0700 Subject: [PATCH 035/214] Fix GULResetLogger API breakage (#2552) * Fix GULResetLogger API breakage * podspec and CHANGELOG * typo --- GoogleUtilities.podspec | 2 +- GoogleUtilities/CHANGELOG.md | 3 +++ GoogleUtilities/Logger/GULLogger.m | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/GoogleUtilities.podspec b/GoogleUtilities.podspec index 7a61dc4db9a..82f2fbc71b2 100644 --- a/GoogleUtilities.podspec +++ b/GoogleUtilities.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleUtilities' - s.version = '5.4.0' + s.version = '5.4.1' s.summary = 'Google Utilities for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/GoogleUtilities/CHANGELOG.md b/GoogleUtilities/CHANGELOG.md index 1286f236d80..873968ee8f5 100644 --- a/GoogleUtilities/CHANGELOG.md +++ b/GoogleUtilities/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +# 5.4.1 +- Fix GULResetLogger API breakage. (#2551) + # 5.4.0 - Update GULLogger to use os_log instead of asl_log on iOS 9 and later. (#2374, #2504) diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index 19cc5479596..f0588b9de06 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -36,6 +36,12 @@ }); return messageCodeRegex; } + +// GULResetLogger is unused but depended on by FirebaseCore versions 5.3.x and earlier. +// It cannot be removed until GoogleUtilities does a breaking change release since FirebaseCore +// allows GoogleUtilities to float up to the highest 5.x version. +void GULResetLogger() { +} #endif @implementation GULLogger (Internal) From 8ed850483247a2ef7bd7310e49cacee5d02b1ffb Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 14 Mar 2019 11:02:58 -0700 Subject: [PATCH 036/214] Rename ApplicationSupportDirectory in IID tests (#2553) --- .../Tests/FIRInstanceIDAuthKeyChainTest.m | 1 - .../FIRInstanceIDBackupExcludedPlistTest.m | 42 +++++++++---------- .../Tests/FIRInstanceIDCheckinStoreTest.m | 24 +++++------ .../InstanceID/Tests/FIRInstanceIDStoreTest.m | 11 +++-- .../Tests/FIRInstanceIDTokenManagerTest.m | 12 +++--- Firebase/InstanceID/FIRInstanceID.m | 4 -- .../FIRInstanceIDBackupExcludedPlist.m | 1 + Firebase/Messaging/FIRMessaging.m | 4 +- 8 files changed, 45 insertions(+), 54 deletions(-) diff --git a/Example/InstanceID/Tests/FIRInstanceIDAuthKeyChainTest.m b/Example/InstanceID/Tests/FIRInstanceIDAuthKeyChainTest.m index 712ea8f786b..97f575a02d8 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDAuthKeyChainTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDAuthKeyChainTest.m @@ -24,7 +24,6 @@ static NSString *const kFIRInstanceIDTestKeychainId = @"com.google.iid-tests"; static NSString *const kFakeCheckinPlistName = @"com.google.test.IIDStoreTestCheckin"; -static NSString *const kApplicationSupportSubDirectoryName = @"FirebaseInstanceIDCheckinTest"; static NSString *const kAuthorizedEntity = @"test-audience"; static NSString *const kScope = @"test-scope"; diff --git a/Example/InstanceID/Tests/FIRInstanceIDBackupExcludedPlistTest.m b/Example/InstanceID/Tests/FIRInstanceIDBackupExcludedPlistTest.m index b6321ce940c..dba4746b58b 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDBackupExcludedPlistTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDBackupExcludedPlistTest.m @@ -21,7 +21,7 @@ #import "Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h" #import "Firebase/InstanceID/FIRInstanceIDStore.h" -static NSString *const kApplicationSupportSubDirectoryName = @"FirebaseInstanceIDBackupPlistTest"; +static NSString *const kSubDirectoryName = @"FirebaseInstanceIDBackupPlistTest"; static NSString *const kTestPlistFileName = @"com.google.test.IIDBackupExcludedPlist"; @interface FIRInstanceIDBackupExcludedPlist () @@ -38,15 +38,14 @@ @implementation FIRInstanceIDBackupExcludedPlistTest - (void)setUp { [super setUp]; - [FIRInstanceIDStore createSubDirectory:kApplicationSupportSubDirectoryName]; - self.plist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kTestPlistFileName - subDirectory:kApplicationSupportSubDirectoryName]; + [FIRInstanceIDStore createSubDirectory:kSubDirectoryName]; + self.plist = [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kTestPlistFileName + subDirectory:kSubDirectoryName]; } - (void)tearDown { [self.plist deleteFile:nil]; - [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; + [FIRInstanceIDStore removeSubDirectory:kSubDirectoryName error:nil]; [super tearDown]; } @@ -77,14 +76,14 @@ - (void)testWriteToPlistInApplicationSupportFolder { XCTAssertTrue([self.plist doesFileExist]); XCTAssertEqualObjects(plistContents, [self.plist contentAsDictionary]); - XCTAssertTrue([self isPlistInApplicationSupportDirectory]); + XCTAssertTrue([self doesPlistFileExist]); } - (void)testMovePlistToApplicationSupportDirectorySuccess { NSDictionary *plistContents = @{@"hello" : @"world", @"id" : @123}; [self.plist writeDictionary:plistContents error:nil]; - [self.plist moveToApplicationSupportSubDirectory:kApplicationSupportSubDirectoryName]; - XCTAssertTrue([self isPlistInApplicationSupportDirectory]); + [self.plist moveToApplicationSupportSubDirectory:kSubDirectoryName]; + XCTAssertTrue([self doesPlistFileExist]); XCTAssertFalse([self isPlistInDocumentsDirectory]); NSDictionary *newPlistContents = @{@"world" : @"hello"}; @@ -97,19 +96,18 @@ - (void)testMovePlistToApplicationSupportDirectoryFailure { // which should only apply to iOS. #if TARGET_OS_IOS // Delete the subdirectory - [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; + [FIRInstanceIDStore removeSubDirectory:kSubDirectoryName error:nil]; // Create a new plistl This would try to move or write to the ApplicationSupport directory // but since the subdirectory is not there anymore it will fail and rather write to the // Documents folder. - self.plist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kTestPlistFileName - subDirectory:kApplicationSupportSubDirectoryName]; + self.plist = [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kTestPlistFileName + subDirectory:kSubDirectoryName]; NSDictionary *plistContents = @{@"hello" : @"world", @"id" : @123}; [self.plist writeDictionary:plistContents error:nil]; - XCTAssertFalse([self isPlistInApplicationSupportDirectory]); + XCTAssertFalse([self doesPlistFileExist]); XCTAssertTrue([self isPlistInDocumentsDirectory]); NSDictionary *newPlistContents = @{@"world" : @"hello"}; @@ -118,14 +116,14 @@ - (void)testMovePlistToApplicationSupportDirectoryFailure { XCTAssertEqualObjects(newPlistContents, [self.plist contentAsDictionary]); // The new file should still be written to the Documents folder. - XCTAssertFalse([self isPlistInApplicationSupportDirectory]); + XCTAssertFalse([self doesPlistFileExist]); XCTAssertTrue([self isPlistInDocumentsDirectory]); #endif } #pragma mark - Private Helpers -- (BOOL)isPlistInApplicationSupportDirectory { +- (BOOL)doesPlistFileExist { #if TARGET_OS_TV NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); @@ -133,11 +131,9 @@ - (BOOL)isPlistInApplicationSupportDirectory { NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); #endif - NSString *applicationSupportDirPath = directoryPaths.lastObject; - NSArray *components = @[ - applicationSupportDirPath, kApplicationSupportSubDirectoryName, - [NSString stringWithFormat:@"%@.plist", kTestPlistFileName] - ]; + NSString *dirPath = directoryPaths.lastObject; + NSArray *components = + @[ dirPath, kSubDirectoryName, [NSString stringWithFormat:@"%@.plist", kTestPlistFileName] ]; NSString *plistPath = [NSString pathWithComponents:components]; return [[NSFileManager defaultManager] fileExistsAtPath:plistPath]; } @@ -145,9 +141,9 @@ - (BOOL)isPlistInApplicationSupportDirectory { - (BOOL)isPlistInDocumentsDirectory { NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *applicationSupportDirPath = directoryPaths.lastObject; + NSString *documentsSupportDirPath = directoryPaths.lastObject; NSArray *components = - @[ applicationSupportDirPath, [NSString stringWithFormat:@"%@.plist", kTestPlistFileName] ]; + @[ documentsSupportDirPath, [NSString stringWithFormat:@"%@.plist", kTestPlistFileName] ]; NSString *plistPath = [NSString pathWithComponents:components]; return [[NSFileManager defaultManager] fileExistsAtPath:plistPath]; } diff --git a/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m b/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m index a1b903c4a6e..28a4dc9a291 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m @@ -37,7 +37,7 @@ - (NSString *)bundleIdentifierForKeychainAccount; // Testing constants static NSString *const kFakeCheckinPlistName = @"com.google.test.IIDStoreTestCheckin"; -static NSString *const kApplicationSupportSubDirectoryName = @"FirebaseInstanceIDCheckinTest"; +static NSString *const kSubDirectoryName = @"FirebaseInstanceIDCheckinTest"; static NSString *const kAuthorizedEntity = @"test-audience"; static NSString *const kAuthID = @"test-auth-id"; @@ -56,7 +56,7 @@ @implementation FIRInstanceIDCheckinStoreTest - (void)setUp { [super setUp]; - [FIRInstanceIDStore createSubDirectory:kApplicationSupportSubDirectoryName]; + [FIRInstanceIDStore createSubDirectory:kSubDirectoryName]; } - (void)tearDown { @@ -65,7 +65,7 @@ - (void)tearDown { NSError *error; [[NSFileManager defaultManager] removeItemAtPath:path error:&error]; } - [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; + [FIRInstanceIDStore removeSubDirectory:kSubDirectoryName error:nil]; [super tearDown]; } @@ -75,9 +75,9 @@ - (void)tearDown { - (void)testInvalidCheckinPreferencesOnKeychainFail { XCTestExpectation *checkinInvalidExpectation = [self expectationWithDescription:@"Checkin preference should be invalid after keychain failure"]; - FIRInstanceIDBackupExcludedPlist *checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kFakeCheckinPlistName - subDirectory:kApplicationSupportSubDirectoryName]; + FIRInstanceIDBackupExcludedPlist *checkinPlist = + [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName + subDirectory:kSubDirectoryName]; FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; @@ -108,9 +108,9 @@ - (void)testInvalidCheckinPreferencesOnKeychainFail { - (void)testCheckinSaveFailsOnKeychainWriteFailure { XCTestExpectation *checkinSaveFailsExpectation = [self expectationWithDescription:@"Checkin save should fail after keychain write failure"]; - FIRInstanceIDBackupExcludedPlist *checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kFakeCheckinPlistName - subDirectory:kApplicationSupportSubDirectoryName]; + FIRInstanceIDBackupExcludedPlist *checkinPlist = + [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName + subDirectory:kSubDirectoryName]; FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; fakeKeychain.cannotWriteToKeychain = YES; @@ -139,9 +139,9 @@ - (void)testCheckinMigrationMovesToNewLocationInKeychain { XCTestExpectation *checkinMigrationExpectation = [self expectationWithDescription:@"checkin migration should move to the new location"]; // Create checkin store class. - FIRInstanceIDBackupExcludedPlist *checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:kFakeCheckinPlistName - subDirectory:kApplicationSupportSubDirectoryName]; + FIRInstanceIDBackupExcludedPlist *checkinPlist = + [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName + subDirectory:kSubDirectoryName]; FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; FIRInstanceIDFakeKeychain *weakKeychain = fakeKeychain; diff --git a/Example/InstanceID/Tests/FIRInstanceIDStoreTest.m b/Example/InstanceID/Tests/FIRInstanceIDStoreTest.m index ba197641819..efa5edc2f39 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDStoreTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDStoreTest.m @@ -28,7 +28,7 @@ #import "Firebase/InstanceID/FIRInstanceIDTokenStore.h" #import "Firebase/InstanceID/FIRInstanceIDUtilities.h" -static NSString *const kApplicationSupportSubDirectoryName = @"FirebaseInstanceIDStoreTest"; +static NSString *const kSubDirectoryName = @"FirebaseInstanceIDStoreTest"; static NSString *const kAuthorizedEntity = @"test-audience"; static NSString *const kScope = @"test-scope"; @@ -74,12 +74,11 @@ @implementation FIRInstanceIDStoreTest - (void)setUp { [super setUp]; - [FIRInstanceIDStore createSubDirectory:kApplicationSupportSubDirectoryName]; + [FIRInstanceIDStore createSubDirectory:kSubDirectoryName]; NSString *checkinPlistName = @"com.google.test.IIDStoreTestCheckin"; - self.checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:checkinPlistName - subDirectory:kApplicationSupportSubDirectoryName]; + self.checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:checkinPlistName + subDirectory:kSubDirectoryName]; // checkin store FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; @@ -103,7 +102,7 @@ - (void)setUp { - (void)tearDown { [self.instanceIDStore removeAllCachedTokensWithHandler:nil]; [self.instanceIDStore removeCheckinPreferencesWithHandler:nil]; - [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; + [FIRInstanceIDStore removeSubDirectory:kSubDirectoryName error:nil]; [_mockCheckinStore stopMocking]; [_mockTokenStore stopMocking]; [_mockInstanceIDStore stopMocking]; diff --git a/Example/InstanceID/Tests/FIRInstanceIDTokenManagerTest.m b/Example/InstanceID/Tests/FIRInstanceIDTokenManagerTest.m index e3bdc8e18f7..96448387d76 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDTokenManagerTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDTokenManagerTest.m @@ -30,7 +30,7 @@ #import "Firebase/InstanceID/FIRInstanceIDTokenOperation.h" #import "Firebase/InstanceID/FIRInstanceIDTokenStore.h" -static NSString *const kApplicationSupportSubDirectoryName = @"FirebaseInstanceIDTokenManagerTest"; +static NSString *const kSubDirectoryName = @"FirebaseInstanceIDTokenManagerTest"; static NSString *const kAuthorizedEntity = @"test-authorized-entity"; static NSString *const kScope = @"test-scope"; @@ -91,12 +91,12 @@ @implementation FIRInstanceIDTokenManagerTest - (void)setUp { [super setUp]; - [FIRInstanceIDStore createSubDirectory:kApplicationSupportSubDirectoryName]; + [FIRInstanceIDStore createSubDirectory:kSubDirectoryName]; NSString *checkinPlistFilename = @"com.google.test.IIDCheckinTest"; - self.checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] - initWithFileName:checkinPlistFilename - subDirectory:kApplicationSupportSubDirectoryName]; + self.checkinPlist = + [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:checkinPlistFilename + subDirectory:kSubDirectoryName]; // checkin store FIRInstanceIDFakeKeychain *fakeCheckinKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; @@ -123,7 +123,7 @@ - (void)tearDown { } self.tokenManager = nil; - [FIRInstanceIDStore removeSubDirectory:kApplicationSupportSubDirectoryName error:nil]; + [FIRInstanceIDStore removeSubDirectory:kSubDirectoryName error:nil]; [super tearDown]; } diff --git a/Firebase/InstanceID/FIRInstanceID.m b/Firebase/InstanceID/FIRInstanceID.m index 795b70b312d..c8efba974e6 100644 --- a/Firebase/InstanceID/FIRInstanceID.m +++ b/Firebase/InstanceID/FIRInstanceID.m @@ -175,10 +175,6 @@ - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)stopAllRequests { - [self.tokenManager stopAllTokenOperations]; -} - #pragma mark - Tokens - (NSString *)token { diff --git a/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m b/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m index 866c8a86082..2c322224fa8 100644 --- a/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m +++ b/Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.m @@ -111,6 +111,7 @@ - (NSDictionary *)contentAsDictionary { - (BOOL)moveToApplicationSupportSubDirectory:(NSString *)subDirectoryName { NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES); + // This only going to happen inside iOS so it is an applicationSupportDirectory. NSString *applicationSupportDirPath = directoryPaths.lastObject; NSArray *components = @[ applicationSupportDirPath, subDirectoryName ]; NSString *subDirectoryPath = [NSString pathWithComponents:components]; diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index fd30b9996ca..0c5c7fe00f7 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -985,8 +985,8 @@ + (BOOL)hasSubDirectory:(NSString *)subDirectoryName { + (NSString *)pathForSubDirectory:(NSString *)subDirectoryName { NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(FIRMessagingSupportedDirectory(), NSUserDomainMask, YES); - NSString *applicationSupportDirPath = directoryPaths.lastObject; - NSArray *components = @[applicationSupportDirPath, subDirectoryName]; + NSString *dirPath = directoryPaths.lastObject; + NSArray *components = @[dirPath, subDirectoryName]; return [NSString pathWithComponents:components]; } From fabaa8b1d7c7a0f39af8b2d46c51a8f3fa4a25a4 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Thu, 14 Mar 2019 14:08:20 -0400 Subject: [PATCH 037/214] Clean the pod cache before starting. (#2554) This prevents errors from occurring, and will also ensure that the most recent version will be included if it was rebuilt. --- ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift | 15 ++++++++++++++- ZipBuilder/Sources/ZipBuilder/ShellUtils.swift | 5 +++-- ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift | 4 ++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift b/ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift index c35c1e60820..826051ea2be 100644 --- a/ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift +++ b/ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift @@ -50,7 +50,20 @@ public enum CocoaPodUtils { } } - /// Execute the `pod cache list` command to get the Pods currently cached on your machine. + /// Executes the `pod cache clean --all` command to remove any cached CocoaPods. + public static func cleanPodCache() { + let result = Shell.executeCommandFromScript("pod cache clean --all", outputToConsole: false) + switch result { + case let .error(code): + fatalError("Could not clean the pod cache, the command exited with \(code). Try running the" + + "command in Terminal to see what's wrong.") + case .success: + // No need to do anything else, continue on. + print("Successfully cleaned pod cache.") + return + } + } + /// Executes the `pod cache list` command to get the Pods curerntly cached on your machine. /// /// - Parameter dir: The directory containing all installed pods. diff --git a/ZipBuilder/Sources/ZipBuilder/ShellUtils.swift b/ZipBuilder/Sources/ZipBuilder/ShellUtils.swift index dd6f8820d65..59b6a256170 100644 --- a/ZipBuilder/Sources/ZipBuilder/ShellUtils.swift +++ b/ZipBuilder/Sources/ZipBuilder/ShellUtils.swift @@ -54,9 +54,10 @@ extension Shell { scriptPath = tempScriptsDir.appendingPathComponent("wrapper.sh") // Write the temporary script contents to the script's path. CocoaPods complains when LANG - // isn't set in the environment, so explicitly set it here. + // isn't set in the environment, so explicitly set it here. The `/usr/local/git/current/bin` + // is to allow the `sso` protocol if it's there. let contents = """ - export PATH="/usr/local/bin:$PATH" + export PATH="/usr/local/bin:/usr/local/git/current/bin:$PATH" export LANG="en_US.UTF-8" source ~/.bash_profile \(command) diff --git a/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift b/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift index 0aba05357d7..30b167a859f 100644 --- a/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift +++ b/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift @@ -182,6 +182,10 @@ struct ZipBuilder { // builds to just install a subset: `[.core, .analytics, .storage, .firestore]` for example. let subspecsToInstall = Subspec.allCases + // Remove CocoaPods cache so the build gets updates after a version is rebuilt during the + // release process. + CocoaPodUtils.cleanPodCache() + // We need to install all the subpsecs in order to get every single framework that we'll need // for the zip file. We can't install each one individually since some pods depend on different // subspecs from the same pod (ex: GoogleUtilities, GoogleToolboxForMac, etc). All of the code From 23191da81dcac2fa68066b1de383a002dfd250ff Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Thu, 14 Mar 2019 15:46:43 -0400 Subject: [PATCH 038/214] InstanceID changelog update (#2557) --- Firebase/InstanceID/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Firebase/InstanceID/CHANGELOG.md b/Firebase/InstanceID/CHANGELOG.md index 248cc813f0e..74ff31004cd 100644 --- a/Firebase/InstanceID/CHANGELOG.md +++ b/Firebase/InstanceID/CHANGELOG.md @@ -1,6 +1,7 @@ # 2019-03-19 -- v3.8.0 - Adding community support for tvOS. (#2428) - Adding Firebase info to checkin. (#2509) +- Fixed a crash in FIRInstanceIDCheckinService. (#2548) # 2019-03-05 -- v3.7.0 - Open source Firebase InstanceID. (#186) From 9502e8dda1ff696e455fcebf1642a55f2243f77f Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 14 Mar 2019 13:01:41 -0700 Subject: [PATCH 039/214] Restore GoogleUtilities 5.3.7 - revert os_log changes (#2556) --- Example/Core/Tests/FIRLoggerTest.m | 258 ++++++++++++++++- Firebase/Core/FIRLogger.m | 48 ++-- GoogleUtilities.podspec | 4 +- GoogleUtilities/CHANGELOG.md | 3 + .../GoogleUtilities.xcodeproj/project.pbxproj | 24 +- .../Example/Tests/Logger/GULASLLoggerTest.m | 149 ---------- .../Example/Tests/Logger/GULLoggerTest.m | 194 +++++++++---- .../Example/Tests/Logger/GULOSLoggerTest.m | 264 ------------------ .../Swizzler/GULRuntimeClassSnapshotTests.m | 5 + GoogleUtilities/Logger/GULASLLogger.h | 26 -- GoogleUtilities/Logger/GULASLLogger.m | 140 ---------- GoogleUtilities/Logger/GULLogger+Internal.h | 55 ---- GoogleUtilities/Logger/GULLogger.m | 223 ++++++++------- GoogleUtilities/Logger/GULOSLogger.h | 27 -- GoogleUtilities/Logger/GULOSLogger.m | 212 -------------- GoogleUtilities/Logger/Private/GULLogger.h | 68 +++-- .../Logger/Private/GULLoggerSystem.h | 50 ---- .../Logger/Public/GULLoggerLevel.h | 24 +- 18 files changed, 611 insertions(+), 1163 deletions(-) delete mode 100644 GoogleUtilities/Example/Tests/Logger/GULASLLoggerTest.m delete mode 100644 GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m delete mode 100644 GoogleUtilities/Logger/GULASLLogger.h delete mode 100644 GoogleUtilities/Logger/GULASLLogger.m delete mode 100644 GoogleUtilities/Logger/GULLogger+Internal.h delete mode 100644 GoogleUtilities/Logger/GULOSLogger.h delete mode 100644 GoogleUtilities/Logger/GULOSLogger.m delete mode 100644 GoogleUtilities/Logger/Private/GULLoggerSystem.h diff --git a/Example/Core/Tests/FIRLoggerTest.m b/Example/Core/Tests/FIRLoggerTest.m index a4415c047e1..cb6a59a5e99 100644 --- a/Example/Core/Tests/FIRLoggerTest.m +++ b/Example/Core/Tests/FIRLoggerTest.m @@ -12,14 +12,268 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import +#ifdef DEBUG +// The tests depend upon library methods only built with #ifdef DEBUG #import "FIRTestCase.h" +// TODO - FIRLoggerTest should be split into a separate FIRLoggerTest and GULLoggerTest. +// No test should include both includes. +#import +#import + +#import + +// The following constants are exposed from FIRLogger for unit tests. +extern NSString *const kFIRDisableDebugModeApplicationArgument; +extern NSString *const kFIREnableDebugModeApplicationArgument; + +/// Key for the debug mode bit in NSUserDefaults. +extern NSString *const kFIRPersistedDebugModeKey; + +extern const char *kGULLoggerASLClientFacilityName; + +extern void FIRResetLogger(void); + +extern void FIRSetLoggerUserDefaults(NSUserDefaults *defaults); + +extern aslclient getGULLoggerClient(void); + +extern dispatch_queue_t getGULClientQueue(void); + +extern BOOL getGULLoggerDebugMode(void); + +static NSString *const kMessageCode = @"I-COR000001"; + @interface FIRLoggerTest : FIRTestCase + +@property(nonatomic) NSString *randomLogString; + +@property(nonatomic, strong) NSUserDefaults *defaults; + @end @implementation FIRLoggerTest -// TODO(bstpierre): Test FIRLogger functionality separate from GULLogger. +- (void)setUp { + [super setUp]; + FIRResetLogger(); + + // Stub NSUserDefaults for cleaner testing. + _defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.firebase.logger_test"]; + FIRSetLoggerUserDefaults(_defaults); +} + +- (void)tearDown { + [super tearDown]; + + _defaults = nil; +} + +// Test some stable variables to make sure they weren't accidently changed. +- (void)testStableVariables { + // Strings of type FIRLoggerServices. + XCTAssertEqualObjects(kFIRLoggerABTesting, @"[Firebase/ABTesting]"); + XCTAssertEqualObjects(kFIRLoggerAdMob, @"[Firebase/AdMob]"); + XCTAssertEqualObjects(kFIRLoggerAnalytics, @"[Firebase/Analytics]"); + XCTAssertEqualObjects(kFIRLoggerAuth, @"[Firebase/Auth]"); + XCTAssertEqualObjects(kFIRLoggerCore, @"[Firebase/Core]"); + XCTAssertEqualObjects(kFIRLoggerCrash, @"[Firebase/Crash]"); + XCTAssertEqualObjects(kFIRLoggerDatabase, @"[Firebase/Database]"); + XCTAssertEqualObjects(kFIRLoggerDynamicLinks, @"[Firebase/DynamicLinks]"); + XCTAssertEqualObjects(kFIRLoggerInstanceID, @"[Firebase/InstanceID]"); + XCTAssertEqualObjects(kFIRLoggerInvites, @"[Firebase/Invites]"); + XCTAssertEqualObjects(kFIRLoggerMLKit, @"[Firebase/MLKit]"); + XCTAssertEqualObjects(kFIRLoggerMessaging, @"[Firebase/Messaging]"); + XCTAssertEqualObjects(kFIRLoggerRemoteConfig, @"[Firebase/RemoteConfig]"); + XCTAssertEqualObjects(kFIRLoggerStorage, @"[Firebase/Storage]"); +} + +- (void)testInitializeASLForNonDebugMode { + // Stub. + id processInfoMock = [OCMockObject partialMockForObject:[NSProcessInfo processInfo]]; + NSArray *arguments = @[ kFIRDisableDebugModeApplicationArgument ]; + [[[processInfoMock stub] andReturn:arguments] arguments]; + + // Test. + FIRLogError(kFIRLoggerCore, kMessageCode, @"Some error."); + + // Assert. +#if MAKE_THREAD_SAFE + NSNumber *debugMode = [self.defaults objectForKey:kFIRPersistedDebugModeKey]; + XCTAssertNil(debugMode); + XCTAssertFalse(getGULLoggerDebugMode()); +#endif + + // Stop. + [processInfoMock stopMocking]; +} + +- (void)testInitializeASLForDebugModeWithArgument { + // Stub. + id processInfoMock = [OCMockObject partialMockForObject:[NSProcessInfo processInfo]]; + NSArray *arguments = @[ kFIREnableDebugModeApplicationArgument ]; + [[[processInfoMock stub] andReturn:arguments] arguments]; + + // Test. + FIRLogError(kFIRLoggerCore, kMessageCode, @"Some error."); + +#ifdef MAKE_THREAD_SAFE + // Assert. + NSNumber *debugMode = [self.defaults objectForKey:kGULPersistedDebugModeKey]; + XCTAssertTrue(debugMode.boolValue); + XCTAssertTrue(getGULLoggerDebugMode()); +#endif + + // Stop. + [processInfoMock stopMocking]; +} + +- (void)testInitializeASLForDebugModeWithUserDefaults { + // Stub. + NSNumber *debugMode = @YES; + [self.defaults setBool:debugMode.boolValue forKey:kFIRPersistedDebugModeKey]; + + // Test. + GULLogError(@"my service", NO, kMessageCode, @"Some error."); + + // Assert. + debugMode = [self.defaults objectForKey:kFIRPersistedDebugModeKey]; + XCTAssertTrue(debugMode.boolValue); +} + +- (void)testMessageCodeFormat { + // Valid case. + XCTAssertNoThrow(FIRLogError(kFIRLoggerCore, @"I-APP000001", @"Message.")); + + // An extra dash or missing dash should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"I-APP-000001", @"Message.")); + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"IAPP000001", @"Message.")); + + // Wrong number of digits should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"I-APP00001", @"Message.")); + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"I-APP0000001", @"Message.")); + + // Lowercase should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"I-app000001", @"Message.")); + +// nil or empty message code should fail. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + XCTAssertThrows(FIRLogError(kFIRLoggerCore, nil, @"Message.")); +#pragma clang diagnostic pop + + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"", @"Message.")); + + // Android message code should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"A-APP000001", @"Message.")); +} + +- (void)testLoggerInterface { + XCTAssertNoThrow(FIRLogError(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogError(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(FIRLogWarning(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogWarning(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(FIRLogNotice(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogNotice(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(FIRLogInfo(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogInfo(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(FIRLogDebug(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogDebug(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); +} + +// asl_set_filter does not perform as expected in unit test environment with simulator. The +// following test only checks whether the logs have been sent to system with the default settings in +// the unit test environment. +- (void)testSystemLogWithDefaultStatus { +#if !(BUG128) // Disable until https://github.com/firebase/firebase-ios-sdk/issues/128 is fixed + // Test fails on device and iOS 9 simulators - b/38130372 + return; +#else + // Sets the time interval that we need to wait in order to fetch all the logs. + NSTimeInterval timeInterval = 0.1f; + // Generates a random string each time and check whether it has been logged. + // Log messages with Notice level and below should be logged to system/device by default. + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogError(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogWarning(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogNotice(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + // Log messages with Info level and above should NOT be logged to system/device by default. + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogInfo(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertFalse([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogDebug(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertFalse([self logExists]); +#endif +} + +// The FIRLoggerLevel enum must match the ASL_LEVEL_* constants, but we manually redefine +// them in FIRLoggerLevel.h since we cannot include (see b/34976089 for more details). +// This test ensures the constants match. +- (void)testFIRLoggerLevelValues { + XCTAssertEqual(FIRLoggerLevelError, ASL_LEVEL_ERR); + XCTAssertEqual(FIRLoggerLevelWarning, ASL_LEVEL_WARNING); + XCTAssertEqual(FIRLoggerLevelNotice, ASL_LEVEL_NOTICE); + XCTAssertEqual(FIRLoggerLevelInfo, ASL_LEVEL_INFO); + XCTAssertEqual(FIRLoggerLevelDebug, ASL_LEVEL_DEBUG); +} + +// Helper functions. +- (BOOL)logExists { + [self drainFIRClientQueue]; + NSString *correctMsg = + [NSString stringWithFormat:@"%@[%@] %@", kFIRLoggerCore, kMessageCode, self.randomLogString]; + return [self messageWasLogged:correctMsg]; +} + +- (void)drainFIRClientQueue { + dispatch_semaphore_t workerSemaphore = dispatch_semaphore_create(0); + dispatch_async(getGULClientQueue(), ^{ + dispatch_semaphore_signal(workerSemaphore); + }); + dispatch_semaphore_wait(workerSemaphore, DISPATCH_TIME_FOREVER); +} + +- (BOOL)messageWasLogged:(NSString *)message { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + aslmsg query = asl_new(ASL_TYPE_QUERY); + asl_set_query(query, ASL_KEY_FACILITY, kGULLoggerASLClientFacilityName, ASL_QUERY_OP_EQUAL); + aslresponse r = asl_search(getGULLoggerClient(), query); + asl_free(query); + aslmsg m; + const char *val; + NSMutableArray *allMsg = [[NSMutableArray alloc] init]; + while ((m = asl_next(r)) != NULL) { + val = asl_get(m, ASL_KEY_MSG); + if (val) { + [allMsg addObject:[NSString stringWithUTF8String:val]]; + } + } + asl_free(m); + asl_release(r); + return [allMsg containsObject:message]; +#pragma clang pop +} + @end +#endif diff --git a/Firebase/Core/FIRLogger.m b/Firebase/Core/FIRLogger.m index 67aee32b8ee..d1e3b37382b 100644 --- a/Firebase/Core/FIRLogger.m +++ b/Firebase/Core/FIRLogger.m @@ -20,23 +20,23 @@ #import "Private/FIRVersion.h" -FIRLoggerService kFIRLoggerABTesting = @"Firebase/ABTesting"; -FIRLoggerService kFIRLoggerAdMob = @"Firebase/AdMob"; -FIRLoggerService kFIRLoggerAnalytics = @"Firebase/Analytics"; -FIRLoggerService kFIRLoggerAuth = @"Firebase/Auth"; -FIRLoggerService kFIRLoggerCore = @"Firebase/Core"; -FIRLoggerService kFIRLoggerCrash = @"Firebase/Crash"; -FIRLoggerService kFIRLoggerDatabase = @"Firebase/Database"; -FIRLoggerService kFIRLoggerDynamicLinks = @"Firebase/DynamicLinks"; -FIRLoggerService kFIRLoggerFirestore = @"Firebase/Firestore"; -FIRLoggerService kFIRLoggerInstanceID = @"Firebase/InstanceID"; -FIRLoggerService kFIRLoggerInvites = @"Firebase/Invites"; -FIRLoggerService kFIRLoggerMLKit = @"Firebase/MLKit"; -FIRLoggerService kFIRLoggerMessaging = @"Firebase/Messaging"; -FIRLoggerService kFIRLoggerPerf = @"Firebase/Performance"; -FIRLoggerService kFIRLoggerRemoteConfig = @"Firebase/RemoteConfig"; -FIRLoggerService kFIRLoggerStorage = @"Firebase/Storage"; -FIRLoggerService kFIRLoggerSwizzler = @"FirebaseSwizzlingUtilities"; +FIRLoggerService kFIRLoggerABTesting = @"[Firebase/ABTesting]"; +FIRLoggerService kFIRLoggerAdMob = @"[Firebase/AdMob]"; +FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]"; +FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]"; +FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]"; +FIRLoggerService kFIRLoggerCrash = @"[Firebase/Crash]"; +FIRLoggerService kFIRLoggerDatabase = @"[Firebase/Database]"; +FIRLoggerService kFIRLoggerDynamicLinks = @"[Firebase/DynamicLinks]"; +FIRLoggerService kFIRLoggerFirestore = @"[Firebase/Firestore]"; +FIRLoggerService kFIRLoggerInstanceID = @"[Firebase/InstanceID]"; +FIRLoggerService kFIRLoggerInvites = @"[Firebase/Invites]"; +FIRLoggerService kFIRLoggerMLKit = @"[Firebase/MLKit]"; +FIRLoggerService kFIRLoggerMessaging = @"[Firebase/Messaging]"; +FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]"; +FIRLoggerService kFIRLoggerRemoteConfig = @"[Firebase/RemoteConfig]"; +FIRLoggerService kFIRLoggerStorage = @"[Firebase/Storage]"; +FIRLoggerService kFIRLoggerSwizzler = @"[FirebaseSwizzlingUtilities]"; /// Arguments passed on launch. NSString *const kFIRDisableDebugModeApplicationArgument = @"-FIRDebugDisabled"; @@ -106,6 +106,20 @@ void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel) { GULSetLoggerLevel((GULLoggerLevel)loggerLevel); } +#ifdef DEBUG +void FIRResetLogger() { + extern void GULResetLogger(void); + sFIRLoggerOnceToken = 0; + [sFIRLoggerUserDefaults removeObjectForKey:kFIRPersistedDebugModeKey]; + sFIRLoggerUserDefaults = nil; + GULResetLogger(); +} + +void FIRSetLoggerUserDefaults(NSUserDefaults *defaults) { + sFIRLoggerUserDefaults = defaults; +} +#endif + /** * Check if the level is high enough to be loggable. * diff --git a/GoogleUtilities.podspec b/GoogleUtilities.podspec index 82f2fbc71b2..b3dac0c0bdf 100644 --- a/GoogleUtilities.podspec +++ b/GoogleUtilities.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleUtilities' - s.version = '5.4.1' + s.version = '5.5.0' s.summary = 'Google Utilities for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC @@ -33,7 +33,7 @@ other Google CocoaPods. They're not intended for direct public usage. s.subspec 'Logger' do |ls| ls.source_files = 'GoogleUtilities/Logger/**/*.[mh]' - ls.public_header_files = 'GoogleUtilities/Logger/Public/*.h' + ls.public_header_files = 'GoogleUtilities/Logger/Private/*.h', 'GoogleUtilities/Logger/Public/*.h' ls.private_header_files = 'GoogleUtilities/Logger/Private/*.h' ls.dependency 'GoogleUtilities/Environment' end diff --git a/GoogleUtilities/CHANGELOG.md b/GoogleUtilities/CHANGELOG.md index 873968ee8f5..2bf1c603e2b 100644 --- a/GoogleUtilities/CHANGELOG.md +++ b/GoogleUtilities/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +# 5.5.0 +- Revert 5.4.x changes restoring 5.3.7 version. + # 5.4.1 - Fix GULResetLogger API breakage. (#2551) diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj index 880663d42fc..754333d6676 100644 --- a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj @@ -13,13 +13,7 @@ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; - C8042CCE220C7B69009A8CCF /* GULASLLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C8042CCD220C7B69009A8CCF /* GULASLLoggerTest.m */; }; - C8BE7EFC22110DA200AC306B /* GULOSLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C8BE7EFB22110DA200AC306B /* GULOSLoggerTest.m */; }; - C8BE7EFD22110DA200AC306B /* GULOSLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C8BE7EFB22110DA200AC306B /* GULOSLoggerTest.m */; }; - C8BE7EFE22110DA200AC306B /* GULOSLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C8BE7EFB22110DA200AC306B /* GULOSLoggerTest.m */; }; DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */; }; - DE83CAE122230DEC002003C1 /* GULASLLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C8042CCD220C7B69009A8CCF /* GULASLLoggerTest.m */; }; - DE83CAE222230DED002003C1 /* GULASLLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C8042CCD220C7B69009A8CCF /* GULASLLoggerTest.m */; }; DE84BBC421D7EC900048A176 /* GULUserDefaultsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */; }; DE84BBC521D7EC900048A176 /* GULUserDefaultsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */; }; DE84BBC621D7EC900048A176 /* GULUserDefaultsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */; }; @@ -99,8 +93,6 @@ 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GoogleUtilities.podspec; path = ../GoogleUtilities.podspec; sourceTree = ""; }; - C8042CCD220C7B69009A8CCF /* GULASLLoggerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GULASLLoggerTest.m; sourceTree = ""; }; - C8BE7EFB22110DA200AC306B /* GULOSLoggerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULOSLoggerTest.m; sourceTree = ""; }; DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULAppEnvironmentUtilTest.m; sourceTree = ""; }; DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULUserDefaultsTests.m; sourceTree = ""; }; DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULReachabilityCheckerTest.m; sourceTree = ""; }; @@ -319,8 +311,6 @@ isa = PBXGroup; children = ( DEC977DF20F6A7A700014E20 /* GULLoggerTest.m */, - C8042CCD220C7B69009A8CCF /* GULASLLoggerTest.m */, - C8BE7EFB22110DA200AC306B /* GULOSLoggerTest.m */, ); path = Logger; sourceTree = ""; @@ -626,14 +616,12 @@ DEC977D820F68C3300014E20 /* GULMutableDictionaryTest.m in Sources */, DEC977E120F6A7C100014E20 /* GULLoggerTest.m in Sources */, DEC977D920F68C3300014E20 /* GULNetworkTest.m in Sources */, - C8042CCE220C7B69009A8CCF /* GULASLLoggerTest.m in Sources */, EFBE67FA2101401100E756A7 /* GULSwizzlerTest.m in Sources */, EFBE67FD2101401100E756A7 /* GULObjectSwizzlerTest.m in Sources */, EFBE67FE2101401100E756A7 /* GULRuntimeClassSnapshotTests.m in Sources */, EFBE67FC2101401100E756A7 /* GULRuntimeClassDiffTests.m in Sources */, DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */, DEC977D720F68C3300014E20 /* GULReachabilityCheckerTest.m in Sources */, - C8BE7EFC22110DA200AC306B /* GULOSLoggerTest.m in Sources */, EFBE67FB2101401100E756A7 /* GULSwizzlingCacheTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -658,8 +646,6 @@ DE84BBC521D7EC900048A176 /* GULUserDefaultsTests.m in Sources */, DEC9781D20F6D39900014E20 /* GTMHTTPServer.m in Sources */, DEC9781B20F6D39500014E20 /* GULMutableDictionaryTest.m in Sources */, - DE83CAE122230DEC002003C1 /* GULASLLoggerTest.m in Sources */, - C8BE7EFD22110DA200AC306B /* GULOSLoggerTest.m in Sources */, DEC9781C20F6D39500014E20 /* GULNetworkTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -684,8 +670,6 @@ DE84BBC621D7EC900048A176 /* GULUserDefaultsTests.m in Sources */, DEC9786C20F6D66700014E20 /* GULReachabilityCheckerTest.m in Sources */, DEC9786820F6D65B00014E20 /* GULLoggerTest.m in Sources */, - DE83CAE222230DED002003C1 /* GULASLLoggerTest.m in Sources */, - C8BE7EFE22110DA200AC306B /* GULOSLoggerTest.m in Sources */, DEC9786D20F6D66B00014E20 /* GULAppEnvironmentUtilTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -902,7 +886,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", - "$(SRCROOT)/../Logger", + "$(SRCROOT)/../AppDelegateSwizzler/Internal", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; @@ -926,7 +910,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", - "$(SRCROOT)/../Logger", + "$(SRCROOT)/../AppDelegateSwizzler/Internal", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; @@ -1005,7 +989,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", - "$(SRCROOT)/../Logger", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; @@ -1037,7 +1020,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", - "$(SRCROOT)/../Logger", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; @@ -1117,7 +1099,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", - "$(SRCROOT)/../Logger", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -1148,7 +1129,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", - "$(SRCROOT)/../Logger", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/GoogleUtilities/Example/Tests/Logger/GULASLLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULASLLoggerTest.m deleted file mode 100644 index 642db000854..00000000000 --- a/GoogleUtilities/Example/Tests/Logger/GULASLLoggerTest.m +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2019 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 -#import "GULASLLogger.h" - -#import - -#import - -NS_ASSUME_NONNULL_BEGIN - -// TODO(bstpierre): Use a C function redirect to mock asl_* methods like GULOSLoggerTest. - -static NSString *const kService = @"my service"; -static NSString *const kCode = @"I-COR000001"; - -// Redefine class property as readwrite for testing. -@interface GULLogger (ForTesting) -@property(nonatomic, nullable, class, readwrite) id logger; -@end - -// Surface aslclient and dispatchQueues for tests. -@interface GULASLLogger (ForTesting) -@property(nonatomic) aslclient aslClient; -@property(nonatomic) dispatch_queue_t dispatchQueue; -@end - -#pragma mark - - -@interface GULASLLoggerTest : XCTestCase -@property(nonatomic, nullable) GULASLLogger *logger; -@end - -@implementation GULASLLoggerTest - -#pragma mark Helper Methods - -// TODO(bstpierre): Replace this with a XCTestExpectation like GULOSLoggerTest. -- (BOOL)messageWasLogged:(NSString *)message { - // Format the message as it's expected. - message = [NSString stringWithFormat:@"%@[%@] %@", kService, kCode, message]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - aslmsg query = asl_new(ASL_TYPE_QUERY); - asl_set_query(query, ASL_KEY_FACILITY, "com.google.utilities.logger", ASL_QUERY_OP_EQUAL); - aslresponse response = asl_search(self.logger.aslClient, query); - asl_release(query); - aslmsg msg; - const char *responseMsg; - BOOL messageFound = NO; - while ((msg = asl_next(response)) != NULL) { - responseMsg = asl_get(msg, ASL_KEY_MSG); - if ([message isEqualToString:[NSString stringWithUTF8String:responseMsg]]) { - messageFound = YES; - break; - } - } - asl_release(msg); - asl_release(response); -#pragma clang pop - return messageFound; -} - -#pragma mark Testing - -- (void)setUp { - [super setUp]; - self.logger = [[GULASLLogger alloc] init]; - GULLogger.logger = self.logger; -} - -- (void)tearDown { - GULLogger.logger = nil; - self.logger = nil; - [super tearDown]; -} - -- (void)testMessageCodeFormat { - // Valid case. - XCTAssertNoThrow(GULLogError(kService, NO, @"I-APP000001", @"Message.")); - - // An extra dash or missing dash should fail. - XCTAssertThrows(GULLogError(kService, NO, @"I-APP-000001", @"Message.")); - XCTAssertThrows(GULLogError(kService, NO, @"IAPP000001", @"Message.")); - - // Wrong number of digits should fail. - XCTAssertThrows(GULLogError(kService, NO, @"I-APP00001", @"Message.")); - XCTAssertThrows(GULLogError(kService, NO, @"I-APP0000001", @"Message.")); - - // Lowercase should fail. - XCTAssertThrows(GULLogError(kService, NO, @"I-app000001", @"Message.")); - - // nil or empty message code should fail. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnonnull" - XCTAssertThrows(GULLogError(kService, NO, nil, @"Message.")); -#pragma clang diagnostic pop - - XCTAssertThrows(GULLogError(kService, NO, @"", @"Message.")); - - // Android message code should fail. - XCTAssertThrows(GULLogError(kService, NO, @"A-APP000001", @"Message.")); -} - -- (void)testLoggerInterface { - XCTAssertNoThrow(GULLogError(kService, NO, kCode, @"Message.")); - XCTAssertNoThrow(GULLogError(kService, NO, kCode, @"Configure %@.", @"blah")); - - XCTAssertNoThrow(GULLogWarning(kService, NO, kCode, @"Message.")); - XCTAssertNoThrow(GULLogWarning(kService, NO, kCode, @"Configure %@.", @"blah")); - - XCTAssertNoThrow(GULLogNotice(kService, NO, kCode, @"Message.")); - XCTAssertNoThrow(GULLogNotice(kService, NO, kCode, @"Configure %@.", @"blah")); - - XCTAssertNoThrow(GULLogInfo(kService, NO, kCode, @"Message.")); - XCTAssertNoThrow(GULLogInfo(kService, NO, kCode, @"Configure %@.", @"blah")); - - XCTAssertNoThrow(GULLogDebug(kService, NO, kCode, @"Message.")); - XCTAssertNoThrow(GULLogDebug(kService, NO, kCode, @"Configure %@.", @"blah")); -} - -// The GULLoggerLevel enum must match the ASL_LEVEL_* constants, but we manually redefine -// them in GULLoggerLevel.h since we cannot include (see b/34976089 for more details). -// This test ensures the constants match. -- (void)testGULLoggerLevelValues { - XCTAssertEqual(GULLoggerLevelError, ASL_LEVEL_ERR); - XCTAssertEqual(GULLoggerLevelWarning, ASL_LEVEL_WARNING); - XCTAssertEqual(GULLoggerLevelNotice, ASL_LEVEL_NOTICE); - XCTAssertEqual(GULLoggerLevelInfo, ASL_LEVEL_INFO); - XCTAssertEqual(GULLoggerLevelDebug, ASL_LEVEL_DEBUG); -} - -// TODO(bstpierre): Add tests for logWithLevel:withService:isForced:withCode:withMessage: - -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m index 01b7fad5b70..9483bbcb5f9 100644 --- a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m +++ b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m @@ -12,92 +12,184 @@ // 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 #import #import -static GULLoggerLevel kLogLevel = GULLoggerLevelError; -static NSString *const kService = @"Test Service"; -static NSString *const kCode = @"I-COR000001"; -static NSString *const kLogMessage = @"Log Message"; -static NSString *const kVersionString = @"2"; -static char *const kVersionChar = "2"; +#import -// Redefine class property as readwrite for testing. -@interface GULLogger (ForTesting) -@property(nonatomic, class, readwrite) id logger; -@end +extern const char *kGULLoggerASLClientFacilityName; + +extern void GULResetLogger(void); + +extern aslclient getGULLoggerClient(void); + +extern dispatch_queue_t getGULClientQueue(void); -#pragma mark - +extern BOOL getGULLoggerDebugMode(void); + +static NSString *const kMessageCode = @"I-COR000001"; @interface GULLoggerTest : XCTestCase -@property(nonatomic) id loggerSystemMock; + +@property(nonatomic) NSString *randomLogString; + +@property(nonatomic, strong) NSUserDefaults *defaults; + @end @implementation GULLoggerTest - (void)setUp { [super setUp]; - self.loggerSystemMock = OCMProtocolMock(@protocol(GULLoggerSystem)); - GULLogger.logger = self.loggerSystemMock; + GULResetLogger(); + + // Stub NSUserDefaults for cleaner testing. + _defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.google.logger_test"]; } - (void)tearDown { - GULLogger.logger = nil; - [self.loggerSystemMock stopMocking]; - self.loggerSystemMock = nil; [super tearDown]; + + _defaults = nil; } -#pragma mark Initialization Tests +- (void)testMessageCodeFormat { + // Valid case. + XCTAssertNoThrow(GULLogError(@"my service", NO, @"I-APP000001", @"Message.")); -- (void)testInitializeEmpty { - [[self.loggerSystemMock expect] initializeLogger]; - GULLoggerInitialize(); - [self.loggerSystemMock verify]; -} + // An extra dash or missing dash should fail. + XCTAssertThrows(GULLogError(@"my service", NO, @"I-APP-000001", @"Message.")); + XCTAssertThrows(GULLogError(@"my service", NO, @"IAPP000001", @"Message.")); + + // Wrong number of digits should fail. + XCTAssertThrows(GULLogError(@"my service", NO, @"I-APP00001", @"Message.")); + XCTAssertThrows(GULLogError(@"my service", NO, @"I-APP0000001", @"Message.")); + + // Lowercase should fail. + XCTAssertThrows(GULLogError(@"my service", NO, @"I-app000001", @"Message.")); + +// nil or empty message code should fail. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + XCTAssertThrows(GULLogError(@"my service", NO, nil, @"Message.")); +#pragma clang diagnostic pop -- (void)testInitializeTwice { - [[self.loggerSystemMock expect] initializeLogger]; - GULLoggerInitialize(); - GULLoggerInitialize(); - [self.loggerSystemMock verify]; + XCTAssertThrows(GULLogError(@"my service", NO, @"", @"Message.")); + + // Android message code should fail. + XCTAssertThrows(GULLogError(@"my service", NO, @"A-APP000001", @"Message.")); } -#pragma mark Forwarded Call Tests +- (void)testLoggerInterface { + XCTAssertNoThrow(GULLogError(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogError(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(GULLogWarning(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogWarning(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(GULLogNotice(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogNotice(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); -- (void)testForceDebug { - [[self.loggerSystemMock expect] setForcedDebug:YES]; - GULLoggerForceDebug(); - [self.loggerSystemMock verify]; + XCTAssertNoThrow(GULLogInfo(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogInfo(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(GULLogDebug(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogDebug(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); } -- (void)testEnableSTDERR { - [[self.loggerSystemMock expect] printToSTDERR]; - GULLoggerEnableSTDERR(); - [self.loggerSystemMock verify]; +// asl_set_filter does not perform as expected in unit test environment with simulator. The +// following test only checks whether the logs have been sent to system with the default settings in +// the unit test environment. +- (void)testSystemLogWithDefaultStatus { +#if !(BUG128) // Disable until https://github.com/firebase/firebase-ios-sdk/issues/128 is fixed + // Test fails on device and iOS 9 simulators - b/38130372 + return; +#else + // Sets the time interval that we need to wait in order to fetch all the logs. + NSTimeInterval timeInterval = 0.1f; + // Generates a random string each time and check whether it has been logged. + // Log messages with Notice level and below should be logged to system/device by default. + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogError(@"my service", NO, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogWarning(@"my service", kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogNotice(@"my service", kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + // Log messages with Info level and above should NOT be logged to system/device by default. + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogInfo(@"my service", kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertFalse([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogDebug(@"my service", kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertFalse([self logExists]); +#endif } -- (void)testSetLoggerLevel { - [[self.loggerSystemMock expect] setLogLevel:kLogLevel]; - GULSetLoggerLevel(kLogLevel); - [self.loggerSystemMock verify]; +// The GULLoggerLevel enum must match the ASL_LEVEL_* constants, but we manually redefine +// them in GULLoggerLevel.h since we cannot include (see b/34976089 for more details). +// This test ensures the constants match. +- (void)testGULLoggerLevelValues { + XCTAssertEqual(GULLoggerLevelError, ASL_LEVEL_ERR); + XCTAssertEqual(GULLoggerLevelWarning, ASL_LEVEL_WARNING); + XCTAssertEqual(GULLoggerLevelNotice, ASL_LEVEL_NOTICE); + XCTAssertEqual(GULLoggerLevelInfo, ASL_LEVEL_INFO); + XCTAssertEqual(GULLoggerLevelDebug, ASL_LEVEL_DEBUG); } -- (void)testIsLoggableLevel { - [[self.loggerSystemMock expect] isLoggableLevel:kLogLevel]; - GULIsLoggableLevel(kLogLevel); - [self.loggerSystemMock verify]; +// Helper functions. +- (BOOL)logExists { + [self drainGULClientQueue]; + NSString *correctMsg = + [NSString stringWithFormat:@"%@[%@] %@", @"my service", kMessageCode, self.randomLogString]; + return [self messageWasLogged:correctMsg]; } -- (void)testRegisterVersion { - [[self.loggerSystemMock expect] setVersion:kVersionString]; - GULLoggerRegisterVersion(kVersionChar); - [self.loggerSystemMock verify]; +- (void)drainGULClientQueue { + dispatch_semaphore_t workerSemaphore = dispatch_semaphore_create(0); + dispatch_async(getGULClientQueue(), ^{ + dispatch_semaphore_signal(workerSemaphore); + }); + dispatch_semaphore_wait(workerSemaphore, DISPATCH_TIME_FOREVER); } -// TODO(bstpierre): Test that LogBasic calls are piped through. OCMock does not currently support -// the mocking of methods with variadic parameters: https://github.com/erikdoe/ocmock/issues/191 +- (BOOL)messageWasLogged:(NSString *)message { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + aslmsg query = asl_new(ASL_TYPE_QUERY); + asl_set_query(query, ASL_KEY_FACILITY, kGULLoggerASLClientFacilityName, ASL_QUERY_OP_EQUAL); + aslresponse r = asl_search(getGULLoggerClient(), query); + asl_free(query); + aslmsg m; + const char *val; + NSMutableArray *allMsg = [[NSMutableArray alloc] init]; + while ((m = asl_next(r)) != NULL) { + val = asl_get(m, ASL_KEY_MSG); + if (val) { + [allMsg addObject:[NSString stringWithUTF8String:val]]; + } + } + asl_free(m); + asl_release(r); + return [allMsg containsObject:message]; +#pragma clang pop +} @end +#endif diff --git a/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m deleted file mode 100644 index 829c3d3859c..00000000000 --- a/GoogleUtilities/Example/Tests/Logger/GULOSLoggerTest.m +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2019 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 "GULOSLogger.h" - -#import -#import -#import - -#import - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -static NSString *const kService = @"my service"; -static NSTimeInterval const kTimeout = 1.0; - -// Expectation that contains the information needed to see if the correct parameters were used in an -// os_log_with_type call. -@interface GULOSLoggerExpectation : XCTestExpectation - -@property(nonatomic, nullable) os_log_t log; -@property(nonatomic) os_log_type_t type; -@property(nonatomic) NSString *message; - -- (instancetype)initWithLog:(nullable os_log_t)log - type:(os_log_type_t)type - message:(NSString *)message; -@end - -@implementation GULOSLoggerExpectation -- (instancetype)initWithLog:(nullable os_log_t)log - type:(os_log_type_t)type - message:(NSString *)message { - self = [super - initWithDescription:[NSString - stringWithFormat:@"os_log_with_type(%@, %iu, %@) was not called.", - log, type, message]]; - if (self) { - _log = log; - _type = type; - _message = message; - } - return self; -} -@end - -// List of expectations that may be fulfilled in the current test. -static NSMutableArray *sExpectations; - -// Function that will be called by GULOSLogger instead of os_log_with_type. -void GULTestOSLogWithType(os_log_t log, os_log_type_t type, char *s, ...) { - // Grab the first variable argument. - va_list args; - va_start(args, s); - NSString *message = [NSString stringWithUTF8String:va_arg(args, char *)]; - va_end(args); - - // Look for an expectation that meets these parameters. - for (GULOSLoggerExpectation *expectation in sExpectations) { - if ((expectation.log == nil || expectation.log == log) && expectation.type == type && - [message containsString:expectation.message]) { - [expectation fulfill]; - return; // Only fulfill one expectation per call. - } - } -} - -#pragma mark - - -// Redefine class property as readwrite for testing. -@interface GULLogger (ForTesting) -@property(nonatomic, nullable, class, readwrite) id logger; -@end - -// Surface osLog and dispatchQueues for tests. -@interface GULOSLogger (ForTesting) -@property(nonatomic) NSMutableDictionary *categoryLoggers; -@property(nonatomic) dispatch_queue_t dispatchQueue; -@property(nonatomic, unsafe_unretained) void (*logFunction)(os_log_t, os_log_type_t, char *, ...); -@end - -#pragma mark - - -@interface GULOSLoggerTest : XCTestCase -@property(nonatomic, nullable) GULOSLogger *osLogger; -@property(nonatomic, nullable) id mock; -@property(nonatomic) BOOL appStoreWasSwizzled; -@end - -@implementation GULOSLoggerTest - -- (void)setAppStoreTo:(BOOL)fromAppStore { - [GULSwizzler swizzleClass:[GULAppEnvironmentUtil class] - selector:@selector(isFromAppStore) - isClassSelector:YES - withBlock:^BOOL() { - return fromAppStore; - }]; - self.appStoreWasSwizzled = YES; -} - -- (void)partialMockLogger { - // Add the ability to intercept calls to the instance under test - self.mock = OCMPartialMock(self.osLogger); - GULLogger.logger = self.mock; -} - -- (void)setUp { - [super setUp]; - // Setup globals and create the instance under test. - sExpectations = [[NSMutableArray alloc] init]; - self.osLogger = [[GULOSLogger alloc] init]; - self.osLogger.logFunction = &GULTestOSLogWithType; -} - -- (void)tearDown { - // Clear globals - sExpectations = nil; - GULLogger.logger = nil; - [self.mock stopMocking]; - self.mock = nil; - if (self.appStoreWasSwizzled) { - [GULSwizzler unswizzleClass:[GULAppEnvironmentUtil class] - selector:@selector(isFromAppStore) - isClassSelector:YES]; - self.appStoreWasSwizzled = NO; - } - [super tearDown]; -} - -#pragma mark Tests - -- (void)testInit { - // First, there are no loggers created. - XCTAssertNil(self.osLogger.categoryLoggers); - - // After initializeLogger, there should be an empty dictionary ready, for loggers. - [self.osLogger initializeLogger]; - NSDictionary *loggers = self.osLogger.categoryLoggers; - XCTAssertNotNil(loggers); - - // Calling initializeLogger logger again, should change the dictionary instance. - [self.osLogger initializeLogger]; - XCTAssertEqual(loggers, self.osLogger.categoryLoggers); -} - -- (void)testSetLogLevelValid { - // Setting the log level to something valid should not result in an error message. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-security" - OCMReject([self.mock logWithLevel:GULLoggerLevelError - withService:OCMOCK_ANY - isForced:NO - withMessage:OCMOCK_ANY]); -#pragma clang diagnostic pop - self.osLogger.logLevel = GULLoggerLevelWarning; - OCMVerifyAll(self.mock); -} - -- (void)testSetLogLevelInvalid { - // The logger should log an error for invalid levels. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-security" - OCMExpect([[self.mock stub] logWithLevel:GULLoggerLevelError - withService:OCMOCK_ANY - isForced:YES - withMessage:OCMOCK_ANY]); - self.osLogger.logLevel = GULLoggerLevelMin - 1; - - OCMExpect([[self.mock stub] logWithLevel:GULLoggerLevelError - withService:OCMOCK_ANY - isForced:YES - withMessage:OCMOCK_ANY]); -#pragma clang diagnostic push - self.osLogger.logLevel = GULLoggerLevelMax + 1; - OCMVerifyAll(self.mock); -} - -- (void)testLogLevelAppStore { - // When not from the App Store, all log levels should be allowed. - [self setAppStoreTo:NO]; - self.osLogger.logLevel = GULLoggerLevelMin; - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelMin); - self.osLogger.logLevel = GULLoggerLevelMax; - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelMax); - - // When from the App store, levels that are Notice or above, should be silently ignored. - [self setAppStoreTo:YES]; - self.osLogger.logLevel = GULLoggerLevelError; - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelError); - self.osLogger.logLevel = GULLoggerLevelWarning; - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelWarning); - self.osLogger.logLevel = GULLoggerLevelNotice; - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelWarning); - self.osLogger.logLevel = GULLoggerLevelInfo; - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelWarning); - self.osLogger.logLevel = GULLoggerLevelDebug; - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelWarning); -} - -- (void)testForceDebug { - [self partialMockLogger]; - [self setAppStoreTo:NO]; - XCTAssertFalse(self.osLogger.forcedDebug); - GULLoggerForceDebug(); - XCTAssertTrue(self.osLogger.forcedDebug); - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelDebug); -} - -- (void)testForceDebugAppStore { - [self partialMockLogger]; - [self setAppStoreTo:YES]; - self.osLogger.logLevel = GULLoggerLevelWarning; - XCTAssertFalse(self.osLogger.forcedDebug); - GULLoggerForceDebug(); - XCTAssertFalse(self.osLogger.forcedDebug); - XCTAssertEqual(self.osLogger.logLevel, GULLoggerLevelWarning); -} - -- (void)testLoggingValidNoVarArgs { - [self.osLogger initializeLogger]; - XCTAssert(self.osLogger.categoryLoggers.count == 0); - NSString *message = [NSUUID UUID].UUIDString; - GULOSLoggerExpectation *expectation = - [[GULOSLoggerExpectation alloc] initWithLog:nil type:OS_LOG_TYPE_DEFAULT message:message]; - [sExpectations addObject:expectation]; - [self.osLogger logWithLevel:GULLoggerLevelNotice - withService:kService - isForced:NO - withMessage:message]; - [self waitForExpectations:sExpectations timeout:kTimeout]; -} - -- (void)testLoggingValidWithVarArgs { - [self.osLogger initializeLogger]; - XCTAssert(self.osLogger.categoryLoggers.count == 0); - NSString *message = [NSUUID UUID].UUIDString; - GULOSLoggerExpectation *expectation = - [[GULOSLoggerExpectation alloc] initWithLog:nil type:OS_LOG_TYPE_DEFAULT message:message]; - [sExpectations addObject:expectation]; - [self.osLogger logWithLevel:GULLoggerLevelNotice - withService:kService - isForced:NO - withMessage:message]; - [self waitForExpectations:sExpectations timeout:kTimeout]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m b/GoogleUtilities/Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m index cbe4faa9939..e340a576c86 100644 --- a/GoogleUtilities/Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m +++ b/GoogleUtilities/Example/Tests/Swizzler/GULRuntimeClassSnapshotTests.m @@ -47,6 +47,11 @@ @interface GULRuntimeClassSnapshotTests : XCTestCase @implementation GULRuntimeClassSnapshotTests +/** Tests the assurance that init throws. */ +- (void)testInitThrows { + XCTAssertThrows([GULRuntimeClassSnapshot new]); +} + /** Tests initialization. */ - (void)testInitWithClass { Class NSObjectClass = [NSObject class]; diff --git a/GoogleUtilities/Logger/GULASLLogger.h b/GoogleUtilities/Logger/GULASLLogger.h deleted file mode 100644 index d7a0bc563fd..00000000000 --- a/GoogleUtilities/Logger/GULASLLogger.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2019 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 - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GULASLLogger : NSObject -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Logger/GULASLLogger.m b/GoogleUtilities/Logger/GULASLLogger.m deleted file mode 100644 index d900d58b835..00000000000 --- a/GoogleUtilities/Logger/GULASLLogger.m +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2019 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 "GULASLLogger.h" - -#import - -#import "GULAppEnvironmentUtil.h" -#import "GULLogger+Internal.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface GULASLLogger () { - GULLoggerLevel _logLevel; -} - -@property(nonatomic) aslclient aslClient; -@property(nonatomic) dispatch_queue_t dispatchQueue; - -@end - -@implementation GULASLLogger - -@synthesize forcedDebug = _forcedDebug; -@synthesize logLevel = _logLevel; -@synthesize version = _version; - -- (instancetype)init { - self = [super init]; - if (self) { - _forcedDebug = NO; - _logLevel = GULLoggerLevelNotice; - _version = @""; - _dispatchQueue = dispatch_queue_create("GULLoggerQueue", DISPATCH_QUEUE_SERIAL); - dispatch_set_target_queue(_dispatchQueue, - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); - } - return self; -} - -- (void)initializeLogger { - dispatch_sync(self.dispatchQueue, ^{ - if (!self.aslClient) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" // asl is deprecated - self.aslClient = asl_open(NULL, kGULLoggerClientFacilityName, ASL_OPT_STDERR); - asl_set_filter(self.aslClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE)); -#pragma clang diagnostic pop - } - }); -} - -- (void)dealloc { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" // asl is deprecated - asl_free(self.aslClient); -#pragma clang diagnostic pop -} - -- (void)setLogLevel:(GULLoggerLevel)logLevel { - if (logLevel < GULLoggerLevelMin || logLevel > GULLoggerLevelMax) { - GULLogError(kGULLoggerName, YES, kGULLoggerInvalidLoggerLevelCore, - kGULLoggerInvalidLoggerLevelMessage, (long)logLevel); - } - - // We should not raise the logger level if we are running from App Store. - if (logLevel >= GULLoggerLevelNotice && [GULAppEnvironmentUtil isFromAppStore]) { - return; - } - - // Ignore setting the level if forcedDebug is on. - if (self.forcedDebug) { - return; - } - - _logLevel = logLevel; -} - -- (GULLoggerLevel)logLevel { - return _logLevel; -} - -- (void)setForcedDebug:(BOOL)forcedDebug { - // We should not enable debug mode if we're running from App Store. - if (![GULAppEnvironmentUtil isFromAppStore]) { - if (forcedDebug) { - self.logLevel = GULLoggerLevelDebug; - } - _forcedDebug = forcedDebug; - } -} - -- (BOOL)forcedDebug { - return _forcedDebug; -} - -- (BOOL)isLoggableLevel:(GULLoggerLevel)logLevel { - return [GULLogger loggerSystem:self shouldLogMessageOfLevel:logLevel]; -} - -- (void)printToSTDERR { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" // asl is deprecated - asl_add_log_file(self.aslClient, STDERR_FILENO); -#pragma clang diagnostic pop -} - -- (void)logWithLevel:(GULLoggerLevel)level - withService:(GULLoggerService)__unused service - isForced:(BOOL)forced - withMessage:(NSString *)message { - // Skip logging this if the level isn't to be logged unless it's forced. - if (![self isLoggableLevel:level] && !forced) { - return; - } - [self initializeLogger]; - dispatch_async(self.dispatchQueue, ^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" // asl is deprecated - asl_log(self.aslClient, NULL, (int)level, "%s", message.UTF8String); -#pragma clang diagnostic pop - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Logger/GULLogger+Internal.h b/GoogleUtilities/Logger/GULLogger+Internal.h deleted file mode 100644 index ce1b12b1cb7..00000000000 --- a/GoogleUtilities/Logger/GULLogger+Internal.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019 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 "GULLogger.h" - -#import "GULLoggerSystem.h" - -NS_ASSUME_NONNULL_BEGIN - -static NSString *const kGULLoggerInvalidLoggerLevelCore = @"I-COR000023"; -static NSString *const kGULLoggerInvalidLoggerLevelMessage = @"Invalid logger level, %ld"; -static GULLoggerService const kGULLoggerName = @"[GULLogger]"; -static const char *const kGULLoggerClientFacilityName = "com.google.utilities.logger"; - -@interface GULLogger (Internal) - -/** - * Checks to see if the given logger system would log a message of a given level. - * - * @param logger The logger that may be logging a message. - * @param logLevel The level of the message that may be logged. - * @return YES if the given logger should be logging a message of the given level. - */ -+ (BOOL)loggerSystem:(id)logger shouldLogMessageOfLevel:(GULLoggerLevel)logLevel; - -/** - * Formats the given message, code and service for output by the given logger system. - * - * @param logger The logger which will output this message - * @param service The service sending the message to the logger - * @param code The code for this message - * @param message The log message, optionally a format string - * @return A completed string, ready to be output by a logger system. - */ -+ (NSString *)messageFromLogger:(id)logger - withService:(GULLoggerService)service - code:(NSString *)code - message:(NSString *)message; - -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index f0588b9de06..495e5830bb0 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -12,95 +12,134 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import "GULLogger.h" +#import "Private/GULLogger.h" -#import "GULASLLogger.h" -#import "GULAppEnvironmentUtil.h" -#import "GULLogger+Internal.h" -#import "GULLoggerLevel.h" -#import "GULOSLogger.h" +#include -#if TARGET_OS_IOS -#import -#endif +#import +#import "Public/GULLoggerLevel.h" + +/// ASL client facility name used by GULLogger. +const char *kGULLoggerASLClientFacilityName = "com.google.utilities.logger"; + +static dispatch_once_t sGULLoggerOnceToken; + +static aslclient sGULLoggerClient; + +static dispatch_queue_t sGULClientQueue; + +static BOOL sGULLoggerDebugMode; + +static GULLoggerLevel sGULLoggerMaximumLevel; + +// Allow clients to register a version to include in the log. +static const char *sVersion = ""; + +static GULLoggerService kGULLoggerLogger = @"[GULLogger]"; #ifdef DEBUG /// The regex pattern for the message code. -NSRegularExpression *GULMessageCodeRegex() { - static dispatch_once_t onceToken; - static NSRegularExpression *messageCodeRegex; - dispatch_once(&onceToken, ^{ - messageCodeRegex = [NSRegularExpression regularExpressionWithPattern:@"^I-[A-Z]{3}[0-9]{6}$" - options:0 - error:NULL]; - }); - return messageCodeRegex; -} - -// GULResetLogger is unused but depended on by FirebaseCore versions 5.3.x and earlier. -// It cannot be removed until GoogleUtilities does a breaking change release since FirebaseCore -// allows GoogleUtilities to float up to the highest 5.x version. -void GULResetLogger() { -} +static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$"; +static NSRegularExpression *sMessageCodeRegex; #endif -@implementation GULLogger (Internal) +void GULLoggerInitializeASL(void) { + dispatch_once(&sGULLoggerOnceToken, ^{ + NSInteger majorOSVersion = [[GULAppEnvironmentUtil systemVersion] integerValue]; + uint32_t aslOptions = ASL_OPT_STDERR; +#if TARGET_OS_SIMULATOR + // The iOS 11 simulator doesn't need the ASL_OPT_STDERR flag. + if (majorOSVersion >= 11) { + aslOptions = 0; + } +#else + // Devices running iOS 10 or higher don't need the ASL_OPT_STDERR flag. + if (majorOSVersion >= 10) { + aslOptions = 0; + } +#endif // TARGET_OS_SIMULATOR -+ (BOOL)loggerSystem:(id)logger shouldLogMessageOfLevel:(GULLoggerLevel)logLevel { - if (logger.forcedDebug) { - return YES; - } else if (logLevel < GULLoggerLevelMin || logLevel > GULLoggerLevelMax) { - return NO; - } - return logLevel <= logger.logLevel; -} +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // asl is deprecated + // Initialize the ASL client handle. + sGULLoggerClient = asl_open(NULL, kGULLoggerASLClientFacilityName, aslOptions); + sGULLoggerMaximumLevel = GULLoggerLevelNotice; -+ (NSString *)messageFromLogger:(id)logger - withService:(GULLoggerService)service - code:(NSString *)code - message:(NSString *)message { + // Set the filter used by system/device log. Initialize in default mode. + asl_set_filter(sGULLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE)); + + sGULClientQueue = dispatch_queue_create("GULLoggingClientQueue", DISPATCH_QUEUE_SERIAL); + dispatch_set_target_queue(sGULClientQueue, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); #ifdef DEBUG - NSCAssert(code.length == 11, @"Incorrect message code length."); - NSRegularExpression *messageCodeRegex = GULMessageCodeRegex(); - NSRange messageCodeRange = NSMakeRange(0, code.length); - NSUInteger numberOfMatches = [messageCodeRegex numberOfMatchesInString:code - options:0 - range:messageCodeRange]; - NSCAssert(numberOfMatches == 1, @"Incorrect message code format."); + sMessageCodeRegex = [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern + options:0 + error:NULL]; #endif - return [NSString stringWithFormat:@"%@ - %@[%@] %@", logger.version, service, code, message]; + }); } -@end +void GULLoggerEnableSTDERR(void) { + asl_add_log_file(sGULLoggerClient, STDERR_FILENO); +} -static id sGULLogger; +void GULLoggerForceDebug(void) { + // We should enable debug mode if we're not running from App Store. + if (![GULAppEnvironmentUtil isFromAppStore]) { + sGULLoggerDebugMode = YES; + GULSetLoggerLevel(GULLoggerLevelDebug); + } +} -void GULLoggerInitialize(void) { - [GULLogger.logger initializeLogger]; +__attribute__((no_sanitize("thread"))) void GULSetLoggerLevel(GULLoggerLevel loggerLevel) { + if (loggerLevel < GULLoggerLevelMin || loggerLevel > GULLoggerLevelMax) { + GULLogError(kGULLoggerLogger, NO, @"I-COR000023", @"Invalid logger level, %ld", + (long)loggerLevel); + return; + } + GULLoggerInitializeASL(); + // We should not raise the logger level if we are running from App Store. + if (loggerLevel >= GULLoggerLevelNotice && [GULAppEnvironmentUtil isFromAppStore]) { + return; + } + + sGULLoggerMaximumLevel = loggerLevel; + dispatch_async(sGULClientQueue, ^{ + asl_set_filter(sGULLoggerClient, ASL_FILTER_MASK_UPTO(loggerLevel)); + }); } -void GULLoggerInitializeASL(void) { - GULLoggerInitialize(); +/** + * Check if the level is high enough to be loggable. + */ +__attribute__((no_sanitize("thread"))) BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel) { + GULLoggerInitializeASL(); + if (sGULLoggerDebugMode) { + return YES; + } + return (BOOL)(loggerLevel <= sGULLoggerMaximumLevel); } -void GULLoggerEnableSTDERR(void) { - [GULLogger.logger printToSTDERR]; +#ifdef DEBUG +void GULResetLogger() { + sGULLoggerOnceToken = 0; } -void GULLoggerForceDebug(void) { - GULLogger.logger.forcedDebug = YES; +aslclient getGULLoggerClient() { + return sGULLoggerClient; } -void GULSetLoggerLevel(GULLoggerLevel loggerLevel) { - GULLogger.logger.logLevel = loggerLevel; +dispatch_queue_t getGULClientQueue() { + return sGULClientQueue; } -BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel) { - return [GULLogger.logger isLoggableLevel:loggerLevel]; +BOOL getGULLoggerDebugMode() { + return sGULLoggerDebugMode; } +#endif void GULLoggerRegisterVersion(const char *version) { - GULLogger.logger.version = [NSString stringWithUTF8String:version]; + sVersion = version; } void GULLogBasic(GULLoggerLevel level, @@ -109,23 +148,26 @@ void GULLogBasic(GULLoggerLevel level, NSString *messageCode, NSString *message, va_list args_ptr) { + GULLoggerInitializeASL(); + if (!(level <= sGULLoggerMaximumLevel || sGULLoggerDebugMode || forceLog)) { + return; + } + #ifdef DEBUG - NSRegularExpression *messageCodeRegex = GULMessageCodeRegex(); NSCAssert(messageCode.length == 11, @"Incorrect message code length."); NSRange messageCodeRange = NSMakeRange(0, messageCode.length); - NSUInteger numberOfMatches = [messageCodeRegex numberOfMatchesInString:messageCode - options:0 - range:messageCodeRange]; + NSUInteger numberOfMatches = [sMessageCodeRegex numberOfMatchesInString:messageCode + options:0 + range:messageCodeRange]; NSCAssert(numberOfMatches == 1, @"Incorrect message code format."); #endif NSString *logMsg = [[NSString alloc] initWithFormat:message arguments:args_ptr]; - NSString *formattedMsg = - [NSString stringWithFormat:@"%@ - [%@] %@", GULLogger.logger.version, messageCode, logMsg]; - [GULLogger.logger logWithLevel:level - withService:service - isForced:forceLog - withMessage:formattedMsg]; + logMsg = [NSString stringWithFormat:@"%s - %@[%@] %@", sVersion, service, messageCode, logMsg]; + dispatch_async(sGULClientQueue, ^{ + asl_log(sGULLoggerClient, NULL, level, "%s", logMsg.UTF8String); + }); } +#pragma clang diagnostic pop /** * Generates the logging functions using macros. @@ -152,39 +194,16 @@ void GULLogBasic(GULLoggerLevel level, #undef GUL_MAKE_LOGGER -// Redefine logger property as readwrite as a form of dependency injection. -@interface GULLogger () -@property(nonatomic, nullable, class, readwrite) id logger; -@end +#pragma mark - GULLoggerWrapper -@implementation GULLogger - -+ (id)logger { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // Synchronize here to avoid undefined behaviour if a setLogger: call happened before the - // first get. - @synchronized(self) { - if (!sGULLogger) { -#if __has_builtin(__builtin_available) - if (@available(iOS 9.0, *)) { -#else - if ([[UIDevice currentDevice].systemVersion integerValue] >= 9) { -#endif - sGULLogger = [[GULOSLogger alloc] init]; - } else { - sGULLogger = [[GULASLLogger alloc] init]; - } - } - } - }); - return sGULLogger; -} +@implementation GULLoggerWrapper -+ (void)setLogger:(nullable id)logger { - @synchronized(self) { - sGULLogger = logger; - } ++ (void)logWithLevel:(GULLoggerLevel)level + withService:(GULLoggerService)service + withCode:(NSString *)messageCode + withMessage:(NSString *)message + withArgs:(va_list)args { + GULLogBasic(level, service, NO, messageCode, message, args); } @end diff --git a/GoogleUtilities/Logger/GULOSLogger.h b/GoogleUtilities/Logger/GULOSLogger.h deleted file mode 100644 index 3a3d0962597..00000000000 --- a/GoogleUtilities/Logger/GULOSLogger.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2019 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 - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GULOSLogger : NSObject -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Logger/GULOSLogger.m b/GoogleUtilities/Logger/GULOSLogger.m deleted file mode 100644 index 39b6061d073..00000000000 --- a/GoogleUtilities/Logger/GULOSLogger.m +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2019 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 "GULOSLogger.h" - -#import - -#import "GULAppEnvironmentUtil.h" -#import "GULLogger+Internal.h" -#import "GULLoggerLevel.h" - -#if TARGET_OS_IOS -#import -#endif - -NS_ASSUME_NONNULL_BEGIN - -// Function which calls the macro so that this can be substituted for testing. -// Since the macro enforces built-in constant-ness of the format string, it is replaced by "s" -// and the va_list should only contain one argument, a full message with format substitutions -// already filled. -static void GULLOSLogWithType(os_log_t log, os_log_type_t type, char *format, ...) { -#if __has_builtin(__builtin_available) - if (@available(iOS 9.0, *)) { -#else - if ([[UIDevice currentDevice].systemVersion integerValue] >= 9) { -#endif - va_list args; - va_start(args, format); - NSString *formattedString = - [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:format] arguments:args]; -#if TARGET_OS_OSX - // Silence macOS 10.10 warning until we move minimum to 10.11. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability" - os_log_with_type(log, type, "%s", [formattedString UTF8String]); -#pragma clang diagnostic pop -#else - os_log_with_type(log, type, "%s", [formattedString UTF8String]); -#endif - va_end(args); - } else { -#ifdef DEBUG - NSCAssert(NO, @"Attempting to use os_log on iOS version prior to 9."); -#endif - } -} - -@interface GULOSLogger () - -// Dictionary of Category -> os_log instances. -@property(nonatomic) NSMutableDictionary *categoryLoggers; - -// The dispatch queue used to asynchronously call to os_log. -@property(nonatomic) dispatch_queue_t dispatchQueue; - -// This property is a function pointer to the method that logs messages to os_log. -// This indirection allows us to inject a different function pointer for dependency injection. -@property(nonatomic, unsafe_unretained) void (*logFunction)(os_log_t, os_log_type_t, char *, ...); - -@end - -@implementation GULOSLogger - -// Auto-synthesis not available for these since they are defined in the protocol. -@synthesize forcedDebug = _forcedDebug; -@synthesize logLevel = _logLevel; -@synthesize version = _version; - -- (instancetype)init { - self = [super init]; - if (self) { - _forcedDebug = NO; - _logLevel = // When running from an App Store build, avoid noisy levels below Warning. - [GULAppEnvironmentUtil isFromAppStore] ? GULLoggerLevelWarning : GULLoggerLevelNotice; - _version = @""; - _dispatchQueue = dispatch_queue_create("GULLoggerQueue", DISPATCH_QUEUE_SERIAL); - _logFunction = &GULLOSLogWithType; - dispatch_set_target_queue(_dispatchQueue, - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); - } - return self; -} - -- (void)initializeLogger { - dispatch_sync(self.dispatchQueue, ^{ - if (!self.categoryLoggers) { - self.categoryLoggers = [[NSMutableDictionary alloc] init]; - } - }); -} - -- (void)setLogLevel:(GULLoggerLevel)logLevel { - if (logLevel < GULLoggerLevelMin || logLevel > GULLoggerLevelMax) { - GULLogError(kGULLoggerName, YES, kGULLoggerInvalidLoggerLevelCore, - kGULLoggerInvalidLoggerLevelMessage, (long)logLevel); - } - - // We should not raise the logger level if we are running from App Store. - if (logLevel >= GULLoggerLevelNotice && [GULAppEnvironmentUtil isFromAppStore]) { - return; - } - - // Ignore setting the level if forcedDebug is on. - if (self.forcedDebug) { - return; - } - - _logLevel = logLevel; -} - -- (GULLoggerLevel)logLevel { - return _logLevel; -} - -- (void)setForcedDebug:(BOOL)forcedDebug { - // We should not enable debug mode if we're running from App Store. - if (![GULAppEnvironmentUtil isFromAppStore]) { - if (forcedDebug) { - self.logLevel = GULLoggerLevelDebug; - } - _forcedDebug = forcedDebug; - } -} - -- (BOOL)forcedDebug { - return _forcedDebug; -} - -- (void)printToSTDERR { - // NO-OP - os_log always outputs to STDERR and cannot be turned off. - // See http://www.openradar.me/36919139 -} - -- (BOOL)isLoggableLevel:(GULLoggerLevel)logLevel { - return [GULLogger loggerSystem:self shouldLogMessageOfLevel:logLevel]; -} - -- (void)logWithLevel:(GULLoggerLevel)level - withService:(GULLoggerService)service - isForced:(BOOL)forced - withMessage:(NSString *)message { - // Skip logging this if the level isn't to be logged unless it's forced. - if (![self isLoggableLevel:level] && !forced) { - return; - } - [self initializeLogger]; - - // Avoid blocking during logging. - dispatch_async(self.dispatchQueue, ^{ - os_log_t osLog = self.categoryLoggers[service]; - if (!osLog) { -#if __has_builtin(__builtin_available) - if (@available(iOS 9.0, *)) { -#else - if ([[UIDevice currentDevice].systemVersion integerValue] >= 9) { -#endif -#if TARGET_OS_OSX - // Silence macOS 10.10 warning until we move minimum to 10.11. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability" - osLog = os_log_create(kGULLoggerClientFacilityName, service.UTF8String); -#pragma clang diagnostic pop -#else - osLog = os_log_create(kGULLoggerClientFacilityName, service.UTF8String); -#endif - self.categoryLoggers[service] = osLog; - } else { -#ifdef DEBUG - NSCAssert(NO, @"Attempting to use os_log on iOS version prior to 9."); -#endif - } - } - // Call the function pointer using the message constructed by GULLogger. - (*self.logFunction)(osLog, [[self class] osLogTypeForGULLoggerLevel:level], "%s", - message.UTF8String); - }); -} - -+ (os_log_type_t)osLogTypeForGULLoggerLevel:(GULLoggerLevel)level { - switch (level) { - case GULLoggerLevelDebug: - return OS_LOG_TYPE_DEBUG; - case GULLoggerLevelInfo: - return OS_LOG_TYPE_INFO; - case GULLoggerLevelNotice: - case GULLoggerLevelWarning: - // Both Notice and Warning map to the os_log default level. - return OS_LOG_TYPE_DEFAULT; - case GULLoggerLevelError: - return OS_LOG_TYPE_ERROR; - default: - return OS_LOG_TYPE_DEFAULT; - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Logger/Private/GULLogger.h b/GoogleUtilities/Logger/Private/GULLogger.h index c7bc7e30c87..ff425768681 100644 --- a/GoogleUtilities/Logger/Private/GULLogger.h +++ b/GoogleUtilities/Logger/Private/GULLogger.h @@ -16,27 +16,21 @@ #import -#import "GULLoggerSystem.h" +#import NS_ASSUME_NONNULL_BEGIN +/** + * The services used in the logger. + */ +typedef NSString *const GULLoggerService; + #ifdef __cplusplus extern "C" { #endif // __cplusplus -/** - * Initialize the default GULLogger. - * - * @discussion On iOS 9 and earlier. GULLogger will use ASL. For iOS 10 and later, os_log is used. - */ -extern void GULLoggerInitialize(void); - /** * Initialize GULLogger. - * - * @discussion This version should no longer be used. ASL is deprecated by Apple to be replaced by - * os_log. Calls to this function are redirected to GULLoggerInitialize to ensure - * functionality on all iOS versions. */ extern void GULLoggerInitializeASL(void); @@ -53,34 +47,34 @@ extern void GULLoggerEnableSTDERR(void); /** * Changes the default logging level of GULLoggerLevelNotice to a user-specified level. * The default level cannot be set above GULLoggerLevelNotice if the app is running from App Store. - * @param loggerLevel Log level (one of the GULLoggerLevel enum values). + * (required) log level (one of the GULLoggerLevel enum values). */ extern void GULSetLoggerLevel(GULLoggerLevel loggerLevel); /** * Checks if the specified logger level is loggable given the current settings. - * @param loggerLevel Log level (one of the GULLoggerLevel enum values). + * (required) log level (one of the GULLoggerLevel enum values). */ extern BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel); /** * Register version to include in logs. - * @param version The version to register with the logger. + * (required) version */ extern void GULLoggerRegisterVersion(const char *version); /** * Logs a message to the Xcode console and the device log. If running from AppStore, will * not log any messages with a level higher than GULLoggerLevelNotice to avoid log spamming. - * @param level Log level (one of the GULLoggerLevel enum values). - * @param service Service name of type GULLoggerService. - * @param forceLog If this message should be output regardless of its level. - * @param messageCode starting with "I-" which means iOS, followed by a capitalized + * (required) log level (one of the GULLoggerLevel enum values). + * (required) service name of type GULLoggerService. + * (required) message code starting with "I-" which means iOS, followed by a capitalized * three-character service identifier and a six digit integer message ID that is unique * within the service. * An example of the message code is @"I-COR000001". - * @param message string which can be a format string. - * @param args_ptr the list of arguments to substitute into the format string. + * (required) message string which can be a format string. + * (optional) variable arguments list obtained from calling va_start, used when message is a format + * string. */ extern void GULLogBasic(GULLoggerLevel level, GULLoggerService service, @@ -98,16 +92,14 @@ extern void GULLogBasic(GULLoggerLevel level, /** * The following functions accept the following parameters in order: - * @param service Name of type GULLoggerService. - * @param messageCode Starting from "I-" which means iOS, followed by a capitalized + * (required) service name of type GULLoggerService. + * (required) message code starting from "I-" which means iOS, followed by a capitalized * three-character service identifier and a six digit integer message ID that is unique * within the service. * An example of the message code is @"I-COR000001". * See go/firebase-log-proposal for details. - * @param message String which can be a format string. - * @param ... The list of arguments to substitute into the format string. - * - * @discussion + * (required) message string which can be a format string. + * (optional) the list of arguments to substitute into the format string. * Example usage: * GULLogError(kGULLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name); */ @@ -141,10 +133,26 @@ extern void GULLogDebug(GULLoggerService service, } // extern "C" #endif // __cplusplus -@interface GULLogger : NSObject +@interface GULLoggerWrapper : NSObject + +/** + * Objective-C wrapper for GULLogBasic to allow weak linking to GULLogger + * (required) log level (one of the GULLoggerLevel enum values). + * (required) service name of type GULLoggerService. + * (required) message code starting with "I-" which means iOS, followed by a capitalized + * three-character service identifier and a six digit integer message ID that is unique + * within the service. + * An example of the message code is @"I-COR000001". + * (required) message string which can be a format string. + * (optional) variable arguments list obtained from calling va_start, used when message is a format + * string. + */ -/// The current default logger. -@property(nonatomic, class, readonly) id logger; ++ (void)logWithLevel:(GULLoggerLevel)level + withService:(GULLoggerService)service + withCode:(NSString *)messageCode + withMessage:(NSString *)message + withArgs:(va_list)args; @end diff --git a/GoogleUtilities/Logger/Private/GULLoggerSystem.h b/GoogleUtilities/Logger/Private/GULLoggerSystem.h deleted file mode 100644 index 358faca4ccb..00000000000 --- a/GoogleUtilities/Logger/Private/GULLoggerSystem.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 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 "GULLoggerLevel.h" - -NS_ASSUME_NONNULL_BEGIN - -/// The service name used by the logger. -typedef NSString *const GULLoggerService; - -/// This protocol describes a GoogleUtilities Logger System implementation. -@protocol GULLoggerSystem - -/// The current log level of this logger. Defaults to GULLoggerLevelNotice. -@property(nonatomic) GULLoggerLevel logLevel; - -/// The version to report to the logs. Defaults to the empty string. -@property(nonatomic) NSString *version; - -/// Forces the current log level to be set to debug. Defaults to NO. -@property(nonatomic) BOOL forcedDebug; - -/// Initializes the logger before use. -- (void)initializeLogger; - -/// Enables output to STDERR. Not enabled by default. -- (void)printToSTDERR; - -/// Checks to see if a given level would be logged given the current level of the logger. -- (BOOL)isLoggableLevel:(GULLoggerLevel)logLevel; - -/// Logs the given message. -- (void)logWithLevel:(GULLoggerLevel)level - withService:(GULLoggerService)service - isForced:(BOOL)forced - withMessage:(NSString *)message; -@end - -NS_ASSUME_NONNULL_END diff --git a/GoogleUtilities/Logger/Public/GULLoggerLevel.h b/GoogleUtilities/Logger/Public/GULLoggerLevel.h index e7f5c3bfe5e..81ff212d716 100644 --- a/GoogleUtilities/Logger/Public/GULLoggerLevel.h +++ b/GoogleUtilities/Logger/Public/GULLoggerLevel.h @@ -14,26 +14,22 @@ * limitations under the License. */ -/// The log levels used by internal logging. +/** + * The log levels used by internal logging. + */ typedef NS_ENUM(NSInteger, GULLoggerLevel) { - /// Error level, matches ASL_LEVEL_ERR and is used for OS_LOG_TYPE_ERROR. + /** Error level, matches ASL_LEVEL_ERR. */ GULLoggerLevelError = 3, - - /// Warning level, matches ASL_LEVEL_WARNING and is used for OS_LOG_TYPE_DEFAULT. + /** Warning level, matches ASL_LEVEL_WARNING. */ GULLoggerLevelWarning = 4, - - /// Notice level, matches ASL_LEVEL_NOTICE and is used for OS_LOG_TYPE_DEFAULT. + /** Notice level, matches ASL_LEVEL_NOTICE. */ GULLoggerLevelNotice = 5, - - /// Info level, matches ASL_LEVEL_INFO and is used for OS_LOG_TYPE_INFO. + /** Info level, matches ASL_LEVEL_INFO. */ GULLoggerLevelInfo = 6, - - /// Debug level, matches ASL_LEVEL_DEBUG and is mapped to OS_LOG_TYPE_DEBUG. + /** Debug level, matches ASL_LEVEL_DEBUG. */ GULLoggerLevelDebug = 7, - - /// Minimum log level. + /** Minimum log level. */ GULLoggerLevelMin = GULLoggerLevelError, - - /// Maximum log level. + /** Maximum log level. */ GULLoggerLevelMax = GULLoggerLevelDebug } NS_SWIFT_NAME(GoogleLoggerLevel); From d5e197db3e95b635b8234d0888b654c41dc6d134 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Thu, 14 Mar 2019 19:13:19 -0400 Subject: [PATCH 040/214] Overwrite CoreDiagnostics framework with framework passed in. (#2560) --- .../Sources/ZipBuilder/ZipBuilder.swift | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift b/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift index 30b167a859f..96ace387463 100644 --- a/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift +++ b/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift @@ -209,12 +209,33 @@ struct ZipBuilder { // Create an array that has the Pod name as the key and the array of frameworks needed - this // will be used as the source of truth for all frameworks to be copied in each product's // directory. - let frameworks = filesToInstall.mapValues { $0.frameworks } + var frameworks = filesToInstall.mapValues { $0.frameworks } for (framework, paths) in frameworks { print("Frameworks for pod: \(framework) were compiled at \(paths)") } - // TODO: Overwrite the `CoreDiagnostics.framework` in the generated framework. + // Overwrite the `FirebaseCoreDiagnostics.framework` in the `FirebaseAnalytics` folder. This is + // needed because it was compiled specifically with the `ZIP` bit enabled, helping us understand + // the distribution of CocoaPods vs Zip file integrations. + if subspecsToInstall.contains(.analytics) { + let overriddenAnalytics: [URL] = { + guard let currentFrameworks = frameworks["FirebaseAnalytics"] else { + fatalError("Attempted to replace CoreDiagnostics framework but the FirebaseAnalytics " + + "directory does not exist. Existing directories: \(frameworks.keys)") + } + + // Filter out any CoreDiagnostics directories from the frameworks to install. There should + // only be one. + let withoutDiagnostics: [URL] = currentFrameworks.filter { url in + url.lastPathComponent != "FirebaseCoreDiagnostics.framework" + } + + return withoutDiagnostics + [paths.coreDiagnosticsDir] + }() + + // Set the newly required framework paths for Analytics. + frameworks["FirebaseAnalytics"] = overriddenAnalytics + } // TODO: The folder heirarchy should change in Firebase 6. // Time to assemble the folder structure of the Zip file. In order to get the frameworks From 2ac4f6d4bcc5fa459ce8901482b6c38c57969533 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Fri, 15 Mar 2019 11:38:48 -0400 Subject: [PATCH 041/214] Avoid using NSRegularExpression in FIRApp (b/65122393) (#2529) --- Example/Core/Tests/FIRAppTest.m | 51 +++++----------- Firebase/Core/FIRApp.m | 104 ++++++++++++++++++++------------ 2 files changed, 82 insertions(+), 73 deletions(-) diff --git a/Example/Core/Tests/FIRAppTest.m b/Example/Core/Tests/FIRAppTest.m index c144d6f438b..d688e3a0c88 100644 --- a/Example/Core/Tests/FIRAppTest.m +++ b/Example/Core/Tests/FIRAppTest.m @@ -389,11 +389,11 @@ - (void)testAppIDFormatInvalid { // Some direct tests of the validateAppIDFormat:withVersion: method. // Sanity checks first. NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef"; - NSString *const kGoodVersionV1 = @"1:"; + NSString *const kGoodVersionV1 = @"1"; XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodVersionV1]); NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec"; - NSString *const kGoodVersionV2 = @"2:"; + NSString *const kGoodVersionV2 = @"2"; XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]); // Version mismatch. @@ -440,18 +440,13 @@ - (void)testAppIDFingerprintInvalid { // Some direct tests of the validateAppIDFingerprint:withVersion: method. // Sanity checks first. NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef"; - NSString *const kGoodVersionV1 = @"1:"; + NSString *const kGoodVersionV1 = @"1"; XCTAssertTrue([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV1]); NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec"; - NSString *const kGoodVersionV2 = @"2:"; + NSString *const kGoodVersionV2 = @"2"; XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]); - // Version mismatch. - XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV2 withVersion:kGoodVersionV1]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV2]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@"999:"]); - // Nil or empty strings. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:nil]); XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@""]); @@ -464,35 +459,19 @@ - (void)testAppIDFingerprintInvalid { XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodVersionV1 withVersion:kGoodVersionV1]); // The version is the entire app ID. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodAppIDV1]); - - // Versions digits that may make a partial match. - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"01:1337:ios:deadbeef" - withVersion:kGoodVersionV1]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"10:1337:ios:deadbeef" - withVersion:kGoodVersionV1]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"11:1337:ios:deadbeef" - withVersion:kGoodVersionV1]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"21:1337:ios:5e18052ab54fbfec" - withVersion:kGoodVersionV2]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"22:1337:ios:5e18052ab54fbfec" - withVersion:kGoodVersionV2]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"02:1337:ios:5e18052ab54fbfec" - withVersion:kGoodVersionV2]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"20:1337:ios:5e18052ab54fbfec" - withVersion:kGoodVersionV2]); - // Extra fields. - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"ab:1:1337:ios:deadbeef" - withVersion:kGoodVersionV1]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:ab:1337:ios:deadbeef" - withVersion:kGoodVersionV1]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ab:ios:deadbeef" - withVersion:kGoodVersionV1]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ios:ab:deadbeef" - withVersion:kGoodVersionV1]); - XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ios:deadbeef:ab" - withVersion:kGoodVersionV1]); } +// Uncomment if you need to measure performance of [FIRApp validateAppID:]. +// It is commented because measures are heavily dependent on a build agent configuration, +// so it cannot produce reliable resault on CI +//- (void)testAppIDFingerprintPerfomance { +// [self measureBlock:^{ +// for (NSInteger i = 0; i < 100; ++i) { +// [self testAppIDPrefix]; +// } +// }]; +//} + #pragma mark - Automatic Data Collection Tests - (void)testGlobalDataCollectionNoFlags { diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index 6d82ab0b340..f6f7b97e533 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -575,33 +575,32 @@ + (BOOL)validateAppID:(NSString *)appID { return NO; } - // All app IDs must start with at least ":". - NSString *const versionPattern = @"^\\d+:"; - NSRegularExpression *versionRegex = - [NSRegularExpression regularExpressionWithPattern:versionPattern options:0 error:NULL]; - if (!versionRegex) { + NSScanner *stringScanner = [NSScanner scannerWithString:appID]; + stringScanner.charactersToBeSkipped = nil; + + NSString *appIDVersion; + if (![stringScanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] + intoString:&appIDVersion]) { return NO; } - NSRange appIDRange = NSMakeRange(0, appID.length); - NSArray *versionMatches = [versionRegex matchesInString:appID options:0 range:appIDRange]; - if (versionMatches.count != 1) { + if (![stringScanner scanString:@":" intoString:NULL]) { + // appIDVersion must be separated by ":" return NO; } - NSRange versionRange = [(NSTextCheckingResult *)versionMatches.firstObject range]; - NSString *appIDVersion = [appID substringWithRange:versionRange]; - NSArray *knownVersions = @[ @"1:" ]; + NSArray *knownVersions = @[ @"1" ]; if (![knownVersions containsObject:appIDVersion]) { // Permit unknown yet properly formatted app ID versions. + FIRLogInfo(kFIRLoggerCore, @"I-COR000010", @"Unknown GOOGLE_APP_ID version: %@", appIDVersion); return YES; } - if (![FIRApp validateAppIDFormat:appID withVersion:appIDVersion]) { + if (![self validateAppIDFormat:appID withVersion:appIDVersion]) { return NO; } - if (![FIRApp validateAppIDFingerprint:appID withVersion:appIDVersion]) { + if (![self validateAppIDFingerprint:appID withVersion:appIDVersion]) { return NO; } @@ -631,33 +630,76 @@ + (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version { return NO; } - if (![version hasSuffix:@":"]) { + NSScanner *stringScanner = [NSScanner scannerWithString:appID]; + stringScanner.charactersToBeSkipped = nil; + + // Skip version part + // '**::ios:' + if (![stringScanner scanString:version intoString:NULL]) { + // The version part is missing or mismatched + return NO; + } + + // Validate version part (see part between '*' symbols below) + // '*:*:ios:' + if (![stringScanner scanString:@":" intoString:NULL]) { + // appIDVersion must be separated by ":" + return NO; + } + + // Validate version part (see part between '*' symbols below) + // ':**:ios:'. + NSInteger projectNumber = NSNotFound; + if (![stringScanner scanInteger:&projectNumber]) { + // NO project number found. + return NO; + } + + // Validate version part (see part between '*' symbols below) + // ':*:*ios:'. + if (![stringScanner scanString:@":" intoString:NULL]) { + // The project number must be separated by ":" + return NO; + } + + // Validate version part (see part between '*' symbols below) + // '::*ios*:'. + NSString *platform; + if (![stringScanner scanUpToString:@":" intoString:&platform]) { return NO; } - if (![appID hasPrefix:version]) { + if (![platform isEqualToString:@"ios"]) { + // The platform must be @"ios" return NO; } - NSString *const pattern = @"^\\d+:ios:[a-f0-9]+$"; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern - options:0 - error:NULL]; - if (!regex) { + // Validate version part (see part between '*' symbols below) + // '::ios*:*'. + if (![stringScanner scanString:@":" intoString:NULL]) { + // The platform must be separated by ":" return NO; } - NSRange localRange = NSMakeRange(version.length, appID.length - version.length); - NSUInteger numberOfMatches = [regex numberOfMatchesInString:appID options:0 range:localRange]; - if (numberOfMatches != 1) { + // Validate version part (see part between '*' symbols below) + // '::ios:**'. + unsigned long long fingerprint = NSNotFound; + if (![stringScanner scanHexLongLong:&fingerprint]) { + // Fingerprint part is missing return NO; } + + if (!stringScanner.isAtEnd) { + // There are not allowed characters in the fingerprint part + return NO; + } + return YES; } /** * Validates that the fingerprint of the app ID string is what is expected based on the supplied - * version. The version must end in ":". + * version. * * Note that the v1 hash algorithm is not permitted on the client and cannot be fully validated. * @@ -667,18 +709,6 @@ + (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version { * otherwise. */ + (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version { - if (!appID.length || !version.length) { - return NO; - } - - if (![version hasSuffix:@":"]) { - return NO; - } - - if (![appID hasPrefix:version]) { - return NO; - } - // Extract the supplied fingerprint from the supplied app ID. // This assumes the app ID format is the same for all known versions below. If the app ID format // changes in future versions, the tokenizing of the app ID format will need to take into account @@ -699,7 +729,7 @@ + (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)versi return NO; } - if ([version isEqual:@"1:"]) { + if ([version isEqual:@"1"]) { // The v1 hash algorithm is not permitted on the client so the actual hash cannot be validated. return YES; } From 92dcff150ab670e10b989ec801ae4f0a7a76bc17 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 15 Mar 2019 09:40:08 -0700 Subject: [PATCH 042/214] Changes to build m45 zip (#2561) --- .../Sources/ZipBuilder/CocoaPodUtils.swift | 2 +- .../Sources/ZipBuilder/FrameworkBuilder.swift | 36 ++++++++++--------- ZipBuilder/Sources/ZipBuilder/Subspec.swift | 1 + 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift b/ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift index 826051ea2be..67fdc56f2e0 100644 --- a/ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift +++ b/ZipBuilder/Sources/ZipBuilder/CocoaPodUtils.swift @@ -146,7 +146,7 @@ public enum CocoaPodUtils { } // Run pod install on the directory that contains the Podfile and blank Xcode project. - let result = Shell.executeCommandFromScript("pod install", workingDir: directory) + let result = Shell.executeCommandFromScript("pod _1.5.3_ install", workingDir: directory) switch result { case let .error(code, output): fatalError(""" diff --git a/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift b/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift index d6de936c2e9..c57349522e5 100755 --- a/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift +++ b/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift @@ -83,24 +83,26 @@ struct FrameworkBuilder { cacheEnabled: Bool = false) -> (framework: URL, resources: URL) { print("Building \(podName)") +// Cache is temporarily disabled due to pod cache list issues. // Get the CocoaPods cache to see if we can pull from any frameworks already built. - let podsCache = CocoaPodUtils.listPodCache(inDir: projectDir) - - guard let cachedVersions = podsCache[podName] else { - fatalError("Cannot find a pod cache for framework \(podName).") - } - - guard let podInfo = cachedVersions[version] else { - fatalError(""" - Cannot find a pod cache for framework \(podName) at version \(version). - Something could be wrong with your CocoaPods cache - try running the following: - - pod cache clean '\(podName)' --all - """) - } - - // TODO: Figure out if we need the MD5 at all. - let md5 = Shell.calculateMD5(for: podInfo.installedLocation) +// let podsCache = CocoaPodUtils.listPodCache(inDir: projectDir) +// +// guard let cachedVersions = podsCache[podName] else { +// fatalError("Cannot find a pod cache for framework \(podName).") +// } +// +// guard let podInfo = cachedVersions[version] else { +// fatalError(""" +// Cannot find a pod cache for framework \(podName) at version \(version). +// Something could be wrong with your CocoaPods cache - try running the following: +// +// pod cache clean '\(podName)' --all +// """) +// } +// +// // TODO: Figure out if we need the MD5 at all. + let md5 = podName +// let md5 = Shell.calculateMD5(for: podInfo.installedLocation) // Get (or create) the cache directory for storing built frameworks. let fileManager = FileManager.default diff --git a/ZipBuilder/Sources/ZipBuilder/Subspec.swift b/ZipBuilder/Sources/ZipBuilder/Subspec.swift index 16eebee0400..8f63c0177fb 100644 --- a/ZipBuilder/Sources/ZipBuilder/Subspec.swift +++ b/ZipBuilder/Sources/ZipBuilder/Subspec.swift @@ -36,6 +36,7 @@ public enum Subspec: String, CaseIterable { case mlModelInterpreter = "MLModelInterpreter" case mlNaturalLanguage = "MLNaturalLanguage" case mlNLLanguageID = "MLNLLanguageID" + case mlNLSmartReply = "MLNLSmartReply" case mlVision = "MLVision" case mlVisionBarcodeModel = "MLVisionBarcodeModel" case mlVisionFaceModel = "MLVisionFaceModel" From 9c3cb4052560095f35e1d51978265f50c9e5cfdc Mon Sep 17 00:00:00 2001 From: Gil Date: Fri, 15 Mar 2019 11:03:29 -0700 Subject: [PATCH 043/214] Add a run-all-checks script (#2533) This makes it easier to check everything before submission to travis, cutting the cycle time on format/lint failures down considerably. --- .travis.yml | 8 +- scripts/check.sh | 220 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 7 deletions(-) create mode 100755 scripts/check.sh diff --git a/.travis.yml b/.travis.yml index 680cb461296..b282222c9fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,13 +13,7 @@ jobs: - brew install clang-format - brew install swiftformat script: - - ./scripts/check_whitespace.sh - - ./scripts/check_copyright.sh - - ./scripts/check_no_module_imports.sh - - ./scripts/check_test_inclusion.py - - ./scripts/style.sh test-only $TRAVIS_COMMIT_RANGE - # Google C++ style compliance - - ./scripts/lint.sh $TRAVIS_COMMIT_RANGE + - ./scripts/check.sh --test-only $TRAVIS_COMMIT_RANGE # The order of builds matters (even though they are run in parallel): # Travis will schedule them in the same order they are listed here. diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 00000000000..04ac2384aac --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,220 @@ +#!/bin/bash + +# Copyright 2019 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. + +# Checks that the current state of the tree is sane and optionally auto-fixes +# errors automatically. Meant for interactive use. + +function usage() { + cat <] + +Runs auto-formatting scripts, source-tree checks, and linters on any files that +have changed since master. + +By default, any changes are left as uncommited changes in the working tree. You +can review them with git diff. Pass --commit to automatically commit any changes. + +Pass an alternate revision to use as the basis for checking changes. + +OPTIONS: + + --allow-dirty + By default, check.sh requires a clean working tree to keep any generated + changes separate from logical changes. + + --commit + Commit any auto-generated changes with a message indicating which tool made + the changes. + + --amend + Commit any auto-generated changes by amending the HEAD commit. + + --fixup + Commit any auto-generated changes with a fixup! message for the HEAD + commit. The next rebase will squash these fixup commits. + + --test-only + Run all checks without making any changes to local files. + + + Specifies a starting revision other than the default of master. + + +EXAMPLES: + + check.sh + Runs automated checks and formatters on all changed files since master. + Check for changes with git diff. + + check.sh --commit + Runs automated checks and formatters on all changed files since master and + commits the results. + + check.sh --amend HEAD + Runs automated checks and formatters on all changed files since the last + commit and amends the last commit with the difference. + + check.sh --allow-dirty HEAD + Runs automated checks and formatters on all changed files since the last + commit and intermingles the changes with any pending changes. Useful for + interactive use from an editor. + +EOF +} + +set -euo pipefail +unset CDPATH + +# Change to the top-directory of the working tree +top_dir=$(git rev-parse --show-toplevel) +cd "${top_dir}" + +ALLOW_DIRTY=false +COMMIT_METHOD="none" +START_REVISION="master" +TEST_ONLY=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --) + # Do nothing: explicitly allow this, but ignore it + ;; + + -h | --help) + usage + exit 1 + ;; + + --allow-dirty) + ALLOW_DIRTY=true + ;; + + --amend) + COMMIT_METHOD=amend + ;; + + --fixup) + COMMIT_METHOD=fixup + ;; + + --commit) + COMMIT_METHOD=message + ;; + + --test-only) + # In test-only mode, no changes are made, so there's no reason to + # require a clean source tree. + ALLOW_DIRTY=true + TEST_ONLY=true + ;; + + *) + if git rev-parse "$1" >& /dev/null; then + START_REVISION="$1" + break + fi + ;; + esac + shift +done + +if [[ "${TEST_ONLY}" == true && "${COMMIT_METHOD}" != "none" ]]; then + echo "--test-only cannot be combined with --amend, --fixup, or --commit" + exit 1 +fi + +if [[ "${ALLOW_DIRTY}" == true && "${COMMIT_METHOD}" == "message" ]]; then + echo "--allow-dirty and --commit are mutually exclusive" + exit 1 +fi + +if ! git diff-index --quiet HEAD --; then + if [[ "${ALLOW_DIRTY}" != true ]]; then + echo "You have local changes that could be overwritten by this script." + echo "Please commit your changes first or pass --allow-dirty." + exit 2 + fi +fi + +# Record actual start, but only if the revision is specified as a single +# commit. Ranges specified with .. or ... are left alone. +if [[ "${START_REVISION}" == *..* ]]; then + START_SHA="${START_REVISION}" +else + START_SHA=$(git rev-parse "${START_REVISION}") +fi + +# If committing --fixup, avoid messages with fixup! fixup! that might come from +# multiple fixup commits. +HEAD_SHA=$(git rev-parse HEAD) + +function maybe_commit() { + local message="$1" + + if [[ "${COMMIT_METHOD}" == "none" ]]; then + return + fi + + echo "${message}" + case "${COMMIT_METHOD}" in + amend) + git commit -a --amend -C "${HEAD_SHA}" + ;; + + fixup) + git commit -a --fixup="${HEAD_SHA}" + ;; + + message) + git commit -a -m "${message}" + ;; + + *) + echo "Unknown commit method ${COMMIT_METHOD}" 1>&2 + exit 2 + ;; + esac +} + +style_cmd=("${top_dir}/scripts/style.sh") +if [[ "${TEST_ONLY}" == true ]]; then + style_cmd+=(test-only) +fi +style_cmd+=("${START_SHA}") + +# Restyle and commit any changes +"${style_cmd[@]}" +if ! git diff --quiet; then + maybe_commit "style.sh generated changes" +fi + +# If there are changes to the Firestore project, ensure they're ordered +# correctly to minimize conflicts. +if ! git diff --quiet -- Firestore/Example/Firestore.xcodeproj; then + "${top_dir}/scripts/sync_project.rb" + if ! git diff --quiet; then + maybe_commit "sync_project.rb generated changes" + fi +fi + +# Check lint errors. +"${top_dir}/scripts/check_whitespace.sh" +"${top_dir}/scripts/check_copyright.sh" +"${top_dir}/scripts/check_no_module_imports.sh" +"${top_dir}/scripts/check_test_inclusion.py" + +# Google C++ style +"${top_dir}/scripts/lint.sh" "${START_SHA}" From 291cab62c465a7f25d07c8c5c4dd61f29caa5c51 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 15 Mar 2019 14:53:09 -0400 Subject: [PATCH 044/214] Fix resources packaging. (#2567) --- ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift b/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift index 96ace387463..091dc64a5e1 100644 --- a/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift +++ b/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift @@ -760,6 +760,17 @@ struct ZipBuilder { fatalError("Cannot move Resource bundles for \(pod.name): \(error)") } + // Smart Reply packages Resources separately from other MLKit subspecs. + if pod.name == "FirebaseMLNLSmartReply" { + do { + resourceBundles = try ResourcesManager.createBundleForFoldersInResourcesDirs( + containedIn: pod.installedLocation, destinationDir: podResourceDir + ) + } catch { + fatalError("Could not generate Resource bundles for \(pod.name): \(error)") + } + } + // Special case for MLKit *Model subspecs, explicitly copy directories from // GoogleMobileVision. This should be fixed in the future to pull all compiled resources // from Xcode's build directory. From 54763bad87e3b7f5bdf1991213a3dce4eb0ab8e9 Mon Sep 17 00:00:00 2001 From: christibbs <43829046+christibbs@users.noreply.github.com> Date: Fri, 15 Mar 2019 12:28:18 -0700 Subject: [PATCH 045/214] Disable flaky tests in FIAM (#2568) --- InAppMessaging/Example/Tests/FIRIAMClearcutUploaderTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InAppMessaging/Example/Tests/FIRIAMClearcutUploaderTests.m b/InAppMessaging/Example/Tests/FIRIAMClearcutUploaderTests.m index c4bc6b6725e..d26c37b4205 100644 --- a/InAppMessaging/Example/Tests/FIRIAMClearcutUploaderTests.m +++ b/InAppMessaging/Example/Tests/FIRIAMClearcutUploaderTests.m @@ -98,7 +98,7 @@ - (void)testUploadTriggeredWhenWaitTimeConditionSatisfied { [self waitForExpectationsWithTimeout:1.0 handler:nil]; } -- (void)testUploadNotTriggeredWhenWaitTimeConditionNotSatisfied { +- (void)disable_testUploadNotTriggeredWhenWaitTimeConditionNotSatisfied { // using a real storage in this case FIRIAMClearcutLogStorage *logStorage = [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:1000 @@ -238,7 +238,7 @@ - (void)testRespectingWaitTimeFromRequestSender { XCTAssertEqual(currentMoment * 1000 + 1500, uploader.nextValidSendTimeInMills); } -- (void)testWaitTimeFromRequestSenderAdjustedByMinWaitTimeInStrategy { +- (void)disable_testWaitTimeFromRequestSenderAdjustedByMinWaitTimeInStrategy { // using a real storage in this case FIRIAMClearcutLogStorage *logStorage = [[FIRIAMClearcutLogStorage alloc] initWithExpireAfterInSeconds:1000 From 2d297eb62a0ead6b24da089a41d1fd5df8c69603 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 15 Mar 2019 17:19:25 -0400 Subject: [PATCH 046/214] Fix method names in CHANGELOG (#2570) * Fix method names in CHANGELOG * Delete reverted os_log Core CHANGELOG --- Firebase/Core/CHANGELOG.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index 250fd0fd67f..05eb0cd3a03 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,11 +1,8 @@ # 2019-03-19 -- v5.4.0 -- M45 - [changed] Allow Bundle IDs that have a valid prefix to enable richer extension support. (#2515) -- [changed] Convert FIRLogger to use the recommended os_log for logging via GoogleUtilities. - See https://developer.apple.com/documentation/os/logging?language=objc. - [changed] Deprecated `FIRAnalyticsConfiguration` API in favor of new methods on the Analytics SDK. - Please call the new APIs directly: Enable/disable Analytics with `Analytics.setAnalyticsEnabled(_)` - and modify the session timeout interval with `Analytics.setAnalyticsCollectionEnabled(_)`. - + Please call the new APIs directly: Enable/disable Analytics with `Analytics.setAnalyticsCollectionEnabled(_)` + and modify the session timeout interval with `Analytics.setSessionTimeoutInterval(_)`. # 2019-01-22 -- v5.2.0 -- M41 - [changed] Added a registerInternalLibrary API. Now other Firebase libraries register with FirebaseCore From 0643b36aef1eab176c1a5714d1e0291b1589dc2b Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Fri, 15 Mar 2019 19:10:32 -0400 Subject: [PATCH 047/214] C++ migration: make `FIRFirestore` implementation delegate to core API (#2492) --- .../Tests/API/FIRQuerySnapshotTests.mm | 23 +- Firestore/Example/Tests/API/FSTAPIHelpers.mm | 20 +- .../Tests/Util/FSTIntegrationTestCase.mm | 2 +- .../Source/API/FIRCollectionReference.mm | 16 +- .../Source/API/FIRDocumentChange+Internal.h | 9 +- Firestore/Source/API/FIRDocumentChange.mm | 30 +-- .../API/FIRDocumentReference+Internal.h | 19 +- Firestore/Source/API/FIRDocumentReference.mm | 92 +++++--- .../Source/API/FIRDocumentSnapshot+Internal.h | 20 +- Firestore/Source/API/FIRDocumentSnapshot.mm | 53 ++--- Firestore/Source/API/FIRFirestore+Internal.h | 13 +- Firestore/Source/API/FIRFirestore.mm | 216 ++++++------------ Firestore/Source/API/FIRQuerySnapshot.mm | 19 +- Firestore/Source/API/FIRTransaction.mm | 21 +- Firestore/Source/API/FSTFirestoreComponent.mm | 15 +- Firestore/Source/Core/FSTFirestoreClient.h | 6 +- Firestore/Source/Core/FSTFirestoreClient.mm | 45 ++-- .../firestore/api/document_reference.h | 27 ++- .../firestore/api/document_reference.mm | 183 ++++++++------- .../firestore/api/document_snapshot.h | 13 +- .../firestore/api/document_snapshot.mm | 9 +- .../src/firebase/firestore/api/firestore.h | 129 +++++++++++ .../src/firebase/firestore/api/firestore.mm | 202 ++++++++++++++++ .../firestore/util/statusor_callback.h | 35 +++ 24 files changed, 774 insertions(+), 443 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/api/firestore.h create mode 100644 Firestore/core/src/firebase/firestore/api/firestore.mm create mode 100644 Firestore/core/src/firebase/firestore/util/statusor_callback.h diff --git a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm index 60dcc398938..1399f822d57 100644 --- a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm +++ b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm @@ -25,6 +25,7 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" #import "Firestore/Source/API/FIRDocumentChange+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Model/FSTDocument.h" @@ -110,16 +111,18 @@ - (void)testIncludeMetadataChanges { snapshot:std::move(viewSnapshot) metadata:metadata]; - FIRQueryDocumentSnapshot *doc1Snap = [FIRQueryDocumentSnapshot snapshotWithFirestore:firestore - documentKey:doc1New.key - document:doc1New - fromCache:NO - hasPendingWrites:NO]; - FIRQueryDocumentSnapshot *doc2Snap = [FIRQueryDocumentSnapshot snapshotWithFirestore:firestore - documentKey:doc2New.key - document:doc2New - fromCache:NO - hasPendingWrites:NO]; + FIRQueryDocumentSnapshot *doc1Snap = + [[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore.wrapped + documentKey:doc1New.key + document:doc1New + fromCache:false + hasPendingWrites:false]; + FIRQueryDocumentSnapshot *doc2Snap = + [[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore.wrapped + documentKey:doc2New.key + document:doc2New + fromCache:false + hasPendingWrites:false]; NSArray *changesWithoutMetadata = @[ [[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeModified diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.mm b/Firestore/Example/Tests/API/FSTAPIHelpers.mm index 61332b50744..8e465b662b1 100644 --- a/Firestore/Example/Tests/API/FSTAPIHelpers.mm +++ b/Firestore/Example/Tests/API/FSTAPIHelpers.mm @@ -55,9 +55,9 @@ dispatch_once(&onceToken, ^{ sharedInstance = [[FIRFirestore alloc] initWithProjectID:"abc" database:"abc" - persistenceKey:@"db123" - credentialsProvider:nil - workerQueue:nil + persistenceKey:"db123" + credentialsProvider:nullptr + workerQueue:nullptr firebaseApp:nil]; }); #pragma clang diagnostic pop @@ -73,11 +73,11 @@ data ? FSTTestDoc(path, version, data, hasMutations ? FSTDocumentStateLocalMutations : FSTDocumentStateSynced) : nil; - return [FIRDocumentSnapshot snapshotWithFirestore:FSTTestFirestore() - documentKey:testutil::Key(path) - document:doc - fromCache:fromCache - hasPendingWrites:hasMutations]; + return [[FIRDocumentSnapshot alloc] initWithFirestore:FSTTestFirestore().wrapped + documentKey:testutil::Key(path) + document:doc + fromCache:fromCache + hasPendingWrites:hasMutations]; } FIRCollectionReference *FSTTestCollectionRef(const absl::string_view path) { @@ -86,8 +86,8 @@ } FIRDocumentReference *FSTTestDocRef(const absl::string_view path) { - return [FIRDocumentReference referenceWithPath:testutil::Resource(path) - firestore:FSTTestFirestore()]; + return [[FIRDocumentReference alloc] initWithPath:testutil::Resource(path) + firestore:FSTTestFirestore().wrapped]; } /** A convenience method for creating a query snapshots for tests. */ diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index cd08ec3756d..fe6cf3a166b 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -193,7 +193,7 @@ - (FIRFirestore *)firestoreWithProjectID:(NSString *)projectID { FIRFirestore *firestore = [[FIRFirestore alloc] initWithProjectID:util::MakeString(projectID) database:DatabaseId::kDefault - persistenceKey:persistenceKey + persistenceKey:util::MakeString(persistenceKey) credentialsProvider:std::move(credentials_provider) workerQueue:std::move(workerQueue) firebaseApp:app]; diff --git a/Firestore/Source/API/FIRCollectionReference.mm b/Firestore/Source/API/FIRCollectionReference.mm index 76f441359eb..3156ef42a63 100644 --- a/Firestore/Source/API/FIRCollectionReference.mm +++ b/Firestore/Source/API/FIRCollectionReference.mm @@ -15,11 +15,13 @@ */ #import "FIRCollectionReference.h" -#import "FIRFirestore.h" + +#include #include "Firestore/core/src/firebase/firestore/util/autoid.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/API/FIRQuery_Init.h" #import "Firestore/Source/Core/FSTQuery.h" @@ -99,7 +101,8 @@ - (FIRDocumentReference *_Nullable)parent { return nil; } else { DocumentKey key{parentPath}; - return [FIRDocumentReference referenceWithKey:key firestore:self.firestore]; + return [[FIRDocumentReference alloc] initWithKey:std::move(key) + firestore:self.firestore.wrapped]; } } @@ -112,8 +115,9 @@ - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath { FSTThrowInvalidArgument(@"Document path cannot be nil."); } const ResourcePath subPath = ResourcePath::FromString(util::MakeString(documentPath)); - const ResourcePath path = self.query.path.Append(subPath); - return [FIRDocumentReference referenceWithPath:path firestore:self.firestore]; + ResourcePath path = self.query.path.Append(subPath); + return [[FIRDocumentReference alloc] initWithPath:std::move(path) + firestore:self.firestore.wrapped]; } - (FIRDocumentReference *)addDocumentWithData:(NSDictionary *)data { @@ -129,8 +133,8 @@ - (FIRDocumentReference *)addDocumentWithData:(NSDictionary *)da } - (FIRDocumentReference *)documentWithAutoID { - const DocumentKey key{self.query.path.Append(CreateAutoId())}; - return [FIRDocumentReference referenceWithKey:key firestore:self.firestore]; + DocumentKey key{self.query.path.Append(CreateAutoId())}; + return [[FIRDocumentReference alloc] initWithKey:std::move(key) firestore:self.firestore.wrapped]; } @end diff --git a/Firestore/Source/API/FIRDocumentChange+Internal.h b/Firestore/Source/API/FIRDocumentChange+Internal.h index 220a3683a42..c1c26904db8 100644 --- a/Firestore/Source/API/FIRDocumentChange+Internal.h +++ b/Firestore/Source/API/FIRDocumentChange+Internal.h @@ -16,6 +16,7 @@ #import "FIRDocumentChange.h" +#include "Firestore/core/src/firebase/firestore/api/firestore.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" @class FIRFirestore; @@ -26,10 +27,10 @@ NS_ASSUME_NONNULL_BEGIN @interface FIRDocumentChange (Internal) /** Calculates the array of FIRDocumentChange's based on the given FSTViewSnapshot. */ -+ (NSArray *)documentChangesForSnapshot: - (const firebase::firestore::core::ViewSnapshot &)snapshot - includeMetadataChanges:(BOOL)includeMetadataChanges - firestore:(FIRFirestore *)firestore; ++ (NSArray *) + documentChangesForSnapshot:(const firebase::firestore::core::ViewSnapshot &)snapshot + includeMetadataChanges:(bool)includeMetadataChanges + firestore:(firebase::firestore::api::Firestore *)firestore; @end diff --git a/Firestore/Source/API/FIRDocumentChange.mm b/Firestore/Source/API/FIRDocumentChange.mm index b793faa3402..ffcf645cfb8 100644 --- a/Firestore/Source/API/FIRDocumentChange.mm +++ b/Firestore/Source/API/FIRDocumentChange.mm @@ -17,6 +17,7 @@ #import "FIRDocumentChange.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentSet.h" @@ -24,6 +25,7 @@ #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +using firebase::firestore::api::Firestore; using firebase::firestore::core::DocumentViewChange; using firebase::firestore::core::ViewSnapshot; @@ -55,8 +57,8 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(const DocumentViewChange & } + (NSArray *)documentChangesForSnapshot:(const ViewSnapshot &)snapshot - includeMetadataChanges:(BOOL)includeMetadataChanges - firestore:(FIRFirestore *)firestore { + includeMetadataChanges:(bool)includeMetadataChanges + firestore:(Firestore *)firestore { if (snapshot.old_documents().isEmpty) { // Special case the first snapshot because index calculation is easy and fast. Also all changes // on the first snapshot are adds so there are also no metadata-only changes to filter out. @@ -64,12 +66,12 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(const DocumentViewChange & NSUInteger index = 0; NSMutableArray *changes = [NSMutableArray array]; for (const DocumentViewChange &change : snapshot.document_changes()) { - FIRQueryDocumentSnapshot *document = [FIRQueryDocumentSnapshot - snapshotWithFirestore:firestore - documentKey:change.document().key - document:change.document() - fromCache:snapshot.from_cache() - hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)]; + FIRQueryDocumentSnapshot *document = [[FIRQueryDocumentSnapshot alloc] + initWithFirestore:firestore + documentKey:change.document().key + document:change.document() + fromCache:snapshot.from_cache() + hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)]; HARD_ASSERT(change.type() == DocumentViewChange::Type::kAdded, "Invalid event type for first snapshot"); HARD_ASSERT(!lastDocument || snapshot.query().comparator(lastDocument, change.document()) == @@ -91,12 +93,12 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(const DocumentViewChange & continue; } - FIRQueryDocumentSnapshot *document = [FIRQueryDocumentSnapshot - snapshotWithFirestore:firestore - documentKey:change.document().key - document:change.document() - fromCache:snapshot.from_cache() - hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)]; + FIRQueryDocumentSnapshot *document = [[FIRQueryDocumentSnapshot alloc] + initWithFirestore:firestore + documentKey:change.document().key + document:change.document() + fromCache:snapshot.from_cache() + hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)]; NSUInteger oldIndex = NSNotFound; NSUInteger newIndex = NSNotFound; diff --git a/Firestore/Source/API/FIRDocumentReference+Internal.h b/Firestore/Source/API/FIRDocumentReference+Internal.h index eb078ca535d..aa12e97f784 100644 --- a/Firestore/Source/API/FIRDocumentReference+Internal.h +++ b/Firestore/Source/API/FIRDocumentReference+Internal.h @@ -16,19 +16,28 @@ #import "FIRDocumentReference.h" +#include "Firestore/core/src/firebase/firestore/api/document_reference.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" NS_ASSUME_NONNULL_BEGIN +@interface FIRDocumentReference (/* Init */) + +- (instancetype)initWithReference:(firebase::firestore::api::DocumentReference &&)reference + NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithPath:(firebase::firestore::model::ResourcePath)path + firestore:(firebase::firestore::api::Firestore *)firestore; + +- (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key + firestore:(firebase::firestore::api::Firestore *)firestore; + +@end + /** Internal FIRDocumentReference API we don't want exposed in our public header files. */ @interface FIRDocumentReference (Internal) -+ (instancetype)referenceWithPath:(const firebase::firestore::model::ResourcePath &)path - firestore:(FIRFirestore *)firestore; -+ (instancetype)referenceWithKey:(firebase::firestore::model::DocumentKey)key - firestore:(FIRFirestore *)firestore; - - (const firebase::firestore::model::DocumentKey &)key; @end diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index f4c3a616878..ef5cce388bf 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -34,27 +34,33 @@ #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/api/document_reference.h" +#include "Firestore/core/src/firebase/firestore/api/document_snapshot.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/error_apple.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" +#include "Firestore/core/src/firebase/firestore/util/statusor.h" +#include "Firestore/core/src/firebase/firestore/util/statusor_callback.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; using firebase::firestore::api::DocumentReference; +using firebase::firestore::api::DocumentSnapshot; +using firebase::firestore::api::Firestore; using firebase::firestore::core::ParsedSetData; using firebase::firestore::core::ParsedUpdateData; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::Precondition; using firebase::firestore::model::ResourcePath; +using firebase::firestore::util::Status; +using firebase::firestore::util::StatusOr; +using firebase::firestore::util::StatusOrCallback; NS_ASSUME_NONNULL_BEGIN #pragma mark - FIRDocumentReference -@interface FIRDocumentReference () -- (instancetype)initWithReference:(DocumentReference &&)reference NS_DESIGNATED_INITIALIZER; -@end - @implementation FIRDocumentReference { DocumentReference _documentReference; } @@ -66,6 +72,20 @@ - (instancetype)initWithReference:(DocumentReference &&)reference { return self; } +- (instancetype)initWithPath:(ResourcePath)path firestore:(Firestore *)firestore { + if (path.size() % 2 != 0) { + FSTThrowInvalidArgument(@"Invalid document reference. Document references must have an even " + "number of segments, but %s has %zu", + path.CanonicalString().c_str(), path.size()); + } + return [self initWithKey:DocumentKey{std::move(path)} firestore:firestore]; +} + +- (instancetype)initWithKey:(DocumentKey)key firestore:(Firestore *)firestore { + DocumentReference delegate{std::move(key), firestore}; + return [self initWithReference:std::move(delegate)]; +} + #pragma mark - NSObject Methods - (BOOL)isEqual:(nullable id)other { @@ -84,7 +104,7 @@ - (NSUInteger)hash { @dynamic firestore; - (FIRFirestore *)firestore { - return _documentReference.firestore(); + return [FIRFirestore recoverFromFirestore:_documentReference.firestore()]; } - (NSString *)documentID { @@ -92,7 +112,8 @@ - (NSString *)documentID { } - (FIRCollectionReference *)parent { - return _documentReference.Parent(); + return [FIRCollectionReference referenceWithPath:_documentReference.key().path().PopLast() + firestore:self.firestore]; } - (NSString *)path { @@ -103,7 +124,10 @@ - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath { if (!collectionPath) { FSTThrowInvalidArgument(@"Collection path cannot be nil."); } - return _documentReference.GetCollectionReference(util::MakeString(collectionPath)); + + ResourcePath subPath = ResourcePath::FromString(util::MakeString(collectionPath)); + ResourcePath path = _documentReference.key().path().Append(subPath); + return [FIRCollectionReference referenceWithPath:path firestore:self.firestore]; } - (void)setData:(NSDictionary *)documentData { @@ -127,10 +151,9 @@ - (void)setData:(NSDictionary *)documentData - (void)setData:(NSDictionary *)documentData merge:(BOOL)merge completion:(nullable void (^)(NSError *_Nullable error))completion { - ParsedSetData parsed = - merge ? [_documentReference.firestore().dataConverter parsedMergeData:documentData - fieldMask:nil] - : [_documentReference.firestore().dataConverter parsedSetData:documentData]; + auto dataConverter = self.firestore.dataConverter; + ParsedSetData parsed = merge ? [dataConverter parsedMergeData:documentData fieldMask:nil] + : [dataConverter parsedSetData:documentData]; _documentReference.SetData( std::move(parsed).ToMutations(_documentReference.key(), Precondition::None()), completion); } @@ -138,8 +161,8 @@ - (void)setData:(NSDictionary *)documentData - (void)setData:(NSDictionary *)documentData mergeFields:(NSArray *)mergeFields completion:(nullable void (^)(NSError *_Nullable error))completion { - ParsedSetData parsed = [_documentReference.firestore().dataConverter parsedMergeData:documentData - fieldMask:mergeFields]; + ParsedSetData parsed = [self.firestore.dataConverter parsedMergeData:documentData + fieldMask:mergeFields]; _documentReference.SetData( std::move(parsed).ToMutations(_documentReference.key(), Precondition::None()), completion); } @@ -150,7 +173,7 @@ - (void)updateData:(NSDictionary *)fields { - (void)updateData:(NSDictionary *)fields completion:(nullable void (^)(NSError *_Nullable error))completion { - ParsedUpdateData parsed = [_documentReference.firestore().dataConverter parsedUpdateData:fields]; + ParsedUpdateData parsed = [self.firestore.dataConverter parsedUpdateData:fields]; _documentReference.UpdateData( std::move(parsed).ToMutations(_documentReference.key(), Precondition::Exists(true)), completion); @@ -164,15 +187,14 @@ - (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error _documentReference.DeleteDocument(completion); } -- (void)getDocumentWithCompletion:(void (^)(FIRDocumentSnapshot *_Nullable document, - NSError *_Nullable error))completion { - [self getDocumentWithSource:FIRFirestoreSourceDefault completion:completion]; +- (void)getDocumentWithCompletion:(FIRDocumentSnapshotBlock)completion { + _documentReference.GetDocument(FIRFirestoreSourceDefault, + [self wrapDocumentSnapshotBlock:completion]); } - (void)getDocumentWithSource:(FIRFirestoreSource)source - completion:(void (^)(FIRDocumentSnapshot *_Nullable document, - NSError *_Nullable error))completion { - _documentReference.GetDocument(source, completion); + completion:(FIRDocumentSnapshotBlock)completion { + _documentReference.GetDocument(source, [self wrapDocumentSnapshotBlock:completion]); } - (id)addSnapshotListener:(FIRDocumentSnapshotBlock)listener { @@ -190,7 +212,8 @@ - (void)getDocumentWithSource:(FIRFirestoreSource)source - (id) addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions listener:(FIRDocumentSnapshotBlock)listener { - return _documentReference.AddSnapshotListener(listener, internalOptions); + return _documentReference.AddSnapshotListener([self wrapDocumentSnapshotBlock:listener], + internalOptions); } /** Converts the public API options object to the internal options object. */ @@ -200,26 +223,25 @@ - (FSTListenOptions *)internalOptionsForIncludeMetadataChanges:(BOOL)includeMeta waitForSyncWhenOnline:NO]; } +- (StatusOrCallback)wrapDocumentSnapshotBlock:(FIRDocumentSnapshotBlock)block { + FIRFirestore *firestore = self.firestore; + return [block, firestore](StatusOr maybe_snapshot) { + if (maybe_snapshot.ok()) { + FIRDocumentSnapshot *result = + [[FIRDocumentSnapshot alloc] initWithSnapshot:std::move(maybe_snapshot).ValueOrDie()]; + block(result, nil); + } else { + block(nil, util::MakeNSError(maybe_snapshot.status())); + } + }; +} + @end #pragma mark - FIRDocumentReference (Internal) @implementation FIRDocumentReference (Internal) -+ (instancetype)referenceWithPath:(const ResourcePath &)path firestore:(FIRFirestore *)firestore { - if (path.size() % 2 != 0) { - FSTThrowInvalidArgument(@"Invalid document reference. Document references must have an even " - "number of segments, but %s has %zu", - path.CanonicalString().c_str(), path.size()); - } - return [FIRDocumentReference referenceWithKey:DocumentKey{path} firestore:firestore]; -} - -+ (instancetype)referenceWithKey:(DocumentKey)key firestore:(FIRFirestore *)firestore { - DocumentReference underlyingReference{firestore, std::move(key)}; - return [[FIRDocumentReference alloc] initWithReference:std::move(underlyingReference)]; -} - - (const DocumentKey &)key { return _documentReference.key(); } diff --git a/Firestore/Source/API/FIRDocumentSnapshot+Internal.h b/Firestore/Source/API/FIRDocumentSnapshot+Internal.h index fdc5ac8a3d5..620a1562753 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot+Internal.h +++ b/Firestore/Source/API/FIRDocumentSnapshot+Internal.h @@ -16,6 +16,7 @@ #import "FIRDocumentSnapshot.h" +#include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" @class FIRFirestore; @@ -23,15 +24,22 @@ NS_ASSUME_NONNULL_BEGIN +@interface FIRDocumentSnapshot (/* Init */) + +- (instancetype)initWithSnapshot:(firebase::firestore::api::DocumentSnapshot &&)snapshot + NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithFirestore:(firebase::firestore::api::Firestore *)firestore + documentKey:(firebase::firestore::model::DocumentKey)documentKey + document:(nullable FSTDocument *)document + fromCache:(bool)fromCache + hasPendingWrites:(bool)pendingWrites; + +@end + /** Internal FIRDocumentSnapshot API we don't want exposed in our public header files. */ @interface FIRDocumentSnapshot (Internal) -+ (instancetype)snapshotWithFirestore:(FIRFirestore *)firestore - documentKey:(firebase::firestore::model::DocumentKey)documentKey - document:(nullable FSTDocument *)document - fromCache:(BOOL)fromCache - hasPendingWrites:(BOOL)pendingWrites; - @property(nonatomic, strong, readonly, nullable) FSTDocument *internalDocument; @end diff --git a/Firestore/Source/API/FIRDocumentSnapshot.mm b/Firestore/Source/API/FIRDocumentSnapshot.mm index 3cc684e5046..f999bc44cd7 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot.mm +++ b/Firestore/Source/API/FIRDocumentSnapshot.mm @@ -20,7 +20,6 @@ #include "Firestore/core/src/firebase/firestore/util/warnings.h" -#import "FIRFirestore.h" #import "FIRFirestoreSettings.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" @@ -32,6 +31,7 @@ #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" +#include "Firestore/core/src/firebase/firestore/api/firestore.h" #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/util/hard_assert.h" @@ -39,6 +39,7 @@ namespace util = firebase::firestore::util; using firebase::firestore::api::DocumentSnapshot; +using firebase::firestore::api::Firestore; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; using firebase::firestore::util::WrapNSString; @@ -65,27 +66,6 @@ ServerTimestampBehavior InternalServerTimestampBehavior(FIRServerTimestampBehavi } // namespace -@interface FIRDocumentSnapshot () - -- (instancetype)initWithSnapshot:(DocumentSnapshot &&)snapshot; - -@end - -@implementation FIRDocumentSnapshot (Internal) - -+ (instancetype)snapshotWithFirestore:(FIRFirestore *)firestore - documentKey:(DocumentKey)documentKey - document:(nullable FSTDocument *)document - fromCache:(BOOL)fromCache - hasPendingWrites:(BOOL)pendingWrites { - DocumentSnapshot underlyingSnapshot{firestore, documentKey, document, - static_cast(fromCache), - static_cast(pendingWrites)}; - return [[[self class] alloc] initWithSnapshot:std::move(underlyingSnapshot)]; -} - -@end - @implementation FIRDocumentSnapshot { DocumentSnapshot _snapshot; } @@ -97,6 +77,15 @@ - (instancetype)initWithSnapshot:(DocumentSnapshot &&)snapshot { return self; } +- (instancetype)initWithFirestore:(Firestore *)firestore + documentKey:(DocumentKey)documentKey + document:(nullable FSTDocument *)document + fromCache:(bool)fromCache + hasPendingWrites:(bool)pendingWrites { + DocumentSnapshot wrapped{firestore, std::move(documentKey), document, fromCache, pendingWrites}; + return [self initWithSnapshot:std::move(wrapped)]; +} + // NSObject Methods - (BOOL)isEqual:(nullable id)other { if (other == self) return YES; @@ -121,7 +110,7 @@ - (FSTDocument *)internalDocument { } - (FIRDocumentReference *)reference { - return _snapshot.CreateReference(); + return [[FIRDocumentReference alloc] initWithReference:_snapshot.CreateReference()]; } - (NSString *)documentID { @@ -174,7 +163,9 @@ - (FSTFieldValueOptions *)optionsForServerTimestampBehavior: SUPPRESS_DEPRECATED_DECLARATIONS_BEGIN() return [[FSTFieldValueOptions alloc] initWithServerTimestampBehavior:InternalServerTimestampBehavior(serverTimestampBehavior) - timestampsInSnapshotsEnabled:_snapshot.firestore().settings.timestampsInSnapshotsEnabled]; + timestampsInSnapshotsEnabled:_snapshot.firestore() + ->settings() + .timestampsInSnapshotsEnabled]; SUPPRESS_END() } @@ -186,7 +177,7 @@ - (id)convertedValue:(FSTFieldValue *)value options:(FSTFieldValueOptions *)opti } else if ([value isKindOfClass:[FSTReferenceValue class]]) { FSTReferenceValue *ref = (FSTReferenceValue *)value; const DatabaseId *refDatabase = ref.databaseID; - const DatabaseId *database = _snapshot.firestore().databaseID; + const DatabaseId *database = &_snapshot.firestore()->database_id(); if (*refDatabase != *database) { // TODO(b/32073923): Log this as a proper warning. NSLog(@"WARNING: Document %@ contains a document reference within a different database " @@ -197,7 +188,7 @@ - (id)convertedValue:(FSTFieldValue *)value options:(FSTFieldValueOptions *)opti database->database_id().c_str()); } DocumentKey key = [[ref valueWithOptions:options] key]; - return [FIRDocumentReference referenceWithKey:key firestore:_snapshot.firestore()]; + return [[FIRDocumentReference alloc] initWithKey:key firestore:_snapshot.firestore()]; } else { return [value valueWithOptions:options]; } @@ -225,18 +216,8 @@ - (id)convertedValue:(FSTFieldValue *)value options:(FSTFieldValueOptions *)opti @end -@interface FIRQueryDocumentSnapshot () - -- (instancetype)initWithSnapshot:(DocumentSnapshot &&)snapshot NS_DESIGNATED_INITIALIZER; - -@end - @implementation FIRQueryDocumentSnapshot -- (instancetype)initWithSnapshot:(DocumentSnapshot &&)snapshot { - return [super initWithSnapshot:std::move(snapshot)]; -} - - (NSDictionary *)data { NSDictionary *data = [super data]; HARD_ASSERT(data, "Document in a QueryDocumentSnapshot should exist"); diff --git a/Firestore/Source/API/FIRFirestore+Internal.h b/Firestore/Source/API/FIRFirestore+Internal.h index ad9da8ed109..06d5b7d008a 100644 --- a/Firestore/Source/API/FIRFirestore+Internal.h +++ b/Firestore/Source/API/FIRFirestore+Internal.h @@ -19,13 +19,13 @@ #include #include +#include "Firestore/core/src/firebase/firestore/api/firestore.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" -#include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" -#include "absl/strings/string_view.h" NS_ASSUME_NONNULL_BEGIN +@class FIRApp; @class FSTFirestoreClient; @class FSTUserDataConverter; @@ -38,12 +38,11 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype) initWithProjectID:(std::string)projectID database:(std::string)database - persistenceKey:(NSString *)persistenceKey + persistenceKey:(std::string)persistenceKey credentialsProvider: (std::unique_ptr)credentialsProvider workerQueue:(std::unique_ptr)workerQueue firebaseApp:(FIRApp *)app; - @end /** Internal FIRFirestore API we don't want exposed in our public header files. */ @@ -65,6 +64,8 @@ NS_ASSUME_NONNULL_BEGIN /** Checks to see if logging is is globally enabled for the Firestore client. */ + (BOOL)isLoggingEnabled; ++ (FIRFirestore *)recoverFromFirestore:(firebase::firestore::api::Firestore *)firestore; + /** * Shutdown this `FIRFirestore`, releasing all resources (abandoning any outstanding writes, * removing all listens, closing all network connections, etc.). @@ -74,7 +75,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)shutdownWithCompletion:(nullable void (^)(NSError *_Nullable error))completion NS_SWIFT_NAME(shutdown(completion:)); -- (firebase::firestore::util::AsyncQueue *)workerQueue; +@property(nonatomic, assign, readonly) firebase::firestore::api::Firestore *wrapped; + +@property(nonatomic, assign, readonly) firebase::firestore::util::AsyncQueue *workerQueue; // FIRFirestore ownes the DatabaseId instance. @property(nonatomic, assign, readonly) const firebase::firestore::model::DatabaseId *databaseID; diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index 6da95559240..cffe3ea36e5 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -26,42 +26,28 @@ #include #include -#import "FIRFirestoreSettings.h" -#import "Firestore/Source/API/FIRCollectionReference+Internal.h" +#import "FIRFirestore.h" + #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/API/FIRQuery+Internal.h" -#import "Firestore/Source/API/FIRTransaction+Internal.h" -#import "Firestore/Source/API/FIRWriteBatch+Internal.h" #import "Firestore/Source/API/FSTFirestoreComponent.h" #import "Firestore/Source/API/FSTUserDataConverter.h" -#import "Firestore/Source/Core/FSTFirestoreClient.h" -#import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/firestore.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" -#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" -#include "Firestore/core/src/firebase/firestore/core/transaction.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" -#include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" -#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" -#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" -#include "absl/memory/memory.h" namespace util = firebase::firestore::util; +using firebase::firestore::api::DocumentReference; +using firebase::firestore::api::Firestore; using firebase::firestore::auth::CredentialsProvider; -using firebase::firestore::auth::FirebaseCredentialsProvider; -using firebase::firestore::core::DatabaseInfo; -using firebase::firestore::core::Transaction; using firebase::firestore::model::DatabaseId; -using firebase::firestore::model::ResourcePath; -using util::AsyncQueue; -using util::Executor; -using util::ExecutorLibdispatch; +using firebase::firestore::util::AsyncQueue; NS_ASSUME_NONNULL_BEGIN @@ -69,32 +55,16 @@ #pragma mark - FIRFirestore -@interface FIRFirestore () { - /** The actual owned DatabaseId instance is allocated in FIRFirestore. */ - DatabaseId _databaseID; - std::unique_ptr _credentialsProvider; -} +@interface FIRFirestore () -@property(nonatomic, strong) NSString *persistenceKey; - -// Note that `client` is updated after initialization, but marking this readwrite would generate an -// incorrect setter (since we make the assignment to `client` inside an `@synchronized` block. -@property(nonatomic, strong, readonly) FSTFirestoreClient *client; @property(nonatomic, strong, readonly) FSTUserDataConverter *dataConverter; @end @implementation FIRFirestore { - // Ownership will be transferred to `FSTFirestoreClient` as soon as the client is created. - std::unique_ptr _workerQueue; + // `std::mutex` member variable makes `core::Firestore` unmovable. - // All guarded by @synchronized(self) - FIRFirestoreSettings *_settings; - FSTFirestoreClient *_client; -} - -- (AsyncQueue *)workerQueue { - return [_client workerQueue]; + std::unique_ptr _firestore; } + (NSMutableDictionary *)instances { @@ -169,12 +139,17 @@ + (instancetype)firestoreForApp:(FIRApp *)app database:(NSString *)database { - (instancetype)initWithProjectID:(std::string)projectID database:(std::string)database - persistenceKey:(NSString *)persistenceKey + persistenceKey:(std::string)persistenceKey credentialsProvider:(std::unique_ptr)credentialsProvider workerQueue:(std::unique_ptr)workerQueue firebaseApp:(FIRApp *)app { if (self = [super init]) { - _databaseID = DatabaseId{std::move(projectID), std::move(database)}; + _firestore = absl::make_unique( + std::move(projectID), std::move(database), std::move(persistenceKey), + std::move(credentialsProvider), std::move(workerQueue), (__bridge void *)self); + + _app = app; + FSTPreConverterBlock block = ^id _Nullable(id _Nullable input) { if ([input isKindOfClass:[FIRDocumentReference class]]) { FIRDocumentReference *documentReference = (FIRDocumentReference *)input; @@ -184,67 +159,26 @@ - (instancetype)initWithProjectID:(std::string)projectID return input; } }; - _dataConverter = [[FSTUserDataConverter alloc] initWithDatabaseID:&_databaseID + + _dataConverter = [[FSTUserDataConverter alloc] initWithDatabaseID:&_firestore->database_id() preConverter:block]; - _persistenceKey = persistenceKey; - _credentialsProvider = std::move(credentialsProvider); - _workerQueue = std::move(workerQueue); - _app = app; - _settings = [[FIRFirestoreSettings alloc] init]; } return self; } - (FIRFirestoreSettings *)settings { - @synchronized(self) { - // Disallow mutation of our internal settings - return [_settings copy]; - } + return _firestore->settings(); } - (void)setSettings:(FIRFirestoreSettings *)settings { - @synchronized(self) { - // As a special exception, don't throw if the same settings are passed repeatedly. This should - // make it more friendly to create a Firestore instance. - if (_client && ![_settings isEqual:settings]) { - FSTThrowInvalidUsage(@"FIRIllegalStateException", - @"Firestore instance has already been started and its settings can no " - "longer be changed. You can only set settings before calling any " - "other methods on a Firestore instance."); - } - _settings = [settings copy]; - } + _firestore->set_settings(settings); } /** * Ensures that the FirestoreClient is configured and returns it. */ - (FSTFirestoreClient *)client { - [self ensureClientConfigured]; - return _client; -} - -- (void)ensureClientConfigured { - @synchronized(self) { - if (!_client) { - // These values are validated elsewhere; this is just double-checking: - HARD_ASSERT(_settings.host, "FirestoreSettings.host cannot be nil."); - HARD_ASSERT(_settings.dispatchQueue, "FirestoreSettings.dispatchQueue cannot be nil."); - - const DatabaseInfo database_info(*self.databaseID, util::MakeString(_persistenceKey), - util::MakeString(_settings.host), _settings.sslEnabled); - - std::unique_ptr userExecutor = - absl::make_unique(_settings.dispatchQueue); - - HARD_ASSERT(_workerQueue, "Expected non-null _workerQueue"); - _client = [FSTFirestoreClient clientWithDatabaseInfo:database_info - settings:_settings - credentialsProvider:_credentialsProvider.get() - userExecutor:std::move(userExecutor) - workerQueue:std::move(_workerQueue)]; - } - } + return _firestore->client(); } - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath { @@ -256,9 +190,7 @@ - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath { collectionPath); } - [self ensureClientConfigured]; - const ResourcePath path = ResourcePath::FromString(util::MakeString(collectionPath)); - return [FIRCollectionReference referenceWithPath:path firestore:self]; + return _firestore->GetCollection(util::MakeString(collectionPath)); } - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath { @@ -269,24 +201,12 @@ - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath { FSTThrowInvalidArgument(@"Invalid path (%@). Paths must not contain // in them.", documentPath); } - [self ensureClientConfigured]; - const ResourcePath path = ResourcePath::FromString(util::MakeString(documentPath)); - return [FIRDocumentReference referenceWithPath:path firestore:self]; + DocumentReference documentReference = _firestore->GetDocument(util::MakeString(documentPath)); + return [[FIRDocumentReference alloc] initWithReference:std::move(documentReference)]; } -- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID { - if (!collectionID) { - FSTThrowInvalidArgument(@"Collection ID cannot be nil."); - } - if ([collectionID containsString:@"/"]) { - FSTThrowInvalidArgument( - @"Invalid collection ID (%@). Collection IDs must not contain / in them.", collectionID); - } - - [self ensureClientConfigured]; - return [FIRQuery referenceWithQuery:[FSTQuery queryWithPath:ResourcePath::Empty() - collectionGroup:collectionID] - firestore:self]; +- (FIRWriteBatch *)batch { + return _firestore->GetBatch(); } - (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **))updateBlock @@ -301,29 +221,7 @@ - (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **)) FSTThrowInvalidArgument(@"Transaction completion block cannot be nil."); } - FSTTransactionBlock wrappedUpdate = - ^(std::shared_ptr internalTransaction, - void (^internalCompletion)(id _Nullable, NSError *_Nullable)) { - FIRTransaction *transaction = - [FIRTransaction transactionWithInternalTransaction:std::move(internalTransaction) - firestore:self]; - dispatch_async(queue, ^{ - NSError *_Nullable error = nil; - id _Nullable result = updateBlock(transaction, &error); - if (error) { - // Force the result to be nil in the case of an error, in case the user set both. - result = nil; - } - internalCompletion(result, error); - }); - }; - [self.client transactionWithRetries:5 updateBlock:wrappedUpdate completion:completion]; -} - -- (FIRWriteBatch *)batch { - [self ensureClientConfigured]; - - return [FIRWriteBatch writeBatchWithFirestore:self]; + _firestore->RunTransaction(updateBlock, queue, completion); } - (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **error))updateBlock @@ -340,38 +238,56 @@ - (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **er completion:completion]; } -- (void)shutdownWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { - if (!_client) { - if (completion) { - // We should be dispatching the callback on the user dispatch queue but if the client is nil - // here that queue was never created. - completion(nil); - } - } else { - [_client shutdownWithCompletion:completion]; - } -} - -+ (BOOL)isLoggingEnabled { - return FIRIsLoggableLevel(FIRLoggerLevelDebug, NO); -} - + (void)enableLogging:(BOOL)logging { FIRSetLoggerLevel(logging ? FIRLoggerLevelDebug : FIRLoggerLevelNotice); } - (void)enableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { - [self ensureClientConfigured]; - [self.client enableNetworkWithCompletion:completion]; + _firestore->EnableNetwork(completion); } - (void)disableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable))completion { - [self ensureClientConfigured]; - [self.client disableNetworkWithCompletion:completion]; + _firestore->DisableNetwork(completion); +} + +@end + +@implementation FIRFirestore (Internal) + +- (Firestore *)wrapped { + return _firestore.get(); +} + +- (AsyncQueue *)workerQueue { + return _firestore->worker_queue(); } - (const DatabaseId *)databaseID { - return &_databaseID; + return &_firestore->database_id(); +} + ++ (BOOL)isLoggingEnabled { + return FIRIsLoggableLevel(FIRLoggerLevelDebug, NO); +} + ++ (FIRFirestore *)recoverFromFirestore:(Firestore *)firestore { + return (__bridge FIRFirestore *)firestore->extension(); +} + +- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID { + if (!collectionID) { + FSTThrowInvalidArgument(@"Collection ID cannot be nil."); + } + if ([collectionID containsString:@"/"]) { + FSTThrowInvalidArgument( + @"Invalid collection ID (%@). Collection IDs must not contain / in them.", collectionID); + } + + return _firestore->GetCollectionGroup(collectionID); +} + +- (void)shutdownWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { + _firestore->Shutdown(completion); } @end diff --git a/Firestore/Source/API/FIRQuerySnapshot.mm b/Firestore/Source/API/FIRQuerySnapshot.mm index c513f29c212..942a7e684ed 100644 --- a/Firestore/Source/API/FIRQuerySnapshot.mm +++ b/Firestore/Source/API/FIRQuerySnapshot.mm @@ -18,10 +18,10 @@ #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" -#import "FIRFirestore.h" #import "FIRSnapshotMetadata.h" #import "Firestore/Source/API/FIRDocumentChange+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" @@ -30,6 +30,7 @@ #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +using firebase::firestore::api::Firestore; using firebase::firestore::core::ViewSnapshot; NS_ASSUME_NONNULL_BEGIN @@ -135,17 +136,17 @@ - (NSInteger)count { - (NSArray *)documents { if (!_documents) { FSTDocumentSet *documentSet = _snapshot.documents(); - FIRFirestore *firestore = self.firestore; + Firestore *firestore = self.firestore.wrapped; BOOL fromCache = self.metadata.fromCache; NSMutableArray *result = [NSMutableArray array]; for (FSTDocument *document in documentSet.documentEnumerator) { - [result addObject:[FIRQueryDocumentSnapshot - snapshotWithFirestore:firestore - documentKey:document.key - document:document - fromCache:fromCache - hasPendingWrites:_snapshot.mutated_keys().contains(document.key)]]; + [result addObject:[[FIRQueryDocumentSnapshot alloc] + initWithFirestore:firestore + documentKey:document.key + document:document + fromCache:fromCache + hasPendingWrites:_snapshot.mutated_keys().contains(document.key)]]; } _documents = result; @@ -168,7 +169,7 @@ - (NSInteger)count { if (!_documentChanges || _documentChangesIncludeMetadataChanges != includeMetadataChanges) { _documentChanges = [FIRDocumentChange documentChangesForSnapshot:_snapshot includeMetadataChanges:includeMetadataChanges - firestore:self.firestore]; + firestore:self.firestore.wrapped]; _documentChangesIncludeMetadataChanges = includeMetadataChanges; } return _documentChanges; diff --git a/Firestore/Source/API/FIRTransaction.mm b/Firestore/Source/API/FIRTransaction.mm index 044c652dc3e..b0d0f9fee9f 100644 --- a/Firestore/Source/API/FIRTransaction.mm +++ b/Firestore/Source/API/FIRTransaction.mm @@ -127,19 +127,20 @@ - (void)getDocument:(FIRDocumentReference *)document HARD_ASSERT(documents.size() == 1, "Mismatch in docs returned from document lookup."); FSTMaybeDocument *internalDoc = documents.front(); if ([internalDoc isKindOfClass:[FSTDeletedDocument class]]) { - FIRDocumentSnapshot *doc = [FIRDocumentSnapshot snapshotWithFirestore:self.firestore - documentKey:document.key - document:nil - fromCache:NO - hasPendingWrites:NO]; + FIRDocumentSnapshot *doc = + [[FIRDocumentSnapshot alloc] initWithFirestore:self.firestore.wrapped + documentKey:document.key + document:nil + fromCache:false + hasPendingWrites:false]; completion(doc, nil); } else if ([internalDoc isKindOfClass:[FSTDocument class]]) { FIRDocumentSnapshot *doc = - [FIRDocumentSnapshot snapshotWithFirestore:self.firestore - documentKey:internalDoc.key - document:(FSTDocument *)internalDoc - fromCache:NO - hasPendingWrites:NO]; + [[FIRDocumentSnapshot alloc] initWithFirestore:self.firestore.wrapped + documentKey:internalDoc.key + document:(FSTDocument *)internalDoc + fromCache:false + hasPendingWrites:false]; completion(doc, nil); } else { HARD_FAIL("BatchGetDocumentsRequest returned unexpected document type: %s", diff --git a/Firestore/Source/API/FSTFirestoreComponent.mm b/Firestore/Source/API/FSTFirestoreComponent.mm index 4791c121442..2d676d96c4d 100644 --- a/Firestore/Source/API/FSTFirestoreComponent.mm +++ b/Firestore/Source/API/FSTFirestoreComponent.mm @@ -31,6 +31,7 @@ #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/include/firebase/firestore/firestore_version.h" +#include "Firestore/core/src/firebase/firestore/api/firestore.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" @@ -39,6 +40,7 @@ #include "absl/memory/memory.h" namespace util = firebase::firestore::util; +using firebase::firestore::api::Firestore; using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::auth::FirebaseCredentialsProvider; using util::AsyncQueue; @@ -90,15 +92,14 @@ - (FIRFirestore *)firestoreForDatabase:(NSString *)database { auto workerQueue = absl::make_unique(std::move(executor)); id auth = FIR_COMPONENT(FIRAuthInterop, self.app.container); - std::unique_ptr credentials_provider = - absl::make_unique(self.app, auth); + auto credentialsProvider = absl::make_unique(self.app, auth); - NSString *persistenceKey = self.app.name; - NSString *projectID = self.app.options.projectID; - firestore = [[FIRFirestore alloc] initWithProjectID:util::MakeString(projectID) + std::string projectID = util::MakeString(self.app.options.projectID); + std::string persistenceKey = util::MakeString(self.app.name); + firestore = [[FIRFirestore alloc] initWithProjectID:std::move(projectID) database:util::MakeString(database) - persistenceKey:persistenceKey - credentialsProvider:std::move(credentials_provider) + persistenceKey:std::move(persistenceKey) + credentialsProvider:std::move(credentialsProvider) workerQueue:std::move(workerQueue) firebaseApp:self.app]; _instances[key] = firestore; diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index a432cb2dc6f..3b4e786107e 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -22,12 +22,14 @@ #import "Firestore/Source/Core/FSTTypes.h" #include "Firestore/core/src/firebase/firestore/api/document_reference.h" +#include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" #include "Firestore/core/src/firebase/firestore/util/executor.h" +#include "Firestore/core/src/firebase/firestore/util/statusor_callback.h" @class FIRDocumentReference; @class FIRDocumentSnapshot; @@ -90,8 +92,8 @@ NS_ASSUME_NONNULL_BEGIN * doesn't exist, an error will be sent to the completion. */ - (void)getDocumentFromLocalCache:(const firebase::firestore::api::DocumentReference &)doc - completion:(void (^)(FIRDocumentSnapshot *_Nullable document, - NSError *_Nullable error))completion; + completion:(firebase::firestore::util::StatusOrCallback< + firebase::firestore::api::DocumentSnapshot> &&)completion; /** * Retrieves a (possibly empty) set of documents from the cache via the diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index 21bedf095f3..bd05eeb4214 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -50,11 +50,15 @@ #include "Firestore/core/src/firebase/firestore/util/async_queue.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" +#include "Firestore/core/src/firebase/firestore/util/statusor.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "absl/memory/memory.h" namespace util = firebase::firestore::util; +using firebase::firestore::FirestoreErrorCode; using firebase::firestore::api::DocumentReference; +using firebase::firestore::api::DocumentSnapshot; using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::auth::User; using firebase::firestore::core::DatabaseInfo; @@ -73,6 +77,9 @@ using firebase::firestore::util::AsyncQueue; using firebase::firestore::util::DelayedOperation; using firebase::firestore::util::Executor; +using firebase::firestore::util::Status; +using firebase::firestore::util::StatusOr; +using firebase::firestore::util::StatusOrCallback; using firebase::firestore::util::TimerId; NS_ASSUME_NONNULL_BEGIN @@ -322,40 +329,30 @@ - (void)removeListener:(FSTQueryListener *)listener { } - (void)getDocumentFromLocalCache:(const DocumentReference &)doc - completion:(void (^)(FIRDocumentSnapshot *_Nullable document, - NSError *_Nullable error))completion { + completion:(StatusOrCallback &&)completion { _workerQueue->Enqueue([self, doc, completion] { FSTMaybeDocument *maybeDoc = [self.localStore readDocument:doc.key()]; - FIRDocumentSnapshot *_Nullable result = nil; - NSError *_Nullable error = nil; + StatusOr maybe_snapshot; if ([maybeDoc isKindOfClass:[FSTDocument class]]) { FSTDocument *document = (FSTDocument *)maybeDoc; - result = [FIRDocumentSnapshot snapshotWithFirestore:doc.firestore() - documentKey:doc.key() - document:document - fromCache:YES - hasPendingWrites:document.hasLocalMutations]; + maybe_snapshot = + DocumentSnapshot{doc.firestore(), doc.key(), document, + /*from_cache=*/true, + /*has_pending_writes=*/static_cast(document.hasLocalMutations)}; } else if ([maybeDoc isKindOfClass:[FSTDeletedDocument class]]) { - result = [FIRDocumentSnapshot snapshotWithFirestore:doc.firestore() - documentKey:doc.key() - document:nil - fromCache:YES - hasPendingWrites:NO]; + maybe_snapshot = DocumentSnapshot{doc.firestore(), doc.key(), nil, + /*from_cache=*/true, + /*has_pending_writes=*/false}; } else { - error = [NSError errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeUnavailable - userInfo:@{ - NSLocalizedDescriptionKey : - @"Failed to get document from cache. (However, this document " - @"may exist on the server. Run again without setting source to " - @"FIRFirestoreSourceCache to attempt to retrieve the document " - @"from the server.)", - }]; + maybe_snapshot = Status{FirestoreErrorCode::Unavailable, + "Failed to get document from cache. (However, this document " + "may exist on the server. Run again without setting source to " + "FIRFirestoreSourceCache to attempt to retrieve the document "}; } if (completion) { - self->_userExecutor->Execute([=] { completion(result, error); }); + self->_userExecutor->Execute([=] { completion(std::move(maybe_snapshot)); }); } }); } diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.h b/Firestore/core/src/firebase/firestore/api/document_reference.h index 45f6bdb55b2..d2bab6464ca 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.h +++ b/Firestore/core/src/firebase/firestore/api/document_reference.h @@ -31,11 +31,13 @@ #import "FIRFirestoreSource.h" #import "FIRListenerRegistration.h" +#include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/statusor_callback.h" NS_ASSUME_NONNULL_BEGIN -@class FIRCollectionReference; @class FIRFirestore; @class FSTListenOptions; @class FSTMutation; @@ -44,6 +46,8 @@ namespace firebase { namespace firestore { namespace api { +class Firestore; + class DocumentReference { public: using Completion = void (^)(NSError* _Nullable error) _Nullable; @@ -51,13 +55,14 @@ class DocumentReference { NSError* _Nullable error) _Nullable; DocumentReference() = default; - DocumentReference(FIRFirestore* firestore, model::DocumentKey document_key) + DocumentReference(model::ResourcePath path, Firestore* firestore); + DocumentReference(model::DocumentKey document_key, Firestore* firestore) : firestore_{firestore}, key_{std::move(document_key)} { } size_t Hash() const; - FIRFirestore* firestore() const { + Firestore* firestore() const { return firestore_; } const model::DocumentKey& key() const { @@ -66,12 +71,14 @@ class DocumentReference { const std::string& document_id() const; - FIRCollectionReference* Parent() const; + // TODO(varconst) uncomment when core API CollectionReference is implemented. + // CollectionReference Parent() const; std::string Path() const; - FIRCollectionReference* GetCollectionReference( - const std::string& collection_path) const; + // TODO(varconst) uncomment when core API CollectionReference is implemented. + // CollectionReference GetCollectionReference( + // const std::string& collection_path) const; void SetData(std::vector&& mutations, Completion completion); @@ -79,13 +86,15 @@ class DocumentReference { void DeleteDocument(Completion completion); - void GetDocument(FIRFirestoreSource source, DocumentCompletion completion); + void GetDocument(FIRFirestoreSource source, + util::StatusOrCallback&& completion); id AddSnapshotListener( - FIRDocumentSnapshotBlock listener, FSTListenOptions* options); + util::StatusOrCallback&& listener, + FSTListenOptions* options); private: - FIRFirestore* firestore_ = nil; + Firestore* firestore_ = nullptr; model::DocumentKey key_; }; diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index bd150a5fe1f..198d9305ede 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -16,9 +16,10 @@ #include "Firestore/core/src/firebase/firestore/api/document_reference.h" +#include + #import "FIRSnapshotMetadata.h" -#import "Firestore/Source/API/FIRCollectionReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" @@ -38,6 +39,8 @@ #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" #include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" +#include "Firestore/core/src/firebase/firestore/util/statusor.h" NS_ASSUME_NONNULL_BEGIN @@ -52,7 +55,21 @@ using model::Precondition; using model::ResourcePath; using util::MakeNSError; +using util::Status; using util::StatusOr; +using util::StatusOrCallback; + +DocumentReference::DocumentReference(model::ResourcePath path, + Firestore* firestore) + : firestore_{firestore} { + if (path.size() % 2 != 0) { + HARD_FAIL( + "Invalid document reference. Document references must have an even " + "number of segments, but %s has %s", + path.CanonicalString(), path.size()); + } + key_ = DocumentKey{std::move(path)}; +} size_t DocumentReference::Hash() const { return util::Hash(firestore_, key_); @@ -62,44 +79,48 @@ return key_.path().last_segment(); } -FIRCollectionReference* DocumentReference::Parent() const { - return [FIRCollectionReference referenceWithPath:key_.path().PopLast() - firestore:firestore_]; -} +// TODO(varconst) uncomment when core API CollectionReference is implemented. +// CollectionReference DocumentReference::Parent() const { +// return CollectionReference{firestore_, key_.path().PopLast()}; +// } std::string DocumentReference::Path() const { return key_.path().CanonicalString(); } -FIRCollectionReference* DocumentReference::GetCollectionReference( - const std::string& collection_path) const { - ResourcePath sub_path = ResourcePath::FromString(collection_path); - ResourcePath path = key_.path().Append(sub_path); - return [FIRCollectionReference referenceWithPath:path firestore:firestore_]; -} +// TODO(varconst) uncomment when core API CollectionReference is implemented. +// CollectionReference DocumentReference::GetCollectionReference( +// const std::string& collection_path) const { +// ResourcePath sub_path = ResourcePath::FromString(collection_path); +// ResourcePath path = key_.path().Append(sub_path); +// return CollectionReference{firestore_, path}; +// } void DocumentReference::SetData(std::vector&& mutations, Completion completion) { - [firestore_.client writeMutations:std::move(mutations) completion:completion]; + [firestore_->client() writeMutations:std::move(mutations) + completion:completion]; } void DocumentReference::UpdateData(std::vector&& mutations, Completion completion) { - return [firestore_.client writeMutations:std::move(mutations) - completion:completion]; + return [firestore_->client() writeMutations:std::move(mutations) + completion:completion]; } void DocumentReference::DeleteDocument(Completion completion) { FSTDeleteMutation* mutation = [[FSTDeleteMutation alloc] initWithKey:key_ precondition:Precondition::None()]; - [firestore_.client writeMutations:{mutation} completion:completion]; + [firestore_->client() writeMutations:{mutation} completion:completion]; } -void DocumentReference::GetDocument(FIRFirestoreSource source, - DocumentCompletion completion) { +void DocumentReference::GetDocument( + FIRFirestoreSource source, + StatusOrCallback&& completion) { if (source == FIRFirestoreSourceCache) { - [firestore_.client getDocumentFromLocalCache:*this completion:completion]; + [firestore_->client() getDocumentFromLocalCache:*this + completion:std::move(completion)]; return; } @@ -113,76 +134,67 @@ // https://github.com/firebase/firebase-ios-sdk/blob/3ccbdcdc65c93c4621c045c3c6d15de9dcefa23f/Firestore/Source/Core/FSTFirestoreClient.mm#L161 // for an example. dispatch_semaphore_t registered = dispatch_semaphore_create(0); - __block id listener_registration; - FIRDocumentSnapshotBlock listener = ^(FIRDocumentSnapshot* snapshot, - NSError* error) { - if (error) { - completion(nil, error); - return; - } - - // Remove query first before passing event to user to avoid user actions - // affecting the now stale query. - dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER); - [listener_registration remove]; - - if (!snapshot.exists && snapshot.metadata.fromCache) { - // TODO(dimond): Reconsider how to raise missing documents when offline. - // If we're online and the document doesn't exist then we call the - // completion with a document with document.exists set to false. If we're - // offline however, we call the completion handler with an error. Two - // options: - // 1) Cache the negative response from the server so we can deliver that - // even when you're offline. - // 2) Actually call the completion handler with an error if the document - // doesn't exist when you are offline. - completion(nil, [NSError errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeUnavailable - userInfo:@{ - NSLocalizedDescriptionKey : - @"Failed to get document because " - @"the client is offline.", - }]); - } else if (snapshot.exists && snapshot.metadata.fromCache && - source == FIRFirestoreSourceServer) { - completion( - nil, - [NSError - errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeUnavailable - userInfo:@{ - NSLocalizedDescriptionKey : - @"Failed to get document from server. (However, " - @"this document does exist in the local cache. Run " - @"again without setting source to " - @"FIRFirestoreSourceServer to retrieve the cached " - @"document.)" - }]); - } else { - completion(snapshot, nil); - } - }; - - listener_registration = AddSnapshotListener(listener, options); + auto listener_registration = std::make_shared>(); + StatusOrCallback listener = + [listener_registration, registered, completion, + source](StatusOr maybe_snapshot) { + if (!maybe_snapshot.ok()) { + completion(std::move(maybe_snapshot)); + return; + } + + DocumentSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie(); + + // Remove query first before passing event to user to avoid user actions + // affecting the now stale query. + dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER); + [*listener_registration remove]; + + if (!snapshot.exists() && snapshot.GetMetadata().fromCache) { + // TODO(dimond): Reconsider how to raise missing documents when + // offline. If we're online and the document doesn't exist then we + // call the completion with a document with document.exists set to + // false. If we're offline however, we call the completion handler + // with an error. Two options: 1) Cache the negative response from the + // server so we can deliver that even when you're offline. + // 2) Actually call the completion handler with an error if the + // document doesn't exist when you are offline. + completion( + Status{FirestoreErrorCode::Unavailable, + "Failed to get document because the client is offline."}); + } else if (snapshot.exists() && snapshot.GetMetadata().fromCache && + source == FIRFirestoreSourceServer) { + completion(Status{FirestoreErrorCode::Unavailable, + "Failed to get document from server. (However, " + "this document does exist in the local cache. Run " + "again without setting source to " + "FIRFirestoreSourceServer to retrieve the cached " + "document.)"}); + } else { + completion(std::move(snapshot)); + } + }; + + *listener_registration = AddSnapshotListener(std::move(listener), options); dispatch_semaphore_signal(registered); } id DocumentReference::AddSnapshotListener( - FIRDocumentSnapshotBlock listener, FSTListenOptions* options) { - FIRFirestore* firestore = firestore_; + StatusOrCallback&& listener, FSTListenOptions* options) { + Firestore* firestore = firestore_; FSTQuery* query = [FSTQuery queryWithPath:key_.path()]; DocumentKey key = key_; ViewSnapshotHandler handler = [key, listener, firestore](const StatusOr& maybe_snapshot) { if (!maybe_snapshot.ok()) { - listener(nil, MakeNSError(maybe_snapshot.status())); + listener(maybe_snapshot.status()); return; } const ViewSnapshot& snapshot = maybe_snapshot.ValueOrDie(); HARD_ASSERT(snapshot.documents().count <= 1, - "Too many document returned on a document query"); + "Too many documents returned on a document query"); FSTDocument* document = [snapshot.documents() documentForKey:key]; bool has_pending_writes = @@ -191,31 +203,26 @@ // We don't raise `has_pending_writes` for deleted documents. : false; - FIRDocumentSnapshot* result = - [FIRDocumentSnapshot snapshotWithFirestore:firestore - documentKey:key - document:document - fromCache:snapshot.from_cache() - hasPendingWrites:has_pending_writes]; - listener(result, nil); + DocumentSnapshot result{firestore, std::move(key), document, + snapshot.from_cache(), has_pending_writes}; + listener(std::move(result)); }; FSTAsyncQueryListener* async_listener = [[FSTAsyncQueryListener alloc] - initWithExecutor:firestore_.client.userExecutor + initWithExecutor:firestore_->client().userExecutor snapshotHandler:std::move(handler)]; - FSTQueryListener* internal_listener = - [firestore.client listenToQuery:query - options:options - viewSnapshotHandler:[async_listener asyncSnapshotHandler]]; - return [[FSTListenerRegistration alloc] initWithClient:firestore_.client + FSTQueryListener* internal_listener = [firestore_->client() + listenToQuery:query + options:options + viewSnapshotHandler:[async_listener asyncSnapshotHandler]]; + return [[FSTListenerRegistration alloc] initWithClient:firestore_->client() asyncListener:async_listener internalListener:internal_listener]; } bool operator==(const DocumentReference& lhs, const DocumentReference& rhs) { - return objc::Equals(lhs.firestore(), rhs.firestore()) && - lhs.key() == rhs.key(); + return lhs.firestore() == rhs.firestore() && lhs.key() == rhs.key(); } } // namespace api diff --git a/Firestore/core/src/firebase/firestore/api/document_snapshot.h b/Firestore/core/src/firebase/firestore/api/document_snapshot.h index 3fa44697b0a..3b60ae2ffc0 100644 --- a/Firestore/core/src/firebase/firestore/api/document_snapshot.h +++ b/Firestore/core/src/firebase/firestore/api/document_snapshot.h @@ -33,20 +33,21 @@ NS_ASSUME_NONNULL_BEGIN -@class FIRFirestore; @class FIRSnapshotMetadata; -@class FIRDocumentReference; @class FSTDocument; namespace firebase { namespace firestore { namespace api { +class DocumentReference; +class Firestore; + class DocumentSnapshot { public: DocumentSnapshot() = default; - DocumentSnapshot(FIRFirestore* firestore, + DocumentSnapshot(Firestore* firestore, model::DocumentKey document_key, FSTDocument* _Nullable document, bool from_cache, @@ -68,13 +69,13 @@ class DocumentSnapshot { } std::string document_id() const; - FIRDocumentReference* CreateReference() const; + DocumentReference CreateReference() const; FIRSnapshotMetadata* GetMetadata() const; FSTObjectValue* _Nullable GetData() const; id _Nullable GetValue(const model::FieldPath& field_path) const; - FIRFirestore* firestore() const { + Firestore* firestore() const { return firestore_; } @@ -82,7 +83,7 @@ class DocumentSnapshot { const DocumentSnapshot& rhs); private: - FIRFirestore* firestore_ = nil; + Firestore* firestore_ = nullptr; model::DocumentKey internal_key_; FSTDocument* internal_document_ = nil; bool from_cache_ = false; diff --git a/Firestore/core/src/firebase/firestore/api/document_snapshot.mm b/Firestore/core/src/firebase/firestore/api/document_snapshot.mm index 4d7d7ee5da2..e03436b1f95 100644 --- a/Firestore/core/src/firebase/firestore/api/document_snapshot.mm +++ b/Firestore/core/src/firebase/firestore/api/document_snapshot.mm @@ -16,8 +16,6 @@ #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" -#import "Firestore/Source/API/FIRFirestore+Internal.h" - #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Model/FSTDocument.h" @@ -40,9 +38,8 @@ has_pending_writes_); } -FIRDocumentReference* DocumentSnapshot::CreateReference() const { - return [FIRDocumentReference referenceWithKey:internal_key_ - firestore:firestore_]; +DocumentReference DocumentSnapshot::CreateReference() const { + return DocumentReference{internal_key_, firestore_}; } std::string DocumentSnapshot::document_id() const { @@ -67,7 +64,7 @@ } bool operator==(const DocumentSnapshot& lhs, const DocumentSnapshot& rhs) { - return objc::Equals(lhs.firestore_, rhs.firestore_) && + return lhs.firestore_ == rhs.firestore_ && lhs.internal_key_ == rhs.internal_key_ && objc::Equals(lhs.internal_document_, rhs.internal_document_) && lhs.from_cache_ == rhs.from_cache_ && diff --git a/Firestore/core/src/firebase/firestore/api/firestore.h b/Firestore/core/src/firebase/firestore/api/firestore.h new file mode 100644 index 00000000000..4be3b0bb4ae --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/firestore.h @@ -0,0 +1,129 @@ +/* + * Copyright 2019 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_API_FIRESTORE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_FIRESTORE_H_ + +#if !defined(__OBJC__) +#error "This header only supports Objective-C++" +#endif // !defined(__OBJC__) + +#import + +#include +#include // NOLINT(build/c++11) +#include +#include +#include "dispatch/dispatch.h" + +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/async_queue.h" + +NS_ASSUME_NONNULL_BEGIN + +@class FIRApp; +@class FIRCollectionReference; +@class FIRFirestore; +@class FIRFirestoreSettings; +@class FIRQuery; +@class FIRTransaction; +@class FIRWriteBatch; +@class FSTFirestoreClient; + +namespace firebase { +namespace firestore { +namespace api { + +class DocumentReference; + +class Firestore { + public: + using TransactionBlock = id _Nullable (^)(FIRTransaction*, NSError** error); + using ErrorCompletion = void (^)(NSError* _Nullable error); + using ResultOrErrorCompletion = void (^)(id _Nullable result, + NSError* _Nullable error); + + Firestore() = default; + + Firestore(std::string project_id, + std::string database, + std::string persistence_key, + std::unique_ptr credentials_provider, + std::unique_ptr worker_queue, + void* extension); + + const model::DatabaseId& database_id() const { + return database_id_; + } + + const std::string& persistence_key() const { + return persistence_key_; + } + + FSTFirestoreClient* client() { + return client_; + } + + util::AsyncQueue* worker_queue(); + + void* extension() { + return extension_; + } + + FIRFirestoreSettings* settings() const; + void set_settings(FIRFirestoreSettings* settings); + + FIRCollectionReference* GetCollection(absl::string_view collection_path); + DocumentReference GetDocument(absl::string_view document_path); + FIRWriteBatch* GetBatch(); + FIRQuery* GetCollectionGroup(NSString* collection_id); + + void RunTransaction(TransactionBlock update_block, + dispatch_queue_t queue, + ResultOrErrorCompletion completion); + + void Shutdown(ErrorCompletion completion); + + void EnableNetwork(ErrorCompletion completion); + void DisableNetwork(ErrorCompletion completion); + + private: + void EnsureClientConfigured(); + + model::DatabaseId database_id_; + std::unique_ptr credentials_provider_; + std::string persistence_key_; + FSTFirestoreClient* client_ = nil; + + // Ownership will be transferred to `FSTFirestoreClient` as soon as the + // client is created. + std::unique_ptr worker_queue_; + + void* extension_ = nullptr; + + FIRFirestoreSettings* settings_ = nil; + + mutable std::mutex mutex_; +}; + +} // namespace api +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_FIRESTORE_H_ diff --git a/Firestore/core/src/firebase/firestore/api/firestore.mm b/Firestore/core/src/firebase/firestore/api/firestore.mm new file mode 100644 index 00000000000..e248dbc4ba9 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/firestore.mm @@ -0,0 +1,202 @@ +/* + * Copyright 2019 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/api/firestore.h" + +#import "FIRFirestoreSettings.h" +#import "Firestore/Source/API/FIRCollectionReference+Internal.h" +#import "Firestore/Source/API/FIRDocumentReference+Internal.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/API/FIRQuery+Internal.h" +#import "Firestore/Source/API/FIRTransaction+Internal.h" +#import "Firestore/Source/API/FIRWriteBatch+Internal.h" +#import "Firestore/Source/Core/FSTFirestoreClient.h" +#import "Firestore/Source/Core/FSTQuery.h" + +#include "Firestore/core/src/firebase/firestore/api/document_reference.h" +#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" +#include "Firestore/core/src/firebase/firestore/core/transaction.h" +#include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/async_queue.h" +#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "absl/memory/memory.h" + +namespace firebase { +namespace firestore { +namespace api { + +using firebase::firestore::api::Firestore; +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::core::Transaction; +using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::ResourcePath; +using util::AsyncQueue; +using util::Executor; +using util::ExecutorLibdispatch; + +Firestore::Firestore(std::string project_id, + std::string database, + std::string persistence_key, + std::unique_ptr credentials_provider, + std::unique_ptr worker_queue, + void* extension) + : database_id_{std::move(project_id), std::move(database)}, + credentials_provider_{std::move(credentials_provider)}, + persistence_key_{std::move(persistence_key)}, + worker_queue_{std::move(worker_queue)}, + extension_{extension} { + settings_ = [[FIRFirestoreSettings alloc] init]; +} + +AsyncQueue* Firestore::worker_queue() { + return [client_ workerQueue]; +} + +FIRFirestoreSettings* Firestore::settings() const { + std::lock_guard lock{mutex_}; + // Disallow mutation of our internal settings + return [settings_ copy]; +} + +void Firestore::set_settings(FIRFirestoreSettings* settings) { + std::lock_guard lock{mutex_}; + // As a special exception, don't throw if the same settings are passed + // repeatedly. This should make it more friendly to create a Firestore + // instance. + if (client_ && ![settings_ isEqual:settings]) { + HARD_FAIL( + "Firestore instance has already been started and its settings can " + "no longer be changed. You can only set settings before calling any " + "other methods on a Firestore instance."); + } + settings_ = [settings copy]; +} + +FIRCollectionReference* Firestore::GetCollection( + absl::string_view collection_path) { + EnsureClientConfigured(); + ResourcePath path = ResourcePath::FromString(collection_path); + return [FIRCollectionReference + referenceWithPath:path + firestore:[FIRFirestore recoverFromFirestore:this]]; +} + +DocumentReference Firestore::GetDocument(absl::string_view document_path) { + EnsureClientConfigured(); + return DocumentReference{ResourcePath::FromString(document_path), this}; +} + +FIRWriteBatch* Firestore::GetBatch() { + EnsureClientConfigured(); + FIRFirestore* wrapper = [FIRFirestore recoverFromFirestore:this]; + + return [FIRWriteBatch writeBatchWithFirestore:wrapper]; +} + +FIRQuery* Firestore::GetCollectionGroup(NSString* collection_id) { + EnsureClientConfigured(); + FIRFirestore* wrapper = [FIRFirestore recoverFromFirestore:this]; + + return + [FIRQuery referenceWithQuery:[FSTQuery queryWithPath:ResourcePath::Empty() + collectionGroup:collection_id] + firestore:wrapper]; +} + +void Firestore::RunTransaction(TransactionBlock update_block, + dispatch_queue_t queue, + ResultOrErrorCompletion completion) { + EnsureClientConfigured(); + FIRFirestore* wrapper = [FIRFirestore recoverFromFirestore:this]; + + FSTTransactionBlock wrapped_update = + ^(std::shared_ptr internal_transaction, + void (^internal_completion)(id _Nullable, NSError* _Nullable)) { + FIRTransaction* transaction = [FIRTransaction + transactionWithInternalTransaction:std::move(internal_transaction) + firestore:wrapper]; + + dispatch_async(queue, ^{ + NSError* _Nullable error = nil; + id _Nullable result = update_block(transaction, &error); + if (error) { + // Force the result to be nil in the case of an error, in case the + // user set both. + result = nil; + } + internal_completion(result, error); + }); + }; + + [client_ transactionWithRetries:5 + updateBlock:wrapped_update + completion:completion]; +} + +void Firestore::Shutdown(ErrorCompletion completion) { + if (!client_) { + if (completion) { + // We should be dispatching the callback on the user dispatch queue + // but if the client is nil here that queue was never created. + completion(nil); + } + } else { + [client_ shutdownWithCompletion:completion]; + } +} + +void Firestore::EnableNetwork(ErrorCompletion completion) { + EnsureClientConfigured(); + [client_ enableNetworkWithCompletion:completion]; +} + +void Firestore::DisableNetwork(ErrorCompletion completion) { + EnsureClientConfigured(); + [client_ disableNetworkWithCompletion:completion]; +} + +void Firestore::EnsureClientConfigured() { + std::lock_guard lock{mutex_}; + + if (!client_) { + // These values are validated elsewhere; this is just double-checking: + HARD_ASSERT(settings_.host, "FirestoreSettings.host cannot be nil."); + HARD_ASSERT(settings_.dispatchQueue, + "FirestoreSettings.dispatchQueue cannot be nil."); + + DatabaseInfo database_info(database_id_, persistence_key_, + util::MakeString(settings_.host), + settings_.sslEnabled); + + std::unique_ptr user_executor = + absl::make_unique(settings_.dispatchQueue); + + HARD_ASSERT(worker_queue_, "Expected non-null worker queue"); + client_ = + [FSTFirestoreClient clientWithDatabaseInfo:database_info + settings:settings_ + credentialsProvider:credentials_provider_.get() + userExecutor:std::move(user_executor) + workerQueue:std::move(worker_queue_)]; + } +} + +} // namespace api +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/statusor_callback.h b/Firestore/core/src/firebase/firestore/util/statusor_callback.h new file mode 100644 index 00000000000..ff39b5df2ae --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/statusor_callback.h @@ -0,0 +1,35 @@ +/* + * Copyright 2019 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_UTIL_STATUSOR_CALLBACK_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STATUSOR_CALLBACK_H_ + +#include + +#include "Firestore/core/src/firebase/firestore/util/statusor.h" + +namespace firebase { +namespace firestore { +namespace util { + +template +using StatusOrCallback = std::function)>; + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STATUSOR_CALLBACK_H_ From 2ae939b6ebf6329984695c71514d28d475f62014 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Fri, 15 Mar 2019 16:24:20 -0700 Subject: [PATCH 048/214] Revert "Disable Facebook API tests (#2547)" (#2566) This reverts commit ed2b99a26c740bdc0f268ac3244b94271828a16d. --- Example/Auth/ApiTests/FacebookAuthTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/Auth/ApiTests/FacebookAuthTests.m b/Example/Auth/ApiTests/FacebookAuthTests.m index ba865bbb5f0..cb74fe71461 100644 --- a/Example/Auth/ApiTests/FacebookAuthTests.m +++ b/Example/Auth/ApiTests/FacebookAuthTests.m @@ -36,7 +36,7 @@ @interface FacebookAuthTests : FIRAuthApiTestsBase @implementation FacebookAuthTests -- (void)DISABLE_testSignInWithFaceboook { +- (void)testSignInWithFaceboook { FIRAuth *auth = [FIRAuth auth]; if (!auth) { XCTFail(@"Could not obtain auth object."); @@ -76,7 +76,7 @@ - (void)DISABLE_testSignInWithFaceboook { [self deleteFacebookTestingAccountbyId:facebookAccountId]; } -- (void)DISABLE_testLinkAnonymousAccountToFacebookAccount { +- (void)testLinkAnonymousAccountToFacebookAccount { FIRAuth *auth = [FIRAuth auth]; if (!auth) { XCTFail(@"Could not obtain auth object."); From 1e6305f494f39e312ca0881c9d60b6ba6e52e47f Mon Sep 17 00:00:00 2001 From: Gil Date: Fri, 15 Mar 2019 17:17:22 -0700 Subject: [PATCH 049/214] Travis: when linting Firestore, only lint iOS (#2569) * Enable strict mode in pod_lib_lint.sh Use bash arrays to make quoting work correctly. * Only check pod lib lint for iOS on each PR This cuts build time for Firestore in half and should help cut bring down total build times. Note that cronned pod lib lint still checks all platforms. --- .travis.yml | 2 +- scripts/pod_lib_lint.sh | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index b282222c9fe..76d56dc6d06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,7 +83,7 @@ jobs: script: # Eliminate the one warning from BoringSSL when CocoaPods 1.6.0 is available. # The travis_wait is necessary because the command takes more than 10 minutes. - - travis_wait 45 ./scripts/if_changed.sh ./scripts/pod_lib_lint.sh FirebaseFirestore.podspec --allow-warnings --no-subspecs + - travis_wait 25 ./scripts/if_changed.sh ./scripts/pod_lib_lint.sh FirebaseFirestore.podspec --platforms=ios --allow-warnings --no-subspecs # pod lib lint to check build and warnings for static library build - only on cron jobs - stage: test diff --git a/scripts/pod_lib_lint.sh b/scripts/pod_lib_lint.sh index f6d6f2e67b2..a9432c323a4 100755 --- a/scripts/pod_lib_lint.sh +++ b/scripts/pod_lib_lint.sh @@ -18,6 +18,8 @@ # # Runs pod lib lint for the given podspec +set -euo pipefail + if [[ $# -lt 1 ]]; then cat 1>&2 < Date: Fri, 15 Mar 2019 17:47:02 -0700 Subject: [PATCH 050/214] Revert "Revert "Disable Facebook API tests (#2547)" (#2566)" (#2573) This reverts commit 2ae939b6ebf6329984695c71514d28d475f62014. --- Example/Auth/ApiTests/FacebookAuthTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/Auth/ApiTests/FacebookAuthTests.m b/Example/Auth/ApiTests/FacebookAuthTests.m index cb74fe71461..ba865bbb5f0 100644 --- a/Example/Auth/ApiTests/FacebookAuthTests.m +++ b/Example/Auth/ApiTests/FacebookAuthTests.m @@ -36,7 +36,7 @@ @interface FacebookAuthTests : FIRAuthApiTestsBase @implementation FacebookAuthTests -- (void)testSignInWithFaceboook { +- (void)DISABLE_testSignInWithFaceboook { FIRAuth *auth = [FIRAuth auth]; if (!auth) { XCTFail(@"Could not obtain auth object."); @@ -76,7 +76,7 @@ - (void)testSignInWithFaceboook { [self deleteFacebookTestingAccountbyId:facebookAccountId]; } -- (void)testLinkAnonymousAccountToFacebookAccount { +- (void)DISABLE_testLinkAnonymousAccountToFacebookAccount { FIRAuth *auth = [FIRAuth auth]; if (!auth) { XCTFail(@"Could not obtain auth object."); From 966ad6878fd7456bf88d69826d757a509c406ec8 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 18 Mar 2019 12:13:33 -0700 Subject: [PATCH 051/214] Fix typos (#2576) --- Example/Auth/Tests/FIROAuthProviderTests.m | 2 +- Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m | 6 +++--- Firebase/Auth/Source/FIRAuthInternalErrors.h | 2 +- Firebase/Auth/Source/FIRUser.m | 2 +- Firebase/Auth/Source/Public/FIRAuth.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Example/Auth/Tests/FIROAuthProviderTests.m b/Example/Auth/Tests/FIROAuthProviderTests.m index 516b653c5d9..3284704c804 100644 --- a/Example/Auth/Tests/FIROAuthProviderTests.m +++ b/Example/Auth/Tests/FIROAuthProviderTests.m @@ -80,7 +80,7 @@ static NSString *const kFakeReverseClientID = @"com.googleusercontent.apps.123456"; /** @var kFakeOAuthResponseURL - @brief A fake OAuth reponse URL used in test. + @brief A fake OAuth response URL used in test. */ static NSString *const kFakeOAuthResponseURL = @"fakeOAuthResponseURL"; diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m index 8cd1e2811c5..5557c16f53d 100644 --- a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m +++ b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m @@ -37,7 +37,7 @@ /** @typedef FIRHeadfulLiteURLCallBack @brief The callback invoked at the end of the flow to fetch a headful-lite URL. @param headfulLiteURL The headful lite URL. - @param error The error that occured while fetching the headful-lite, if any. + @param error The error that occurred while fetching the headful-lite, if any. */ typedef void (^FIRHeadfulLiteURLCallBack)(NSURL *_Nullable headfulLiteURL, NSError *_Nullable error); @@ -167,7 +167,7 @@ - (void)getCredentialWithUIDelegate:(nullable id)UIDelegate #pragma mark - Internal Methods /** @fn initWithProviderID:auth: - @brief returns an instance of @c FIROAuthProvider assocaited with the provided auth instance. + @brief returns an instance of @c FIROAuthProvider associated with the provided auth instance. @param auth The Auth instance to be associated with the OAuthProvider instance. @return An Instance of @c FIROAuthProvider. */ @@ -314,7 +314,7 @@ - (nullable NSString *)customParametersStringWithError:(NSError *_Nullable *_Nul } /** @fn hashforString: - @brief Returns the SHA256 hash representaion of a given string object. + @brief Returns the SHA256 hash representation of a given string object. @param string The string for which a SHA256 hash is desired. @return An hexadecimal string representation of the SHA256 hash. */ diff --git a/Firebase/Auth/Source/FIRAuthInternalErrors.h b/Firebase/Auth/Source/FIRAuthInternalErrors.h index 0db204ca4b9..92f3b55ec9d 100644 --- a/Firebase/Auth/Source/FIRAuthInternalErrors.h +++ b/Firebase/Auth/Source/FIRAuthInternalErrors.h @@ -355,7 +355,7 @@ typedef NS_ENUM(NSInteger, FIRAuthInternalErrorCode) { FIRAuthInternalErrorCodeWebInternalError = FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeWebInternalError, - /** Indicates that an internal error occured within a SFSafariViewController or UIWebview. + /** Indicates that an internal error occurred within a SFSafariViewController or UIWebview. */ FIRAuthInternalErrorCodeWebSignInUserInteractionFailure = FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeWebSignInUserInteractionFailure, diff --git a/Firebase/Auth/Source/FIRUser.m b/Firebase/Auth/Source/FIRUser.m index cbe360751d5..9989614437f 100644 --- a/Firebase/Auth/Source/FIRUser.m +++ b/Firebase/Auth/Source/FIRUser.m @@ -1191,7 +1191,7 @@ - (void)unlinkFromProvider:(NSString *)provider return; } - // We can't just use the provider info objects in FIRSetAcccountInfoResponse because they + // We can't just use the provider info objects in FIRSetAccountInfoResponse because they // don't have localID and email fields. Remove the specific provider manually. NSMutableDictionary *mutableProviderData = [self->_providerData mutableCopy]; [mutableProviderData removeObjectForKey:provider]; diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h index 50d8ca58be1..25ee65dac84 100644 --- a/Firebase/Auth/Source/Public/FIRAuth.h +++ b/Firebase/Auth/Source/Public/FIRAuth.h @@ -487,7 +487,7 @@ NS_SWIFT_NAME(Auth)
  • @c FIRAuthErrorCodeWebNetworkRequestFailed - Indicates that a network request within a SFSafariViewController or UIWebview failed.
  • -
  • @c FIRAuthErrorCodeWebInternalError - Indicates that an internal error occured within a +
  • @c FIRAuthErrorCodeWebInternalError - Indicates that an internal error occurred within a SFSafariViewController or UIWebview.
  • @c FIRAuthErrorCodeWebSignInUserInteractionFailure - Indicates a general failure during From 128e1b0f1822c034c98df2799282f25dc65a1146 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Mon, 18 Mar 2019 13:06:32 -0700 Subject: [PATCH 052/214] Retain OAuthProvider instance in sample app (#2577) --- Example/Auth/Sample/MainViewController.m | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 9069d02de9c..6d74531b1b2 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -732,6 +732,16 @@ @implementation MainViewController { @brief The continue URL to be used in the next action code request. */ NSURL *_actionCodeContinueURL; + + /** @var _googleOAuthProvider + @brief OAuth provider instance for Google. + */ + FIROAuthProvider *_googleOAuthProvider; + + /** @var _microsoftOAuthProvider + @brief OAuth provider instance for Microsoft. + */ + FIROAuthProvider *_microsoftOAuthProvider; } /** @fn initWithNibName:bundle: @@ -744,6 +754,8 @@ - (id)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundl _actionCodeContinueURL = [NSURL URLWithString:KCONTINUE_URL]; _authStateDidChangeListeners = [NSMutableArray array]; _IDTokenDidChangeListeners = [NSMutableArray array]; + _googleOAuthProvider = [FIROAuthProvider providerWithProviderID:FIRGoogleAuthProviderID]; + _microsoftOAuthProvider = [FIROAuthProvider providerWithProviderID:@"microsoft.com"]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(authStateChangedForAuth:) name:FIRAuthStateDidChangeNotification @@ -1782,7 +1794,7 @@ - (void)signInGoogleAndRetrieveData { } - (void)signInGoogleProvider { - FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRGoogleAuthProviderID]; + FIROAuthProvider *provider = _googleOAuthProvider; provider.customParameters = @{ @"prompt" : @"consent", }; @@ -1816,7 +1828,7 @@ - (void)signInGoogleProvider { @brief Invoked when "Sign in with Google (headful-lite)" row is pressed. */ - (void)signInGoogleHeadfulLite { - FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRGoogleAuthProviderID]; + FIROAuthProvider *provider = _googleOAuthProvider; provider.customParameters = @{ @"prompt" : @"consent", }; @@ -1850,7 +1862,7 @@ - (void)signInGoogleHeadfulLite { @brief Invoked when "Sign in with Microsoft (headful-lite)" row is pressed. */ - (void)signInMicrosoftHeadfulLite { - FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:@"microsoft.com"]; + FIROAuthProvider *provider = _microsoftOAuthProvider; provider.customParameters = @{ @"prompt" : @"consent", @"login_hint" : @"tu8731@gmail.com", From accf22b3cdab4ee043ea7f7a539682c5267872ff Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 18 Mar 2019 14:35:59 -0700 Subject: [PATCH 053/214] Upgrade project to CocoaPods 1.6.1 (#2578) --- .../Firestore.xcodeproj/project.pbxproj | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 506f7b881dd..5f649b14c09 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -1635,7 +1635,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-iOS/openssl_grpc.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-iOS/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", @@ -1658,7 +1658,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 329C25E418360CEF62F6CB2B /* [CP] Embed Pods Frameworks */ = { @@ -1667,7 +1667,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/leveldb-library-iOS/leveldb.framework", "${BUILT_PRODUCTS_DIR}/GoogleTest/GoogleTest.framework", "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", @@ -1682,7 +1682,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 4C71ED5B5EF024AEF16B5E55 /* [CP] Embed Pods Frameworks */ = { @@ -1691,7 +1691,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/GoogleBenchmark/GoogleBenchmark.framework", ); name = "[CP] Embed Pods Frameworks"; @@ -1700,7 +1700,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 6A86E48DF663B6AA1CB5BA83 /* [CP] Embed Pods Frameworks */ = { @@ -1709,7 +1709,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-macOS_example/Pods-macOS_example-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-macOS_example/Pods-macOS_example-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-macOS/openssl_grpc.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-macOS/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities-Environment-Logger/GoogleUtilities.framework", @@ -1732,7 +1732,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-macOS_example/Pods-macOS_example-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-macOS_example/Pods-macOS_example-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 6E622C7A20F52C8300B7E93A /* Run Script */ = { @@ -1776,7 +1776,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Protobuf-iOS9.0/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/LibFuzzer/LibFuzzer.framework", ); @@ -1787,7 +1787,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 7C2467DCD3E3E16FB0A737DE /* [CP] Check Pods Manifest.lock */ = { @@ -1868,7 +1868,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/leveldb-library-iOS/leveldb.framework", "${BUILT_PRODUCTS_DIR}/GoogleTest/GoogleTest.framework", "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", @@ -1881,7 +1881,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; BF6384844477A4F850F0E89F /* [CP] Check Pods Manifest.lock */ = { @@ -1926,7 +1926,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-iOS/openssl_grpc.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-iOS/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", @@ -1949,7 +1949,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ From 2d943444af367ebb4c3f14c6ae19f45fc38d3173 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Mon, 18 Mar 2019 16:12:33 -0700 Subject: [PATCH 054/214] update the changelog (#2583) --- Firebase/Messaging/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Firebase/Messaging/CHANGELOG.md b/Firebase/Messaging/CHANGELOG.md index 2ca86182df8..be8cbc927d3 100644 --- a/Firebase/Messaging/CHANGELOG.md +++ b/Firebase/Messaging/CHANGELOG.md @@ -1,5 +1,4 @@ # 2019-03-19 -- v3.4.0 -- Adding image support for notification. Enable service extension and use FIRMessagingExtensionHelper API to display image on your notification. (#2491) - Adding community support for tvOS. (#2428) # 2019-03-05 -- v3.3.2 From 714bb34bc304122cc571fe3a3eef871787fd0618 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 18 Mar 2019 16:28:18 -0700 Subject: [PATCH 055/214] Revert #2491 (#2582) --- Example/Firebase.xcodeproj/project.pbxproj | 17 +-- .../Tests/FIRMessagingExtensionHelperTest.m | 114 ----------------- Firebase/Messaging/FIRMMessageCode.h | 6 - Firebase/Messaging/FIRMessaging.m | 10 -- .../Messaging/FIRMessagingExtensionHelper.m | 116 ------------------ Firebase/Messaging/Public/FIRMessaging.h | 13 -- .../Public/FIRMessagingExtensionHelper.h | 34 ----- Firebase/Messaging/Public/FirebaseMessaging.h | 1 - 8 files changed, 9 insertions(+), 302 deletions(-) delete mode 100644 Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m delete mode 100644 Firebase/Messaging/FIRMessagingExtensionHelper.m delete mode 100644 Firebase/Messaging/Public/FIRMessagingExtensionHelper.h diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 47a10da20fb..be4844b799f 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -124,6 +124,9 @@ 511DD27D2225C4D20094D78D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 511DD27C2225C4D20094D78D /* Assets.xcassets */; }; 511DD2802225C4D20094D78D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 511DD27F2225C4D20094D78D /* main.m */; }; 511DD2922225C8C40094D78D /* FIRInstanceIDWithFCMTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8DB550221F5B470068BB0E /* FIRInstanceIDWithFCMTest.m */; }; + 511DD2932225C8C40094D78D /* FIRMessagingFakeConnection.h in Sources */ = {isa = PBXBuildFile; fileRef = DE9315C81E8738B70083EDBF /* FIRMessagingFakeConnection.h */; }; + 511DD2942225C8C40094D78D /* FIRMessagingFakeSocket.h in Sources */ = {isa = PBXBuildFile; fileRef = DE9315CA1E8738B70083EDBF /* FIRMessagingFakeSocket.h */; }; + 511DD2952225C8C40094D78D /* FIRMessagingTestNotificationUtilities.h in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D61E8738B70083EDBF /* FIRMessagingTestNotificationUtilities.h */; }; 511DD2962225C8C40094D78D /* FIRMessagingClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315C31E8738B70083EDBF /* FIRMessagingClientTest.m */; }; 511DD2972225C8C40094D78D /* FIRMessagingCodedInputStreamTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315C41E8738B70083EDBF /* FIRMessagingCodedInputStreamTest.m */; }; 511DD2982225C8C40094D78D /* FIRMessagingConnectionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315C51E8738B70083EDBF /* FIRMessagingConnectionTest.m */; }; @@ -144,8 +147,8 @@ 511DD2A72225C8C40094D78D /* FIRMessagingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D51E8738B70083EDBF /* FIRMessagingTest.m */; }; 511DD2A82225C8C40094D78D /* FIRMessagingTestNotificationUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D71E8738B70083EDBF /* FIRMessagingTestNotificationUtilities.m */; }; 511DD2A92225C8C40094D78D /* FIRMessagingAnalyticsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE37C63A2163D5F30025D03E /* FIRMessagingAnalyticsTest.m */; }; + 511DD2AA2225C8D50094D78D /* FIRMessagingTestUtilities.h in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242A21EA364600BB24C6 /* FIRMessagingTestUtilities.h */; }; 511DD2AB2225C8D50094D78D /* FIRMessagingTestUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242B21EA364600BB24C6 /* FIRMessagingTestUtilities.m */; }; - 51559F8A2238B8DB00CFC32C /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */; }; 518854D92230652900CA4141 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 518854D82230652900CA4141 /* AppDelegate.m */; }; 518854DC2230652900CA4141 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 518854DB2230652900CA4141 /* ViewController.m */; }; 518854DF2230652900CA4141 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 518854DD2230652900CA4141 /* Main.storyboard */; }; @@ -173,7 +176,6 @@ 51885509223067E900CA4141 /* FIRInstanceIDTokenOperationsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE958BDB21F7DF0B00E6C1C5 /* FIRInstanceIDTokenOperationsTest.m */; }; 5188550A223067E900CA4141 /* FIRInstanceIDUtilitiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE958BE121F7DF0C00E6C1C5 /* FIRInstanceIDUtilitiesTest.m */; }; 5188550C2230873000CA4141 /* FIRInstanceIDAPNSInfoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE958BDA21F7DF0B00E6C1C5 /* FIRInstanceIDAPNSInfoTest.m */; }; - 5188550E2231E02400CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */; }; 7E21E0731F857DFC00D0AC1C /* FIROAuthProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E21E0721F857DFC00D0AC1C /* FIROAuthProviderTests.m */; }; 7E9485421F578AC4005A3939 /* FIRAuthURLPresenterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */; }; 7EE21F7A1FE89193009B1370 /* FIREmailLinkRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */; }; @@ -1076,7 +1078,6 @@ 518854E22230652B00CA4141 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 518854E32230652B00CA4141 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 518854EC223066BE00CA4141 /* InstanceID_Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InstanceID_Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRMessagingExtensionHelperTest.m; sourceTree = ""; }; 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -2538,7 +2539,6 @@ DE9315C21E8738B70083EDBF /* Tests */ = { isa = PBXGroup; children = ( - 5188550D2231E02300CA4141 /* FIRMessagingExtensionHelperTest.m */, DE8DB550221F5B470068BB0E /* FIRInstanceIDWithFCMTest.m */, DE9315C81E8738B70083EDBF /* FIRMessagingFakeConnection.h */, DE9315CA1E8738B70083EDBF /* FIRMessagingFakeSocket.h */, @@ -3790,7 +3790,6 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( - English, en, Base, "es-MX", @@ -4298,10 +4297,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 511DD2962225C8C40094D78D /* FIRMessagingClientTest.m in Sources */, - 51559F8A2238B8DB00CFC32C /* FIRMessagingExtensionHelperTest.m in Sources */, + 511DD2AA2225C8D50094D78D /* FIRMessagingTestUtilities.h in Sources */, 511DD2AB2225C8D50094D78D /* FIRMessagingTestUtilities.m in Sources */, 511DD2922225C8C40094D78D /* FIRInstanceIDWithFCMTest.m in Sources */, + 511DD2932225C8C40094D78D /* FIRMessagingFakeConnection.h in Sources */, + 511DD2942225C8C40094D78D /* FIRMessagingFakeSocket.h in Sources */, + 511DD2952225C8C40094D78D /* FIRMessagingTestNotificationUtilities.h in Sources */, + 511DD2962225C8C40094D78D /* FIRMessagingClientTest.m in Sources */, 511DD2972225C8C40094D78D /* FIRMessagingCodedInputStreamTest.m in Sources */, 511DD2982225C8C40094D78D /* FIRMessagingConnectionTest.m in Sources */, 511DD2992225C8C40094D78D /* FIRMessagingContextManagerServiceTest.m in Sources */, @@ -4919,7 +4921,6 @@ buildActionMask = 2147483647; files = ( DE9315F41E8738E60083EDBF /* FIRMessagingClientTest.m in Sources */, - 5188550E2231E02400CA4141 /* FIRMessagingExtensionHelperTest.m in Sources */, DE9315F51E8738E60083EDBF /* FIRMessagingCodedInputStreamTest.m in Sources */, DE9315F71E8738E60083EDBF /* FIRMessagingContextManagerServiceTest.m in Sources */, DE9315FD1E8738E60083EDBF /* FIRMessagingPubSubTest.m in Sources */, diff --git a/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m deleted file mode 100644 index e2275660bb6..00000000000 --- a/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2019 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 -#import - -#import - -#import "FIRMessaging.h" -#import "FIRMessagingExtensionHelper.h" - -typedef void (^FIRMessagingContentHandler)(UNNotificationContent *content); - -static NSString *const kFCMPayloadOptionsName = @"fcm_options"; -static NSString *const kFCMPayloadOptionsImageURLName = @"image"; -static NSString *const kValidImageURL = - @"https://firebasestorage.googleapis.com/v0/b/fcm-ios-f7f9c.appspot.com/o/" - @"chubbyBunny.jpg?alt=media&token=d6c56a57-c007-4b27-b20f-f267cc83e9e5"; - -@interface FIRMessagingExtensionHelper (ExposedForTest) -#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 -- (void)loadAttachmentForURL:(NSURL *)attachmentURL - completionHandler:(void (^)(UNNotificationAttachment *))completionHandler; -#endif -@end - -@interface FIRMessagingExtensionHelperTest : XCTestCase { - id _mockExtensionHelper; -} - -@end - -@implementation FIRMessagingExtensionHelperTest - -- (void)setUp { - [super setUp]; - FIRMessagingExtensionHelper *extensionHelper = [[FIRMessagingExtensionHelper alloc] init]; - _mockExtensionHelper = OCMPartialMock(extensionHelper); -} - -- (void)tearDown { - [_mockExtensionHelper stopMocking]; -} - -- (void)testModifyNotificationWithValidPayloadData { -#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - XCTestExpectation *validPayloadExpectation = - [self expectationWithDescription:@"Test payload is valid."]; - - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - content.userInfo = @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : kValidImageURL}}; - FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { - [validPayloadExpectation fulfill]; - }; - [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; - - OCMVerify([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] - completionHandler:[OCMArg any]]); - [self waitForExpectationsWithTimeout:1.0 handler:nil]; -#endif -} - -- (void)testModifyNotificationWithInvalidPayloadData { -#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - XCTestExpectation *validPayloadExpectation = - [self expectationWithDescription:@"Test payload is valid."]; - - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - content.userInfo = - @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : @"a invalid URL"}}; - FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { - [validPayloadExpectation fulfill]; - }; - [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; - - OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] - completionHandler:[OCMArg any]]); - [self waitForExpectationsWithTimeout:1.0 handler:nil]; -#endif -} - -- (void)testModifyNotificationWithEmptyPayloadData { -#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - XCTestExpectation *validPayloadExpectation = - [self expectationWithDescription:@"Test payload is valid."]; - - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - content.userInfo = - @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : @"a invalid URL"}}; - FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { - [validPayloadExpectation fulfill]; - }; - [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; - - OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] - completionHandler:[OCMArg any]]); - [self waitForExpectationsWithTimeout:1.0 handler:nil]; -#endif -} - -@end diff --git a/Firebase/Messaging/FIRMMessageCode.h b/Firebase/Messaging/FIRMMessageCode.h index 810c44c3042..4b16189affb 100644 --- a/Firebase/Messaging/FIRMMessageCode.h +++ b/Firebase/Messaging/FIRMMessageCode.h @@ -188,10 +188,4 @@ typedef NS_ENUM(NSInteger, FIRMessagingMessageCode) { kFIRMessagingMessageCodeAnalyticsInvalidEvent = 19006, // I-FCM019006 kFIRMessagingMessageCodeAnalytics007 = 19007, // I-FCM019007 kFIRMessagingMessageCodeAnalyticsCouldNotInvokeAnalyticsLog = 19008, // I-FCM019008 - // FIRMessagingExtensionHelper.m - kFIRMessagingServiceExtensionImageInvalidURL = 20000, - kFIRMessagingServiceExtensionImageNotDownloaded = 20001, - kFIRMessagingServiceExtensionLocalFileNotCreated = 20002, - kFIRMessagingServiceExtensionImageNotAttached = 20003, - }; diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 0c5c7fe00f7..d941014169a 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -29,7 +29,6 @@ #import "FIRMessagingContextManagerService.h" #import "FIRMessagingDataMessageManager.h" #import "FIRMessagingDefines.h" -#import "FIRMessagingExtensionHelper.h" #import "FIRMessagingLogger.h" #import "FIRMessagingPubSub.h" #import "FIRMessagingReceiver.h" @@ -179,15 +178,6 @@ + (FIRMessaging *)messaging { return messaging; } -+ (FIRMessagingExtensionHelper *)extensionHelper { - static dispatch_once_t once; - static FIRMessagingExtensionHelper *extensionHelper; - dispatch_once(&once, ^{ - extensionHelper = [[FIRMessagingExtensionHelper alloc] init]; - }); - return extensionHelper; -} - - (instancetype)initWithAnalytics:(nullable id)analytics withInstanceID:(FIRInstanceID *)instanceID withUserDefaults:(GULUserDefaults *)defaults { diff --git a/Firebase/Messaging/FIRMessagingExtensionHelper.m b/Firebase/Messaging/FIRMessagingExtensionHelper.m deleted file mode 100644 index 542bd7254b9..00000000000 --- a/Firebase/Messaging/FIRMessagingExtensionHelper.m +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2019 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 "FIRMessagingExtensionHelper.h" - -#import "FIRMMessageCode.h" -#import "FIRMessagingLogger.h" - -static NSString *const kPayloadOptionsName = @"fcm_options"; -static NSString *const kPayloadOptionsImageURLName = @"image"; - -@interface FIRMessagingExtensionHelper () -@property(nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); -@property(nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; - -@end - -@implementation FIRMessagingExtensionHelper - -- (void)populateNotificationContent:(UNMutableNotificationContent *)content - withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler { - self.contentHandler = [contentHandler copy]; - self.bestAttemptContent = content; - - NSString *currentImageURL = content.userInfo[kPayloadOptionsName][kPayloadOptionsImageURLName]; - if (!currentImageURL) { - [self deliverNotification]; - return; - } -#if TARGET_OS_IOS - NSURL *attachmentURL = [NSURL URLWithString:currentImageURL]; - if (attachmentURL) { - [self loadAttachmentForURL:attachmentURL - completionHandler:^(UNNotificationAttachment *attachment) { - self.bestAttemptContent.attachments = @[ attachment ]; - [self deliverNotification]; - }]; - } else { - FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageInvalidURL, - @"The Image URL provided is invalid %@.", currentImageURL); - [self deliverNotification]; - } -#else - [self deliverNotification]; -#endif -} - -#if TARGET_OS_IOS -- (void)loadAttachmentForURL:(NSURL *)attachmentURL - completionHandler:(void (^)(UNNotificationAttachment *))completionHandler { - __block UNNotificationAttachment *attachment = nil; - - NSURLSession *session = [NSURLSession - sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; - [[session - downloadTaskWithURL:attachmentURL - completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) { - if (error != nil) { - FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotDownloaded, - @"Failed to download image given URL %@, error: %@\n", - attachmentURL, error); - completionHandler(attachment); - return; - } - - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSString *fileExtension = - [NSString stringWithFormat:@".%@", [response.suggestedFilename pathExtension]]; - NSURL *localURL = [NSURL - fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExtension]]; - [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error]; - if (error) { - FIRMessagingLoggerError( - kFIRMessagingServiceExtensionLocalFileNotCreated, - @"Failed to move the image file to local location: %@, error: %@\n", localURL, - error); - completionHandler(attachment); - return; - } - - attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" - URL:localURL - options:nil - error:&error]; - if (error) { - FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotAttached, - @"Failed to create attachment with URL %@, error: %@\n", - localURL, error); - completionHandler(attachment); - return; - } - completionHandler(attachment); - }] resume]; -} -#endif - -- (void)deliverNotification { - if (self.contentHandler) { - self.contentHandler(self.bestAttemptContent); - } -} - -@end diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h index 10b84dd4d33..5b8e7ad4d2d 100644 --- a/Firebase/Messaging/Public/FIRMessaging.h +++ b/Firebase/Messaging/Public/FIRMessaging.h @@ -245,8 +245,6 @@ NS_SWIFT_NAME(MessagingRemoteMessage) @end @class FIRMessaging; -@class FIRMessagingExtensionHelper; - /** * A protocol to handle token update or data message delivery from FCM. * @@ -333,17 +331,6 @@ NS_SWIFT_NAME(Messaging) */ + (instancetype)messaging NS_SWIFT_NAME(messaging()); -/** - * FIRMessagingExtensionHelper - * - * Use FIRMessagingExtensionHelper to populate rich UI contents for your notifications. - * e.g. If an image URL is set in your notification payload or on the console, call - * FIRMessagingExtensionHelper API to render it on your notification. - * - * @return An instance of FIRMessagingExtensionHelper that handles the extensions API. - */ -+ (FIRMessagingExtensionHelper *)extensionHelper NS_SWIFT_NAME(serviceExtension()) NS_AVAILABLE_IOS(10.0); - /** * Unavailable. Use +messaging instead. */ diff --git a/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h b/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h deleted file mode 100644 index 51679da24ec..00000000000 --- a/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2019 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. - */ -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 -#import -#endif - -NS_ASSUME_NONNULL_BEGIN - -__IOS_AVAILABLE(10.0) -@interface FIRMessagingExtensionHelper : NSObject - -/// Call this API to complete your notification content modification. If you like to -/// overwrite some properties of the content instead of using the default payload, -/// make sure to make your customized motification to the content before passing it to -/// this call. -- (void)populateNotificationContent:(UNMutableNotificationContent *)content - withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firebase/Messaging/Public/FirebaseMessaging.h b/Firebase/Messaging/Public/FirebaseMessaging.h index c5d0bd05046..ef081c90f55 100755 --- a/Firebase/Messaging/Public/FirebaseMessaging.h +++ b/Firebase/Messaging/Public/FirebaseMessaging.h @@ -15,4 +15,3 @@ */ #import "FIRMessaging.h" -#import "FIRMessagingExtensionHelper.h" From e4dcc96e52a20085d7263ce6bbeb5958c5708aa5 Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 18 Mar 2019 17:28:46 -0700 Subject: [PATCH 056/214] Eliminate Objective-C BOOL in Firestore/core (#2574) * Add objc::EqualTo and objc::Hash These make it possible to use std::unordered_map with Objective-C object keys. * Use std::unordered_map in MemoryQueryCache * Fix objective-C library detection This ensures that .mm files are compiled with -fobjc-arc (among other things). * Remove BOOL in FSTDocument to eliminate static_cast --- .../Local/FSTLRUGarbageCollectorTests.mm | 2 +- Firestore/Source/Core/FSTFirestoreClient.mm | 7 +-- .../Source/Local/FSTLRUGarbageCollector.h | 10 ++-- .../Source/Local/FSTLRUGarbageCollector.mm | 13 ++--- Firestore/Source/Local/FSTLevelDB.mm | 21 +++---- .../Source/Local/FSTMemoryPersistence.mm | 18 +++--- Firestore/Source/Model/FSTDocument.h | 10 ++-- Firestore/Source/Model/FSTDocument.mm | 20 +++---- .../firebase/firestore/local/CMakeLists.txt | 21 ++++++- .../firestore/local/leveldb_query_cache.h | 9 +-- .../firestore/local/leveldb_query_cache.mm | 24 ++++---- .../firestore/local/memory_query_cache.h | 18 ++++-- .../firestore/local/memory_query_cache.mm | 53 ++++++++++-------- .../firebase/firestore/local/query_cache.h | 10 +++- .../firebase/firestore/util/CMakeLists.txt | 1 + .../firestore/util/objc_compatibility.h | 28 ++++++++++ .../util/objc_compatibility_apple_test.mm | 55 +++++++++++++++++++ cmake/cc_rules.cmake | 6 +- 18 files changed, 221 insertions(+), 105 deletions(-) diff --git a/Firestore/Example/Tests/Local/FSTLRUGarbageCollectorTests.mm b/Firestore/Example/Tests/Local/FSTLRUGarbageCollectorTests.mm index 41d185d4dea..c4c9915e719 100644 --- a/Firestore/Example/Tests/Local/FSTLRUGarbageCollectorTests.mm +++ b/Firestore/Example/Tests/Local/FSTLRUGarbageCollectorTests.mm @@ -398,7 +398,7 @@ - (void)testRemoveQueriesUpThroughSequenceNumber { XCTAssertEqual(10, removed); // Make sure we removed the even targets with targetID <= 20. _persistence.run("verify remaining targets are > 20 or odd", [&]() { - _queryCache->EnumerateTargets(^(FSTQueryData *queryData, BOOL *stop) { + _queryCache->EnumerateTargets([&](FSTQueryData *queryData) { XCTAssertTrue(queryData.targetID > 20 || queryData.targetID % 2 == 1); }); }); diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index bd05eeb4214..8b3ed242fdb 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -336,10 +336,9 @@ - (void)getDocumentFromLocalCache:(const DocumentReference &)doc if ([maybeDoc isKindOfClass:[FSTDocument class]]) { FSTDocument *document = (FSTDocument *)maybeDoc; - maybe_snapshot = - DocumentSnapshot{doc.firestore(), doc.key(), document, - /*from_cache=*/true, - /*has_pending_writes=*/static_cast(document.hasLocalMutations)}; + maybe_snapshot = DocumentSnapshot{doc.firestore(), doc.key(), document, + /*from_cache=*/true, + /*has_pending_writes=*/document.hasLocalMutations}; } else if ([maybeDoc isKindOfClass:[FSTDeletedDocument class]]) { maybe_snapshot = DocumentSnapshot{doc.firestore(), doc.key(), nil, /*from_cache=*/true, diff --git a/Firestore/Source/Local/FSTLRUGarbageCollector.h b/Firestore/Source/Local/FSTLRUGarbageCollector.h index 0b3cc7b3110..9d59813a94c 100644 --- a/Firestore/Source/Local/FSTLRUGarbageCollector.h +++ b/Firestore/Source/Local/FSTLRUGarbageCollector.h @@ -21,6 +21,8 @@ #import "FIRFirestoreSettings.h" #import "Firestore/Source/Local/FSTQueryData.h" + +#include "Firestore/core/src/firebase/firestore/local/query_cache.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/types.h" @@ -79,15 +81,13 @@ struct LruResults { * Enumerates all the targets that the delegate is aware of. This is typically all of the targets in * an FSTQueryCache. */ -- (void)enumerateTargetsUsingBlock:(void (^)(FSTQueryData *queryData, BOOL *stop))block; +- (void)enumerateTargetsUsingCallback:(const firebase::firestore::local::TargetCallback &)callback; /** * Enumerates all of the outstanding mutations. */ -- (void)enumerateMutationsUsingBlock: - (void (^)(const firebase::firestore::model::DocumentKey &key, - firebase::firestore::model::ListenSequenceNumber sequenceNumber, - BOOL *stop))block; +- (void)enumerateMutationsUsingCallback: + (const firebase::firestore::local::OrphanedDocumentCallback &)callback; /** * Removes all unreferenced documents from the cache that have a sequence number less than or equal diff --git a/Firestore/Source/Local/FSTLRUGarbageCollector.mm b/Firestore/Source/Local/FSTLRUGarbageCollector.mm index b47a833857a..95a86fabb56 100644 --- a/Firestore/Source/Local/FSTLRUGarbageCollector.mm +++ b/Firestore/Source/Local/FSTLRUGarbageCollector.mm @@ -159,14 +159,13 @@ - (ListenSequenceNumber)sequenceNumberForQueryCount:(NSUInteger)queryCount { return kFSTListenSequenceNumberInvalid; } RollingSequenceNumberBuffer buffer(queryCount); - // Pointer is necessary to access stack-allocated buffer from a block. - RollingSequenceNumberBuffer *ptr_to_buffer = &buffer; - [_delegate enumerateTargetsUsingBlock:^(FSTQueryData *queryData, BOOL *stop) { - ptr_to_buffer->AddElement(queryData.sequenceNumber); + + [_delegate enumerateTargetsUsingCallback:[&buffer](FSTQueryData *queryData) { + buffer.AddElement(queryData.sequenceNumber); }]; - [_delegate enumerateMutationsUsingBlock:^(const DocumentKey &docKey, - ListenSequenceNumber sequenceNumber, BOOL *stop) { - ptr_to_buffer->AddElement(sequenceNumber); + [_delegate enumerateMutationsUsingCallback:[&buffer](const DocumentKey &docKey, + ListenSequenceNumber sequenceNumber) { + buffer.AddElement(sequenceNumber); }]; return buffer.max_value(); } diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index 89a719fbb2f..478e68ab4e5 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -73,8 +73,10 @@ using firebase::firestore::local::LevelDbTransaction; using firebase::firestore::local::ListenSequence; using firebase::firestore::local::LruParams; +using firebase::firestore::local::OrphanedDocumentCallback; using firebase::firestore::local::ReferenceSet; using firebase::firestore::local::RemoteDocumentCache; +using firebase::firestore::local::TargetCallback; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::ListenSequenceNumber; @@ -210,19 +212,18 @@ - (BOOL)isPinned:(const DocumentKey &)docKey { return NO; } -- (void)enumerateTargetsUsingBlock:(void (^)(FSTQueryData *queryData, BOOL *stop))block { - _db.queryCache->EnumerateTargets(block); +- (void)enumerateTargetsUsingCallback:(const TargetCallback &)callback { + _db.queryCache->EnumerateTargets(callback); } -- (void)enumerateMutationsUsingBlock: - (void (^)(const DocumentKey &key, ListenSequenceNumber sequenceNumber, BOOL *stop))block { - _db.queryCache->EnumerateOrphanedDocuments(block); +- (void)enumerateMutationsUsingCallback:(const OrphanedDocumentCallback &)callback { + _db.queryCache->EnumerateOrphanedDocuments(callback); } - (int)removeOrphanedDocumentsThroughSequenceNumber:(ListenSequenceNumber)upperBound { - __block int count = 0; + int count = 0; _db.queryCache->EnumerateOrphanedDocuments( - ^(const DocumentKey &docKey, ListenSequenceNumber sequenceNumber, BOOL *stop) { + [&count, self, upperBound](const DocumentKey &docKey, ListenSequenceNumber sequenceNumber) { if (sequenceNumber <= upperBound) { if (![self isPinned:docKey]) { count++; @@ -245,9 +246,9 @@ - (int)removeTargetsThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber } - (size_t)sequenceNumberCount { - __block size_t totalCount = _db.queryCache->size(); - [self enumerateMutationsUsingBlock:^(const DocumentKey &key, ListenSequenceNumber sequenceNumber, - BOOL *stop) { + size_t totalCount = _db.queryCache->size(); + [self enumerateMutationsUsingCallback:[&totalCount](const DocumentKey &key, + ListenSequenceNumber sequenceNumber) { totalCount++; }]; return totalCount; diff --git a/Firestore/Source/Local/FSTMemoryPersistence.mm b/Firestore/Source/Local/FSTMemoryPersistence.mm index fa7b4657b24..d60f7e37f69 100644 --- a/Firestore/Source/Local/FSTMemoryPersistence.mm +++ b/Firestore/Source/Local/FSTMemoryPersistence.mm @@ -42,6 +42,7 @@ using firebase::firestore::local::MemoryQueryCache; using firebase::firestore::local::MemoryRemoteDocumentCache; using firebase::firestore::local::ReferenceSet; +using firebase::firestore::local::TargetCallback; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::DocumentKeyHash; using firebase::firestore::model::ListenSequenceNumber; @@ -234,20 +235,19 @@ - (void)commitTransaction { _currentSequenceNumber = kFSTListenSequenceNumberInvalid; } -- (void)enumerateTargetsUsingBlock:(void (^)(FSTQueryData *queryData, BOOL *stop))block { - return _persistence.queryCache->EnumerateTargets(block); +- (void)enumerateTargetsUsingCallback:(const TargetCallback &)callback { + return _persistence.queryCache->EnumerateTargets(callback); } -- (void)enumerateMutationsUsingBlock: - (void (^)(const DocumentKey &key, ListenSequenceNumber sequenceNumber, BOOL *stop))block { - BOOL stop = NO; +- (void)enumerateMutationsUsingCallback: + (const firebase::firestore::local::OrphanedDocumentCallback &)callback { for (const auto &entry : _sequenceNumbers) { ListenSequenceNumber sequenceNumber = entry.second; const DocumentKey &key = entry.first; // Pass in the exact sequence number as the upper bound so we know it won't be pinned by being // too recent. if (![self isPinnedAtSequenceNumber:sequenceNumber document:key]) { - block(key, sequenceNumber, &stop); + callback(key, sequenceNumber); } } } @@ -259,9 +259,9 @@ - (int)removeTargetsThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber } - (size_t)sequenceNumberCount { - __block size_t totalCount = _persistence.queryCache->size(); - [self enumerateMutationsUsingBlock:^(const DocumentKey &key, ListenSequenceNumber sequenceNumber, - BOOL *stop) { + size_t totalCount = _persistence.queryCache->size(); + [self enumerateMutationsUsingCallback:[&totalCount](const DocumentKey &key, + ListenSequenceNumber sequenceNumber) { totalCount++; }]; return totalCount; diff --git a/Firestore/Source/Model/FSTDocument.h b/Firestore/Source/Model/FSTDocument.h index add79dc9309..acbec89d528 100644 --- a/Firestore/Source/Model/FSTDocument.h +++ b/Firestore/Source/Model/FSTDocument.h @@ -48,7 +48,7 @@ typedef NS_ENUM(NSInteger, FSTDocumentState) { /** * Whether this document has a local mutation applied that has not yet been acknowledged by Watch. */ -- (BOOL)hasPendingWrites; +- (bool)hasPendingWrites; @end @@ -65,8 +65,8 @@ typedef NS_ENUM(NSInteger, FSTDocumentState) { proto:(GCFSDocument *)proto; - (nullable FSTFieldValue *)fieldForPath:(const firebase::firestore::model::FieldPath &)path; -- (BOOL)hasLocalMutations; -- (BOOL)hasCommittedMutations; +- (bool)hasLocalMutations; +- (bool)hasCommittedMutations; @property(nonatomic, strong, readonly) FSTObjectValue *data; @@ -81,9 +81,9 @@ typedef NS_ENUM(NSInteger, FSTDocumentState) { @interface FSTDeletedDocument : FSTMaybeDocument + (instancetype)documentWithKey:(firebase::firestore::model::DocumentKey)key version:(firebase::firestore::model::SnapshotVersion)version - hasCommittedMutations:(BOOL)committedMutations; + hasCommittedMutations:(bool)committedMutations; -- (BOOL)hasCommittedMutations; +- (bool)hasCommittedMutations; @end diff --git a/Firestore/Source/Model/FSTDocument.mm b/Firestore/Source/Model/FSTDocument.mm index 5158ac7716e..1e35d5bfd7f 100644 --- a/Firestore/Source/Model/FSTDocument.mm +++ b/Firestore/Source/Model/FSTDocument.mm @@ -55,7 +55,7 @@ - (instancetype)initWithKey:(DocumentKey)key version:(SnapshotVersion)version { return self; } -- (BOOL)hasPendingWrites { +- (bool)hasPendingWrites { @throw FSTAbstractMethodException(); // NOLINT } @@ -127,15 +127,15 @@ - (instancetype)initWithData:(FSTObjectValue *)data return self; } -- (BOOL)hasLocalMutations { +- (bool)hasLocalMutations { return _documentState == FSTDocumentStateLocalMutations; } -- (BOOL)hasCommittedMutations { +- (bool)hasCommittedMutations { return _documentState == FSTDocumentStateCommittedMutations; } -- (BOOL)hasPendingWrites { +- (bool)hasPendingWrites { return self.hasLocalMutations || self.hasCommittedMutations; } @@ -174,12 +174,12 @@ - (nullable FSTFieldValue *)fieldForPath:(const FieldPath &)path { @end @implementation FSTDeletedDocument { - BOOL _hasCommittedMutations; + bool _hasCommittedMutations; } + (instancetype)documentWithKey:(DocumentKey)key version:(SnapshotVersion)version - hasCommittedMutations:(BOOL)committedMutations { + hasCommittedMutations:(bool)committedMutations { FSTDeletedDocument *deletedDocument = [[FSTDeletedDocument alloc] initWithKey:std::move(key) version:std::move(version)]; @@ -190,11 +190,11 @@ + (instancetype)documentWithKey:(DocumentKey)key return deletedDocument; } -- (BOOL)hasCommittedMutations { +- (bool)hasCommittedMutations { return _hasCommittedMutations; } -- (BOOL)hasPendingWrites { +- (bool)hasPendingWrites { return self.hasCommittedMutations; } @@ -233,8 +233,8 @@ + (instancetype)documentWithKey:(DocumentKey)key version:(SnapshotVersion)versio return [[FSTUnknownDocument alloc] initWithKey:std::move(key) version:std::move(version)]; } -- (BOOL)hasPendingWrites { - return YES; +- (bool)hasPendingWrites { + return true; } - (BOOL)isEqual:(id)other { diff --git a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt index 8341c79df17..c18f3d03dd2 100644 --- a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt @@ -16,11 +16,18 @@ if(HAVE_LEVELDB) cc_library( firebase_firestore_local_persistence_leveldb SOURCES + leveldb_index_manager.h + #leveldb_index_manager.mm leveldb_key.cc leveldb_key.h - #leveldb_index_manager.mm leveldb_migrations.cc leveldb_migrations.h + leveldb_mutation_queue.h + #leveldb_mutation_queue.mm + leveldb_query_cache.h + #leveldb_query_cache.mm + leveldb_remote_document_cache.h + #leveldb_remote_document_cache.mm leveldb_transaction.cc leveldb_transaction.h leveldb_util.cc @@ -49,13 +56,25 @@ cc_library( SOURCES document_reference.h document_reference.cc + index_manager.h + listen_sequence.h local_serializer.h local_serializer.cc memory_index_manager.cc + memory_index_manager.h + memory_mutation_queue.h + #memory_mutation_queue.mm + memory_query_cache.h + #memory_query_cache.mm + memory_remote_document_cache.h + #memory_remote_document_cache.mm + mutation_queue.h + query_cache.h query_data.cc query_data.h reference_set.cc reference_set.h + remote_document_cache.h DEPENDS # TODO(b/111328563) Force nanopb first to work around ODR violations protobuf-nanopb diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_query_cache.h b/Firestore/core/src/firebase/firestore/local/leveldb_query_cache.h index 6dae8bb7974..caf28c403cf 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_query_cache.h +++ b/Firestore/core/src/firebase/firestore/local/leveldb_query_cache.h @@ -47,11 +47,6 @@ namespace local { /** Cached Queries backed by LevelDB. */ class LevelDbQueryCache : public QueryCache { public: - /** Enumerator callback type for orphaned documents */ - typedef void (^OrphanedDocumentEnumerator)(const model::DocumentKey&, - model::ListenSequenceNumber, - BOOL*); - /** * Retrieves the global singleton metadata row from the given database, if it * exists. @@ -75,7 +70,7 @@ class LevelDbQueryCache : public QueryCache { FSTQueryData* _Nullable GetTarget(FSTQuery* query) override; - void EnumerateTargets(TargetEnumerator block) override; + void EnumerateTargets(const TargetCallback& callback) override; int RemoveTargets(model::ListenSequenceNumber upper_bound, const std::unordered_map& @@ -123,7 +118,7 @@ class LevelDbQueryCache : public QueryCache { // Non-interface methods void Start(); - void EnumerateOrphanedDocuments(OrphanedDocumentEnumerator block); + void EnumerateOrphanedDocuments(const OrphanedDocumentCallback& callback); private: void Save(FSTQueryData* query_data); diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_query_cache.mm b/Firestore/core/src/firebase/firestore/local/leveldb_query_cache.mm index a9e14635bac..4412c466bfb 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_query_cache.mm +++ b/Firestore/core/src/firebase/firestore/local/leveldb_query_cache.mm @@ -170,16 +170,15 @@ return nil; } -void LevelDbQueryCache::EnumerateTargets(TargetEnumerator block) { +void LevelDbQueryCache::EnumerateTargets(const TargetCallback& callback) { // Enumerate all targets, give their sequence numbers. std::string target_prefix = LevelDbTargetKey::KeyPrefix(); auto it = db_.currentTransaction->NewIterator(); it->Seek(target_prefix); - BOOL stop = NO; - for (; !stop && it->Valid() && absl::StartsWith(it->key(), target_prefix); + for (; it->Valid() && absl::StartsWith(it->key(), target_prefix); it->Next()) { FSTQueryData* target = DecodeTarget(it->value()); - block(target, &stop); + callback(target); } } @@ -306,23 +305,22 @@ } void LevelDbQueryCache::EnumerateOrphanedDocuments( - OrphanedDocumentEnumerator block) { + const OrphanedDocumentCallback& callback) { std::string document_target_prefix = LevelDbDocumentTargetKey::KeyPrefix(); auto it = db_.currentTransaction->NewIterator(); it->Seek(document_target_prefix); ListenSequenceNumber next_to_report = 0; DocumentKey key_to_report; LevelDbDocumentTargetKey key; - BOOL stop = NO; - for (; !stop && it->Valid() && - absl::StartsWith(it->key(), document_target_prefix); + + for (; it->Valid() && absl::StartsWith(it->key(), document_target_prefix); it->Next()) { HARD_ASSERT(key.Decode(it->key()), "Failed to decode DocumentTarget key"); if (key.IsSentinel()) { // if next_to_report is non-zero, report it, this is a new key so the last // one must be not be a member of any targets. if (next_to_report != 0) { - block(key_to_report, next_to_report, &stop); + callback(key_to_report, next_to_report); } // set next_to_report to be this sequence number. It's the next one we // might report, if we don't find any targets for this document. @@ -335,10 +333,10 @@ next_to_report = 0; } } - // if not stop and next_to_report is non-zero, report it. We didn't find any - // targets for that document, and we weren't asked to stop. - if (!stop && next_to_report != 0) { - block(key_to_report, next_to_report, &stop); + // if next_to_report is non-zero, report it. We didn't find any targets for + // that document, and we weren't asked to stop. + if (next_to_report != 0) { + callback(key_to_report, next_to_report); } } diff --git a/Firestore/core/src/firebase/firestore/local/memory_query_cache.h b/Firestore/core/src/firebase/firestore/local/memory_query_cache.h index ad11b693e85..706b8f40401 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_query_cache.h +++ b/Firestore/core/src/firebase/firestore/local/memory_query_cache.h @@ -32,6 +32,7 @@ #include "Firestore/core/src/firebase/firestore/model/document_key_set.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/model/types.h" +#include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" @class FSTLocalSerializer; @class FSTMemoryPersistence; @@ -57,7 +58,7 @@ class MemoryQueryCache : public QueryCache { FSTQueryData* _Nullable GetTarget(FSTQuery* query) override; - void EnumerateTargets(TargetEnumerator block) override; + void EnumerateTargets(const TargetCallback& callback) override; int RemoveTargets(model::ListenSequenceNumber upper_bound, const std::unordered_map& @@ -78,7 +79,7 @@ class MemoryQueryCache : public QueryCache { size_t CalculateByteSize(FSTLocalSerializer* serializer); size_t size() const override { - return [queries_ count]; + return queries_.size(); } model::ListenSequenceNumber highest_listen_sequence_number() const override { @@ -103,9 +104,16 @@ class MemoryQueryCache : public QueryCache { model::SnapshotVersion last_remote_snapshot_version_; /** Maps a query to the data about that query. */ - NSMutableDictionary* queries_; - /** A ordered bidirectional mapping between documents and the remote target - * IDs. */ + std::unordered_map, + util::objc::EqualTo> + queries_; + + /** + * A ordered bidirectional mapping between documents and the remote target + * IDs. + */ ReferenceSet references_; }; diff --git a/Firestore/core/src/firebase/firestore/local/memory_query_cache.mm b/Firestore/core/src/firebase/firestore/local/memory_query_cache.mm index 9c76b36e664..05a69858d41 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_query_cache.mm +++ b/Firestore/core/src/firebase/firestore/local/memory_query_cache.mm @@ -15,12 +15,16 @@ */ #include "Firestore/core/src/firebase/firestore/local/memory_query_cache.h" + #import +#include + #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTMemoryPersistence.h" #import "Firestore/Source/Local/FSTQueryData.h" + #include "Firestore/core/src/firebase/firestore/model/document_key.h" using firebase::firestore::model::DocumentKey; @@ -40,7 +44,7 @@ highest_listen_sequence_number_(ListenSequenceNumber(0)), highest_target_id_(TargetId(0)), last_remote_snapshot_version_(SnapshotVersion::None()), - queries_([NSMutableDictionary dictionary]) { + queries_() { } void MemoryQueryCache::AddTarget(FSTQueryData* query_data) { @@ -59,36 +63,41 @@ } void MemoryQueryCache::RemoveTarget(FSTQueryData* query_data) { - [queries_ removeObjectForKey:query_data.query]; + queries_.erase(query_data.query); references_.RemoveReferences(query_data.targetID); } FSTQueryData* _Nullable MemoryQueryCache::GetTarget(FSTQuery* query) { - return queries_[query]; + auto iter = queries_.find(query); + return iter == queries_.end() ? nil : iter->second; } -void MemoryQueryCache::EnumerateTargets(TargetEnumerator block) { - [queries_ enumerateKeysAndObjectsUsingBlock:^( - FSTQuery* query, FSTQueryData* query_data, BOOL* stop) { - block(query_data, stop); - }]; +void MemoryQueryCache::EnumerateTargets(const TargetCallback& callback) { + for (const auto& kv : queries_) { + callback(kv.second); + } } int MemoryQueryCache::RemoveTargets( model::ListenSequenceNumber upper_bound, const std::unordered_map& live_targets) { - NSMutableArray* toRemove = [NSMutableArray array]; - [queries_ enumerateKeysAndObjectsUsingBlock:^( - FSTQuery* query, FSTQueryData* queryData, BOOL* stop) { - if (queryData.sequenceNumber <= upper_bound) { - if (live_targets.find(queryData.targetID) == live_targets.end()) { - [toRemove addObject:query]; - references_.RemoveReferences(queryData.targetID); + std::vector to_remove; + for (const auto& kv : queries_) { + FSTQuery* query = kv.first; + FSTQueryData* query_data = kv.second; + + if (query_data.sequenceNumber <= upper_bound) { + if (live_targets.find(query_data.targetID) == live_targets.end()) { + to_remove.push_back(query); + references_.RemoveReferences(query_data.targetID); } } - }]; - [queries_ removeObjectsForKeys:toRemove]; - return static_cast([toRemove count]); + } + + for (FSTQuery* element : to_remove) { + queries_.erase(element); + } + return static_cast(to_remove.size()); } void MemoryQueryCache::AddMatchingKeys(const DocumentKeySet& keys, @@ -116,11 +125,11 @@ } size_t MemoryQueryCache::CalculateByteSize(FSTLocalSerializer* serializer) { - __block size_t count = 0; - [queries_ enumerateKeysAndObjectsUsingBlock:^( - FSTQuery* query, FSTQueryData* query_data, BOOL* stop) { + size_t count = 0; + for (const auto& kv : queries_) { + FSTQueryData* query_data = kv.second; count += [[serializer encodedQueryData:query_data] serializedSize]; - }]; + } return count; } diff --git a/Firestore/core/src/firebase/firestore/local/query_cache.h b/Firestore/core/src/firebase/firestore/local/query_cache.h index 9fa61c1b519..75240ceb9c1 100644 --- a/Firestore/core/src/firebase/firestore/local/query_cache.h +++ b/Firestore/core/src/firebase/firestore/local/query_cache.h @@ -23,6 +23,7 @@ #import +#include #include #include "Firestore/core/src/firebase/firestore/model/document_key.h" @@ -39,6 +40,11 @@ namespace firebase { namespace firestore { namespace local { +using OrphanedDocumentCallback = + std::function; + +using TargetCallback = std::function; + /** * Represents cached targets received from the remote backend. This contains * both a mapping between targets and the documents that matched them according @@ -49,8 +55,6 @@ namespace local { */ class QueryCache { public: - typedef void (^TargetEnumerator)(FSTQueryData*, BOOL*); - virtual ~QueryCache() { } @@ -89,7 +93,7 @@ class QueryCache { */ virtual FSTQueryData* _Nullable GetTarget(FSTQuery* query) = 0; - virtual void EnumerateTargets(TargetEnumerator block) = 0; + virtual void EnumerateTargets(const TargetCallback& callback) = 0; virtual int RemoveTargets( model::ListenSequenceNumber upper_bound, diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index 3bac4f2757f..63c696ccf50 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -247,6 +247,7 @@ cc_library( config.h hashing.h iterator_adaptors.h + objc_compatibility.h ordered_code.cc ordered_code.h range.h diff --git a/Firestore/core/src/firebase/firestore/util/objc_compatibility.h b/Firestore/core/src/firebase/firestore/util/objc_compatibility.h index 52c79e5d0cd..74a4d44f89a 100644 --- a/Firestore/core/src/firebase/firestore/util/objc_compatibility.h +++ b/Firestore/core/src/firebase/firestore/util/objc_compatibility.h @@ -73,6 +73,34 @@ bool Equals(const T& lhs, const T& rhs) { [](Ptr o1, Ptr o2) { return Equals(o1, o2); }); } +/** + * A function object that implements equality for an Objective-C pointer by + * delegating to -isEqual:. This is useful for using Objective-C objects as + * keys in STL associative containers. + */ +template ::value>> +class EqualTo { + public: + bool operator()(T lhs, T rhs) const { + return [lhs isEqual:rhs]; + } +}; + +/** + * A function object that implements STL-compatible hash code for an Objective-C + * pointer by delegating to -hash. This is useful for using Objective-C objects + * as keys in std::unordered_map. + */ +template ::value>> +class Hash { + public: + size_t operator()(T value) const { + return static_cast([value hash]); + } +}; + /** * Creates a debug description of the given `value` by calling `ToString` on it, * converting the result to an `NSString`. Exists mainly to simplify writing diff --git a/Firestore/core/test/firebase/firestore/util/objc_compatibility_apple_test.mm b/Firestore/core/test/firebase/firestore/util/objc_compatibility_apple_test.mm index 9f89dbf9e7b..8454eaf12ff 100644 --- a/Firestore/core/test/firebase/firestore/util/objc_compatibility_apple_test.mm +++ b/Firestore/core/test/firebase/firestore/util/objc_compatibility_apple_test.mm @@ -19,6 +19,7 @@ #import #include +#include #include #import "Firestore/Example/Tests/Util/FSTHelpers.h" @@ -70,6 +71,60 @@ EXPECT_TRUE([Description(v) isEqual:@"[foo, bar]"]); } +TEST(ObjCCompatibilityTest, EqualToAndHash) { + EqualTo equals; + Hash hash; + + NSMutableString* source = [NSMutableString stringWithUTF8String:"value"]; + NSString* value = [source copy]; + NSString* copy = [source copy]; + + EXPECT_TRUE(equals(value, value)); + EXPECT_EQ(hash(value), hash(value)); + + // Same type, different instance + EXPECT_TRUE(equals(value, copy)); + EXPECT_EQ(hash(value), hash(copy)); + + // Different type, same value + EXPECT_TRUE(equals(source, value)); + EXPECT_EQ(hash(source), hash(value)); + + NSString* other = @"other"; + EXPECT_FALSE(equals(value, other)); + EXPECT_FALSE(equals(value, nil)); + EXPECT_FALSE(equals(nil, value)); + EXPECT_FALSE(equals(nil, nil)); +} + +TEST(ObjCCompatibilityTest, UnorderedMap) { + using MapType = std::unordered_map, + EqualTo>; + MapType map; + + auto inserted = map.insert({ @"foo", @1 }); + ASSERT_TRUE(inserted.second); + + inserted = map.insert({ @"bar", @2 }); + ASSERT_TRUE(inserted.second); + ASSERT_EQ(map.size(), 2); + + auto foo_iter = map.find(@"foo"); + ASSERT_NE(foo_iter, map.end()); + ASSERT_EQ(foo_iter->first, @"foo"); + + auto bar_iter = map.find(@"bar"); + ASSERT_NE(bar_iter, map.end()); + ASSERT_EQ(bar_iter->first, @"bar"); + + auto result = map.insert({ @"foo", @3 }); + ASSERT_FALSE(result.second); // not inserted + ASSERT_TRUE(Equals(result.first->second, @1)); // old value preserved + + map.erase(@"foo"); + ASSERT_EQ(map.size(), 1); +} + } // namespace objc } // namespace util } // namespace firestore diff --git a/cmake/cc_rules.cmake b/cmake/cc_rules.cmake index bf376102a96..39b56c9fcd8 100644 --- a/cmake/cc_rules.cmake +++ b/cmake/cc_rules.cmake @@ -30,7 +30,7 @@ function(cc_library name) maybe_remove_objc_sources(sources ${ccl_SOURCES}) add_library(${name} ${sources}) - add_objc_flags(${name} ccl) + add_objc_flags(${name} ${ccl_SOURCES}) target_include_directories( ${name} PUBLIC @@ -107,7 +107,7 @@ function(cc_binary name) maybe_remove_objc_sources(sources ${ccb_SOURCES}) add_executable(${name} ${sources}) - add_objc_flags(${name} ccb) + add_objc_flags(${name} ${ccb_SOURCES}) add_test(${name} ${name}) target_include_directories(${name} PUBLIC ${FIREBASE_SOURCE_DIR}) @@ -137,7 +137,7 @@ function(cc_test name) maybe_remove_objc_sources(sources ${cct_SOURCES}) add_executable(${name} ${sources}) - add_objc_flags(${name} cct) + add_objc_flags(${name} ${cct_SOURCES}) add_test(${name} ${name}) target_include_directories(${name} PUBLIC ${FIREBASE_SOURCE_DIR}) From fd8947ce7440d0a2309aa71390d868a9f86613d0 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 19 Mar 2019 12:55:24 -0700 Subject: [PATCH 057/214] Port FIRSnapshotMetadata to C++ (#2580) --- .travis.yml | 2 +- .../Tests/API/FIRQuerySnapshotTests.mm | 4 +- .../Tests/API/FIRSnapshotMetadataTests.mm | 23 ++++--- Firestore/Example/Tests/API/FSTAPIHelpers.mm | 2 +- .../Source/API/FIRDocumentSnapshot+Internal.h | 20 ++++-- Firestore/Source/API/FIRDocumentSnapshot.mm | 23 +++++-- Firestore/Source/API/FIRQuery.mm | 4 +- .../Source/API/FIRSnapshotMetadata+Internal.h | 10 ++- Firestore/Source/API/FIRSnapshotMetadata.mm | 50 +++++++-------- Firestore/Source/Core/FSTFirestoreClient.mm | 4 +- Firestore/Source/Public/FIRSnapshotMetadata.h | 1 + Firestore/core/CMakeLists.txt | 1 + .../src/firebase/firestore/api/CMakeLists.txt | 23 +++++++ .../firestore/api/document_reference.mm | 6 +- .../firestore/api/document_snapshot.h | 25 +++++--- .../firestore/api/document_snapshot.mm | 16 +---- .../firestore/api/snapshot_metadata.cc | 36 +++++++++++ .../firestore/api/snapshot_metadata.h | 64 +++++++++++++++++++ 18 files changed, 234 insertions(+), 80 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/api/CMakeLists.txt create mode 100644 Firestore/core/src/firebase/firestore/api/snapshot_metadata.cc create mode 100644 Firestore/core/src/firebase/firestore/api/snapshot_metadata.h diff --git a/.travis.yml b/.travis.yml index 76d56dc6d06..5ad2c59dc30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,7 +83,7 @@ jobs: script: # Eliminate the one warning from BoringSSL when CocoaPods 1.6.0 is available. # The travis_wait is necessary because the command takes more than 10 minutes. - - travis_wait 25 ./scripts/if_changed.sh ./scripts/pod_lib_lint.sh FirebaseFirestore.podspec --platforms=ios --allow-warnings --no-subspecs + - travis_wait 30 ./scripts/if_changed.sh ./scripts/pod_lib_lint.sh FirebaseFirestore.podspec --platforms=ios --allow-warnings --no-subspecs # pod lib lint to check build and warnings for static library build - only on cron jobs - stage: test diff --git a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm index 1399f822d57..beb02e48ff0 100644 --- a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm +++ b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm @@ -104,8 +104,8 @@ - (void)testIncludeMetadataChanges { /*from_cache=*/false, /*sync_state_changed=*/true, /*excludes_metadata_changes=*/false}; - FIRSnapshotMetadata *metadata = [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:NO - fromCache:NO]; + FIRSnapshotMetadata *metadata = [[FIRSnapshotMetadata alloc] initWithPendingWrites:NO + fromCache:NO]; FIRQuerySnapshot *snapshot = [FIRQuerySnapshot snapshotWithFirestore:firestore originalQuery:query snapshot:std::move(viewSnapshot) diff --git a/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm b/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm index f705aa76284..442406dca14 100644 --- a/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm +++ b/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm @@ -28,14 +28,11 @@ @interface FIRSnapshotMetadataTests : XCTestCase @implementation FIRSnapshotMetadataTests - (void)testEquals { - FIRSnapshotMetadata *foo = [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:YES - fromCache:YES]; - FIRSnapshotMetadata *fooDup = [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:YES - fromCache:YES]; - FIRSnapshotMetadata *bar = [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:YES - fromCache:NO]; - FIRSnapshotMetadata *baz = [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:NO - fromCache:YES]; + FIRSnapshotMetadata *foo = [[FIRSnapshotMetadata alloc] initWithPendingWrites:YES fromCache:YES]; + FIRSnapshotMetadata *fooDup = [[FIRSnapshotMetadata alloc] initWithPendingWrites:YES + fromCache:YES]; + FIRSnapshotMetadata *bar = [[FIRSnapshotMetadata alloc] initWithPendingWrites:YES fromCache:NO]; + FIRSnapshotMetadata *baz = [[FIRSnapshotMetadata alloc] initWithPendingWrites:NO fromCache:YES]; XCTAssertEqualObjects(foo, fooDup); XCTAssertNotEqualObjects(foo, bar); XCTAssertNotEqualObjects(foo, baz); @@ -47,6 +44,16 @@ - (void)testEquals { XCTAssertNotEqual([bar hash], [baz hash]); } +- (void)testProperties { + FIRSnapshotMetadata *metadata = [[FIRSnapshotMetadata alloc] initWithPendingWrites:YES + fromCache:NO]; + XCTAssertTrue(metadata.hasPendingWrites); + XCTAssertTrue(metadata.pendingWrites); + + XCTAssertFalse(metadata.isFromCache); + XCTAssertFalse(metadata.fromCache); +} + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.mm b/Firestore/Example/Tests/API/FSTAPIHelpers.mm index 8e465b662b1..98642ba1320 100644 --- a/Firestore/Example/Tests/API/FSTAPIHelpers.mm +++ b/Firestore/Example/Tests/API/FSTAPIHelpers.mm @@ -98,7 +98,7 @@ bool hasPendingWrites, bool fromCache) { FIRSnapshotMetadata *metadata = - [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:hasPendingWrites fromCache:fromCache]; + [[FIRSnapshotMetadata alloc] initWithPendingWrites:hasPendingWrites fromCache:fromCache]; FSTDocumentSet *oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[]); DocumentKeySet mutatedKeys; for (NSString *key in oldDocs) { diff --git a/Firestore/Source/API/FIRDocumentSnapshot+Internal.h b/Firestore/Source/API/FIRDocumentSnapshot+Internal.h index 620a1562753..b371b6a03cc 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot+Internal.h +++ b/Firestore/Source/API/FIRDocumentSnapshot+Internal.h @@ -17,23 +17,33 @@ #import "FIRDocumentSnapshot.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" +#include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" @class FIRFirestore; @class FSTDocument; +using firebase::firestore::api::DocumentSnapshot; +using firebase::firestore::api::Firestore; +using firebase::firestore::api::SnapshotMetadata; +using firebase::firestore::model::DocumentKey; + NS_ASSUME_NONNULL_BEGIN @interface FIRDocumentSnapshot (/* Init */) -- (instancetype)initWithSnapshot:(firebase::firestore::api::DocumentSnapshot &&)snapshot - NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithSnapshot:(DocumentSnapshot &&)snapshot NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithFirestore:(Firestore *)firestore + documentKey:(DocumentKey)documentKey + document:(nullable FSTDocument *)document + metadata:(SnapshotMetadata)metadata; -- (instancetype)initWithFirestore:(firebase::firestore::api::Firestore *)firestore - documentKey:(firebase::firestore::model::DocumentKey)documentKey +- (instancetype)initWithFirestore:(Firestore *)firestore + documentKey:(DocumentKey)documentKey document:(nullable FSTDocument *)document fromCache:(bool)fromCache - hasPendingWrites:(bool)pendingWrites; + hasPendingWrites:(bool)hasPendingWrites; @end diff --git a/Firestore/Source/API/FIRDocumentSnapshot.mm b/Firestore/Source/API/FIRDocumentSnapshot.mm index f999bc44cd7..eb210d96e02 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot.mm +++ b/Firestore/Source/API/FIRDocumentSnapshot.mm @@ -68,6 +68,8 @@ ServerTimestampBehavior InternalServerTimestampBehavior(FIRServerTimestampBehavi @implementation FIRDocumentSnapshot { DocumentSnapshot _snapshot; + + FIRSnapshotMetadata *_cachedMetadata; } - (instancetype)initWithSnapshot:(DocumentSnapshot &&)snapshot { @@ -80,12 +82,22 @@ - (instancetype)initWithSnapshot:(DocumentSnapshot &&)snapshot { - (instancetype)initWithFirestore:(Firestore *)firestore documentKey:(DocumentKey)documentKey document:(nullable FSTDocument *)document - fromCache:(bool)fromCache - hasPendingWrites:(bool)pendingWrites { - DocumentSnapshot wrapped{firestore, std::move(documentKey), document, fromCache, pendingWrites}; + metadata:(SnapshotMetadata)metadata { + DocumentSnapshot wrapped{firestore, std::move(documentKey), document, std::move(metadata)}; return [self initWithSnapshot:std::move(wrapped)]; } +- (instancetype)initWithFirestore:(Firestore *)firestore + documentKey:(DocumentKey)documentKey + document:(nullable FSTDocument *)document + fromCache:(bool)fromCache + hasPendingWrites:(bool)hasPendingWrites { + return [self initWithFirestore:firestore + documentKey:std::move(documentKey) + document:document + metadata:SnapshotMetadata(hasPendingWrites, fromCache)]; +} + // NSObject Methods - (BOOL)isEqual:(nullable id)other { if (other == self) return YES; @@ -120,7 +132,10 @@ - (NSString *)documentID { @dynamic metadata; - (FIRSnapshotMetadata *)metadata { - return _snapshot.GetMetadata(); + if (!_cachedMetadata) { + _cachedMetadata = [[FIRSnapshotMetadata alloc] initWithMetadata:_snapshot.metadata()]; + } + return _cachedMetadata; } - (nullable NSDictionary *)data { diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 0665a335ab7..29f5da27499 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -183,8 +183,8 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)source ViewSnapshot snapshot = maybe_snapshot.ValueOrDie(); FIRSnapshotMetadata *metadata = - [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:snapshot.has_pending_writes() - fromCache:snapshot.from_cache()]; + [[FIRSnapshotMetadata alloc] initWithPendingWrites:snapshot.has_pending_writes() + fromCache:snapshot.from_cache()]; listener([FIRQuerySnapshot snapshotWithFirestore:firestore originalQuery:query diff --git a/Firestore/Source/API/FIRSnapshotMetadata+Internal.h b/Firestore/Source/API/FIRSnapshotMetadata+Internal.h index d3265cd0959..6c5c6997bff 100644 --- a/Firestore/Source/API/FIRSnapshotMetadata+Internal.h +++ b/Firestore/Source/API/FIRSnapshotMetadata+Internal.h @@ -18,11 +18,17 @@ #import +#include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" + +using firebase::firestore::api::SnapshotMetadata; + NS_ASSUME_NONNULL_BEGIN -@interface FIRSnapshotMetadata (Internal) +@interface FIRSnapshotMetadata (/* Init */) + +- (instancetype)initWithMetadata:(SnapshotMetadata)metadata NS_DESIGNATED_INITIALIZER; -+ (instancetype)snapshotMetadataWithPendingWrites:(BOOL)pendingWrites fromCache:(BOOL)fromCache; +- (instancetype)initWithPendingWrites:(bool)pendingWrites fromCache:(bool)fromCache; @end diff --git a/Firestore/Source/API/FIRSnapshotMetadata.mm b/Firestore/Source/API/FIRSnapshotMetadata.mm index 27747cee260..87fa45efa23 100644 --- a/Firestore/Source/API/FIRSnapshotMetadata.mm +++ b/Firestore/Source/API/FIRSnapshotMetadata.mm @@ -16,53 +16,49 @@ #import "FIRSnapshotMetadata.h" -#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FIRSnapshotMetadata () +#include -- (instancetype)initWithPendingWrites:(BOOL)pendingWrites fromCache:(BOOL)fromCache; +#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" -@end +#include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" -@implementation FIRSnapshotMetadata (Internal) +NS_ASSUME_NONNULL_BEGIN -+ (instancetype)snapshotMetadataWithPendingWrites:(BOOL)pendingWrites fromCache:(BOOL)fromCache { - return [[FIRSnapshotMetadata alloc] initWithPendingWrites:pendingWrites fromCache:fromCache]; +@implementation FIRSnapshotMetadata { + SnapshotMetadata _metadata; } -@end - -@implementation FIRSnapshotMetadata - -- (instancetype)initWithPendingWrites:(BOOL)pendingWrites fromCache:(BOOL)fromCache { +- (instancetype)initWithMetadata:(SnapshotMetadata)metadata { if (self = [super init]) { - _pendingWrites = pendingWrites; - _fromCache = fromCache; + _metadata = std::move(metadata); } return self; } +- (instancetype)initWithPendingWrites:(bool)pendingWrites fromCache:(bool)fromCache { + SnapshotMetadata wrapped(pendingWrites, fromCache); + return [self initWithMetadata:std::move(wrapped)]; +} + // NSObject Methods - (BOOL)isEqual:(nullable id)other { if (other == self) return YES; - if (![[other class] isEqual:[self class]]) return NO; + if (![other isKindOfClass:[FIRSnapshotMetadata class]]) return NO; - return [self isEqualToMetadata:other]; + FIRSnapshotMetadata *otherMetadata = other; + return _metadata == otherMetadata->_metadata; } -- (BOOL)isEqualToMetadata:(nullable FIRSnapshotMetadata *)metadata { - if (self == metadata) return YES; - if (metadata == nil) return NO; +- (NSUInteger)hash { + return _metadata.Hash(); +} - return self.pendingWrites == metadata.pendingWrites && self.fromCache == metadata.fromCache; +- (BOOL)hasPendingWrites { + return _metadata.pending_writes(); } -- (NSUInteger)hash { - NSUInteger hash = self.pendingWrites ? 1 : 0; - hash = hash * 31u + (self.fromCache ? 1 : 0); - return hash; +- (BOOL)isFromCache { + return _metadata.from_cache(); } @end diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index 8b3ed242fdb..e0706fbd851 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -372,8 +372,8 @@ - (void)getDocumentsFromLocalCache:(FIRQuery *)query ViewSnapshot snapshot = std::move(viewChange.snapshot).value(); FIRSnapshotMetadata *metadata = - [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:snapshot.has_pending_writes() - fromCache:snapshot.from_cache()]; + [[FIRSnapshotMetadata alloc] initWithPendingWrites:snapshot.has_pending_writes() + fromCache:snapshot.from_cache()]; FIRQuerySnapshot *result = [FIRQuerySnapshot snapshotWithFirestore:query.firestore originalQuery:query.query diff --git a/Firestore/Source/Public/FIRSnapshotMetadata.h b/Firestore/Source/Public/FIRSnapshotMetadata.h index f47f3839810..8e4e6a3c647 100644 --- a/Firestore/Source/Public/FIRSnapshotMetadata.h +++ b/Firestore/Source/Public/FIRSnapshotMetadata.h @@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(SnapshotMetadata) @interface FIRSnapshotMetadata : NSObject +/** */ - (instancetype)init NS_UNAVAILABLE; /** diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index a7fd72a8508..b9812921311 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(include/firebase/firestore) add_subdirectory(src/firebase/firestore) +add_subdirectory(src/firebase/firestore/api) add_subdirectory(src/firebase/firestore/auth) add_subdirectory(src/firebase/firestore/core) add_subdirectory(src/firebase/firestore/immutable) diff --git a/Firestore/core/src/firebase/firestore/api/CMakeLists.txt b/Firestore/core/src/firebase/firestore/api/CMakeLists.txt new file mode 100644 index 00000000000..cc241aa0c7e --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright 2019 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. + +cc_library( + firebase_firestore_api + SOURCES + snapshot_metadata.cc + snapshot_metadata.h + DEPENDS + absl_meta + firebase_firestore_util +) diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index 198d9305ede..dc883d05cf0 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -18,8 +18,6 @@ #include -#import "FIRSnapshotMetadata.h" - #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" @@ -150,7 +148,7 @@ dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER); [*listener_registration remove]; - if (!snapshot.exists() && snapshot.GetMetadata().fromCache) { + if (!snapshot.exists() && snapshot.metadata().from_cache()) { // TODO(dimond): Reconsider how to raise missing documents when // offline. If we're online and the document doesn't exist then we // call the completion with a document with document.exists set to @@ -162,7 +160,7 @@ completion( Status{FirestoreErrorCode::Unavailable, "Failed to get document because the client is offline."}); - } else if (snapshot.exists() && snapshot.GetMetadata().fromCache && + } else if (snapshot.exists() && snapshot.metadata().from_cache() && source == FIRFirestoreSourceServer) { completion(Status{FirestoreErrorCode::Unavailable, "Failed to get document from server. (However, " diff --git a/Firestore/core/src/firebase/firestore/api/document_snapshot.h b/Firestore/core/src/firebase/firestore/api/document_snapshot.h index 3b60ae2ffc0..4fcafa2b7d5 100644 --- a/Firestore/core/src/firebase/firestore/api/document_snapshot.h +++ b/Firestore/core/src/firebase/firestore/api/document_snapshot.h @@ -28,12 +28,12 @@ #import "Firestore/Source/Model/FSTFieldValue.h" +#include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" NS_ASSUME_NONNULL_BEGIN -@class FIRSnapshotMetadata; @class FSTDocument; namespace firebase { @@ -47,6 +47,16 @@ class DocumentSnapshot { public: DocumentSnapshot() = default; + DocumentSnapshot(Firestore* firestore, + model::DocumentKey document_key, + FSTDocument* _Nullable document, + SnapshotMetadata metadata) + : firestore_{firestore}, + internal_key_{std::move(document_key)}, + internal_document_{document}, + metadata_{std::move(metadata)} { + } + DocumentSnapshot(Firestore* firestore, model::DocumentKey document_key, FSTDocument* _Nullable document, @@ -55,8 +65,7 @@ class DocumentSnapshot { : firestore_{firestore}, internal_key_{std::move(document_key)}, internal_document_{document}, - from_cache_{from_cache}, - has_pending_writes_{has_pending_writes} { + metadata_{has_pending_writes, from_cache} { } size_t Hash() const; @@ -69,8 +78,11 @@ class DocumentSnapshot { } std::string document_id() const; + const SnapshotMetadata& metadata() const { + return metadata_; + } + DocumentReference CreateReference() const; - FIRSnapshotMetadata* GetMetadata() const; FSTObjectValue* _Nullable GetData() const; id _Nullable GetValue(const model::FieldPath& field_path) const; @@ -86,10 +98,7 @@ class DocumentSnapshot { Firestore* firestore_ = nullptr; model::DocumentKey internal_key_; FSTDocument* internal_document_ = nil; - bool from_cache_ = false; - bool has_pending_writes_ = false; - - mutable FIRSnapshotMetadata* cached_metadata_ = nil; + SnapshotMetadata metadata_; }; } // namespace api diff --git a/Firestore/core/src/firebase/firestore/api/document_snapshot.mm b/Firestore/core/src/firebase/firestore/api/document_snapshot.mm index e03436b1f95..f07885b3a2a 100644 --- a/Firestore/core/src/firebase/firestore/api/document_snapshot.mm +++ b/Firestore/core/src/firebase/firestore/api/document_snapshot.mm @@ -17,7 +17,6 @@ #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" -#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Model/FSTDocument.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" @@ -34,8 +33,7 @@ using model::FieldPath; size_t DocumentSnapshot::Hash() const { - return util::Hash(firestore_, internal_key_, internal_document_, from_cache_, - has_pending_writes_); + return util::Hash(firestore_, internal_key_, internal_document_, metadata_); } DocumentReference DocumentSnapshot::CreateReference() const { @@ -46,15 +44,6 @@ return internal_key_.path().last_segment(); } -FIRSnapshotMetadata* DocumentSnapshot::GetMetadata() const { - if (!cached_metadata_) { - cached_metadata_ = [FIRSnapshotMetadata - snapshotMetadataWithPendingWrites:has_pending_writes_ - fromCache:from_cache_]; - } - return cached_metadata_; -} - FSTObjectValue* _Nullable DocumentSnapshot::GetData() const { return internal_document_ == nil ? nil : [internal_document_ data]; } @@ -67,8 +56,7 @@ return lhs.firestore_ == rhs.firestore_ && lhs.internal_key_ == rhs.internal_key_ && objc::Equals(lhs.internal_document_, rhs.internal_document_) && - lhs.from_cache_ == rhs.from_cache_ && - lhs.has_pending_writes_ == rhs.has_pending_writes_; + lhs.metadata_ == rhs.metadata_; } } // namespace api diff --git a/Firestore/core/src/firebase/firestore/api/snapshot_metadata.cc b/Firestore/core/src/firebase/firestore/api/snapshot_metadata.cc new file mode 100644 index 00000000000..1790bfc6bf6 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/snapshot_metadata.cc @@ -0,0 +1,36 @@ +/* + * Copyright 2019 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/api/snapshot_metadata.h" + +#include "Firestore/core/src/firebase/firestore/util/hashing.h" + +namespace firebase { +namespace firestore { +namespace api { + +bool operator==(const SnapshotMetadata& lhs, const SnapshotMetadata& rhs) { + return lhs.pending_writes_ == rhs.pending_writes_ && + lhs.from_cache_ == rhs.from_cache_; +} + +size_t SnapshotMetadata::Hash() const { + return util::Hash(pending_writes_, from_cache_); +} + +} // namespace api +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/api/snapshot_metadata.h b/Firestore/core/src/firebase/firestore/api/snapshot_metadata.h new file mode 100644 index 00000000000..9fe2b808b4d --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/snapshot_metadata.h @@ -0,0 +1,64 @@ +/* + * Copyright 2019 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_API_SNAPSHOT_METADATA_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_SNAPSHOT_METADATA_H_ + +#include + +namespace firebase { +namespace firestore { +namespace api { + +/** Metadata about a snapshot, describing the state of the snapshot. */ +class SnapshotMetadata { + public: + SnapshotMetadata() = default; + SnapshotMetadata(bool pending_writes, bool from_cache) + : pending_writes_(pending_writes), from_cache_(from_cache) { + } + + /** + * Returns true if the snapshot contains the result of local writes (e.g. + * set() or update() calls) that have not yet been committed to the backend. + */ + bool pending_writes() const { + return pending_writes_; + } + + /** + * Returns true if the snapshot was created from cached data rather than + * guaranteed up-to-date server data. + */ + bool from_cache() const { + return from_cache_; + } + + friend bool operator==(const SnapshotMetadata& lhs, + const SnapshotMetadata& rhs); + + size_t Hash() const; + + private: + bool pending_writes_ = false; + bool from_cache_ = false; +}; + +} // namespace api +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_SNAPSHOT_METADATA_H_ From 6cd4f4400d84068f2829fa1d9cc3b60b1272464d Mon Sep 17 00:00:00 2001 From: rsgowman Date: Tue, 19 Mar 2019 16:01:54 -0400 Subject: [PATCH 058/214] Create ObjectValue as a wrapper around a FieldValue which contains a map (#2527) --- .../firestore/local/local_serializer.cc | 5 +- .../src/firebase/firestore/model/document.cc | 3 +- .../src/firebase/firestore/model/document.h | 8 +- .../firebase/firestore/model/field_value.cc | 90 +++++++------ .../firebase/firestore/model/field_value.h | 124 ++++++++++-------- .../src/firebase/firestore/model/mutation.cc | 24 ++-- .../src/firebase/firestore/model/mutation.h | 24 ++-- .../firebase/firestore/remote/serializer.cc | 51 ++++--- .../firebase/firestore/remote/serializer.h | 9 -- .../firestore/local/local_serializer_test.cc | 5 +- .../firebase/firestore/model/document_test.cc | 15 ++- .../firestore/model/field_value_test.cc | 61 ++++----- .../firebase/firestore/model/mutation_test.cc | 2 +- .../firestore/remote/serializer_test.cc | 14 +- .../firebase/firestore/testutil/testutil.cc | 6 +- .../firebase/firestore/testutil/testutil.h | 12 +- 16 files changed, 222 insertions(+), 231 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/local/local_serializer.cc b/Firestore/core/src/firebase/firestore/local/local_serializer.cc index 49d25968f4f..7f252969186 100644 --- a/Firestore/core/src/firebase/firestore/local/local_serializer.cc +++ b/Firestore/core/src/firebase/firestore/local/local_serializer.cc @@ -42,7 +42,6 @@ using model::MaybeDocument; using model::Mutation; using model::MutationBatch; using model::NoDocument; -using model::ObjectValue; using model::SnapshotVersion; using model::UnknownDocument; using nanopb::Reader; @@ -124,13 +123,13 @@ google_firestore_v1_Document LocalSerializer::EncodeDocument( rpc_serializer_.EncodeString(rpc_serializer_.EncodeKey(doc.key())); // Encode Document.fields (unless it's empty) - size_t count = doc.data().object_value().internal_value.size(); + size_t count = doc.data().GetInternalValue().size(); HARD_ASSERT(count <= std::numeric_limits::max(), "Unable to encode specified document. Too many fields."); result.fields_count = static_cast(count); result.fields = MakeArray(count); int i = 0; - for (const auto& kv : doc.data().object_value().internal_value) { + for (const auto& kv : doc.data().GetInternalValue()) { result.fields[i].key = rpc_serializer_.EncodeString(kv.first); result.fields[i].value = rpc_serializer_.EncodeFieldValue(kv.second); i++; diff --git a/Firestore/core/src/firebase/firestore/model/document.cc b/Firestore/core/src/firebase/firestore/model/document.cc index 06deb50576b..da540c88324 100644 --- a/Firestore/core/src/firebase/firestore/model/document.cc +++ b/Firestore/core/src/firebase/firestore/model/document.cc @@ -24,7 +24,7 @@ namespace firebase { namespace firestore { namespace model { -Document::Document(FieldValue&& data, +Document::Document(ObjectValue&& data, DocumentKey key, SnapshotVersion version, DocumentState document_state) @@ -32,7 +32,6 @@ Document::Document(FieldValue&& data, data_(std::move(data)), document_state_(document_state) { set_type(Type::Document); - HARD_ASSERT(FieldValue::Type::Object == data.type()); } bool Document::Equals(const MaybeDocument& other) const { diff --git a/Firestore/core/src/firebase/firestore/model/document.h b/Firestore/core/src/firebase/firestore/model/document.h index 9afa327f64b..acd04212572 100644 --- a/Firestore/core/src/firebase/firestore/model/document.h +++ b/Firestore/core/src/firebase/firestore/model/document.h @@ -50,14 +50,14 @@ enum class DocumentState { class Document : public MaybeDocument { public: /** - * Construct a document. FieldValue must be passed by rvalue. + * Construct a document. ObjectValue must be passed by rvalue. */ - Document(FieldValue&& data, + Document(ObjectValue&& data, DocumentKey key, SnapshotVersion version, DocumentState document_state); - const FieldValue& data() const { + const ObjectValue& data() const { return data_; } @@ -81,7 +81,7 @@ class Document : public MaybeDocument { bool Equals(const MaybeDocument& other) const override; private: - FieldValue data_; // This is of type Object. + ObjectValue data_; DocumentState document_state_; }; diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc index 53e197a9113..fee10451e9a 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.cc +++ b/Firestore/core/src/firebase/firestore/model/field_value.cc @@ -23,6 +23,7 @@ #include #include +#include "Firestore/core/src/firebase/firestore/immutable/sorted_map.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/memory/memory.h" @@ -91,8 +92,8 @@ FieldValue& FieldValue::operator=(const FieldValue& value) { } case Type::Object: { // copy-and-swap - ObjectValue::Map tmp = value.object_value_->internal_value; - std::swap(object_value_->internal_value, tmp); + Map tmp = *value.object_value_; + std::swap(*object_value_, tmp); break; } default: @@ -143,45 +144,41 @@ bool FieldValue::Comparable(Type lhs, Type rhs) { } } -FieldValue FieldValue::Set(const FieldPath& field_path, - const FieldValue& value) const { - HARD_ASSERT(type() == Type::Object, - "Cannot set field for non-object FieldValue"); +// TODO(rsgowman): Reorder this file to match its header. +ObjectValue ObjectValue::Set(const FieldPath& field_path, + const FieldValue& value) const { HARD_ASSERT(!field_path.empty(), "Cannot set field for empty path on FieldValue"); // Set the value by recursively calling on child object. const std::string& child_name = field_path.first_segment(); - const ObjectValue::Map& object_map = object_value_->internal_value; if (field_path.size() == 1) { return SetChild(child_name, value); } else { - FieldValue child; - const auto iter = object_map.find(child_name); - if (iter != object_map.end() && iter->second.type() == Type::Object) { - child = iter->second; - } else { - child = EmptyObject(); + ObjectValue child = ObjectValue::Empty(); + const auto iter = fv_.object_value_->find(child_name); + if (iter != fv_.object_value_->end() && + iter->second.type() == Type::Object) { + child = ObjectValue(iter->second); } - FieldValue new_child = child.Set(field_path.PopFirst(), value); - return SetChild(child_name, new_child); + ObjectValue new_child = child.Set(field_path.PopFirst(), value); + return SetChild(child_name, new_child.fv_); } } -FieldValue FieldValue::Delete(const FieldPath& field_path) const { - HARD_ASSERT(type() == Type::Object, - "Cannot delete field for non-object FieldValue"); +ObjectValue ObjectValue::Delete(const FieldPath& field_path) const { HARD_ASSERT(!field_path.empty(), "Cannot delete field for empty path on FieldValue"); // Delete the value by recursively calling on child object. const std::string& child_name = field_path.first_segment(); - const ObjectValue::Map& object_map = object_value_->internal_value; if (field_path.size() == 1) { - return FieldValue::FromMap(object_map.erase(child_name)); + return ObjectValue::FromMap(fv_.object_value_->erase(child_name)); } else { - const auto iter = object_map.find(child_name); - if (iter != object_map.end() && iter->second.type() == Type::Object) { - FieldValue new_child = iter->second.Delete(field_path.PopFirst()); - return SetChild(child_name, new_child); + const auto iter = fv_.object_value_->find(child_name); + if (iter != fv_.object_value_->end() && + iter->second.type() == Type::Object) { + ObjectValue new_child = + ObjectValue(iter->second).Delete(field_path.PopFirst()); + return SetChild(child_name, new_child.fv_); } else { // If the found value isn't an object, it cannot contain the remaining // segments of the path. We don't actually change a primitive value to @@ -191,17 +188,14 @@ FieldValue FieldValue::Delete(const FieldPath& field_path) const { } } -absl::optional FieldValue::Get(const FieldPath& field_path) const { - HARD_ASSERT(type() == Type::Object, - "Cannot get field for non-object FieldValue"); - const FieldValue* current = this; +absl::optional ObjectValue::Get(const FieldPath& field_path) const { + const FieldValue* current = &this->fv_; for (const auto& path : field_path) { if (current->type() != Type::Object) { return absl::nullopt; } - const ObjectValue::Map& object_map = current->object_value_->internal_value; - const auto iter = object_map.find(path); - if (iter == object_map.end()) { + const auto iter = current->object_value_->find(path); + if (iter == current->object_value_->end()) { return absl::nullopt; } else { current = &iter->second; @@ -210,12 +204,9 @@ absl::optional FieldValue::Get(const FieldPath& field_path) const { return *current; } -FieldValue FieldValue::SetChild(const std::string& child_name, - const FieldValue& value) const { - HARD_ASSERT(type() == Type::Object, - "Cannot set child for non-object FieldValue"); - return FieldValue::FromMap( - object_value_->internal_value.insert(child_name, value)); +ObjectValue ObjectValue::SetChild(const std::string& child_name, + const FieldValue& value) const { + return ObjectValue::FromMap(fv_.object_value_->insert(child_name, value)); } FieldValue FieldValue::Null() { @@ -239,7 +230,7 @@ FieldValue FieldValue::Nan() { } FieldValue FieldValue::EmptyObject() { - return FieldValue::FromMap(ObjectValue::Empty()); + return FieldValue::FromMap(FieldValue::Map()); } FieldValue FieldValue::FromInteger(int64_t value) { @@ -344,19 +335,19 @@ FieldValue FieldValue::FromArray(std::vector&& value) { return result; } -FieldValue FieldValue::FromMap(const ObjectValue::Map& value) { - ObjectValue::Map copy(value); +FieldValue FieldValue::FromMap(const FieldValue::Map& value) { + FieldValue::Map copy(value); return FromMap(std::move(copy)); } -FieldValue FieldValue::FromMap(ObjectValue::Map&& value) { +FieldValue FieldValue::FromMap(FieldValue::Map&& value) { FieldValue result; result.SwitchTo(Type::Object); - std::swap(result.object_value_->internal_value, value); + std::swap(*result.object_value_, value); return result; } -bool operator<(const ObjectValue::Map& lhs, const ObjectValue::Map& rhs) { +bool operator<(const FieldValue::Map& lhs, const FieldValue::Map& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } @@ -454,7 +445,7 @@ void FieldValue::SwitchTo(const Type type) { array_value_.~unique_ptr>(); break; case Type::Object: - object_value_.~unique_ptr(); + object_value_.~unique_ptr(); break; default: {} // The other types where there is nothing to worry about. } @@ -491,13 +482,20 @@ void FieldValue::SwitchTo(const Type type) { absl::make_unique>()); break; case Type::Object: - new (&object_value_) - std::unique_ptr(absl::make_unique()); + new (&object_value_) std::unique_ptr(absl::make_unique()); break; default: {} // The other types where there is nothing to worry about. } } +ObjectValue ObjectValue::FromMap(const FieldValue::Map& value) { + return ObjectValue(FieldValue::FromMap(value)); +} + +ObjectValue ObjectValue::FromMap(FieldValue::Map&& value) { + return ObjectValue(FieldValue::FromMap(std::move(value))); +} + } // namespace model } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h index c6902d3dfd7..0aeb2d316e2 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.h +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "Firestore/core/include/firebase/firestore/geo_point.h" @@ -46,8 +47,6 @@ struct ReferenceValue { const DatabaseId* database_id; }; -struct ObjectValue; - /** * tagged-union class representing an immutable data value as stored in * Firestore. FieldValue represents all the different kinds of values @@ -55,6 +54,8 @@ struct ObjectValue; */ class FieldValue { public: + using Map = immutable::SortedMap; + /** * All the different kinds of values that can be stored in fields in * a document. The types of the same comparison order should be defined @@ -126,39 +127,6 @@ class FieldValue { return *string_value_; } - const ObjectValue& object_value() const { - HARD_ASSERT(tag_ == Type::Object); - return *object_value_; - } - - /** - * Returns a FieldValue with the field at the named path set to value. - * Any absent parent of the field will also be created accordingly. - * - * @param field_path The field path to set. Cannot be empty. - * @param value The value to set. - * @return A new FieldValue with the field set. - */ - FieldValue Set(const FieldPath& field_path, const FieldValue& value) const; - - /** - * Returns a FieldValue with the field path deleted. If there is no field at - * the specified path, the returned value is an identical copy. - * - * @param field_path The field path to remove. Cannot be empty. - * @return A new FieldValue with the field path removed. - */ - FieldValue Delete(const FieldPath& field_path) const; - - /** - * Returns the value at the given path or absl::nullopt. If the path is empty, - * an identical copy of the FieldValue is returned. - * - * @param field_path the path to search. - * @return The value at the path or absl::nullopt if it doesn't exist. - */ - absl::optional Get(const FieldPath& field_path) const; - /** factory methods. */ static FieldValue Null(); static FieldValue True(); @@ -183,14 +151,14 @@ class FieldValue { static FieldValue FromGeoPoint(const GeoPoint& value); static FieldValue FromArray(const std::vector& value); static FieldValue FromArray(std::vector&& value); - static FieldValue FromMap( - const immutable::SortedMap& value); - static FieldValue FromMap( - immutable::SortedMap&& value); + static FieldValue FromMap(const Map& value); + static FieldValue FromMap(Map&& value); friend bool operator<(const FieldValue& lhs, const FieldValue& rhs); private: + friend class ObjectValue; + explicit FieldValue(bool value) : tag_(Type::Boolean), boolean_value_(value) { } @@ -199,9 +167,6 @@ class FieldValue { */ void SwitchTo(Type type); - FieldValue SetChild(const std::string& child_name, - const FieldValue& value) const; - Type tag_ = Type::Null; union { // There is no null type as tag_ alone is enough for Null FieldValue. @@ -216,27 +181,72 @@ class FieldValue { std::unique_ptr reference_value_; std::unique_ptr geo_point_value_; std::unique_ptr> array_value_; - std::unique_ptr object_value_; + std::unique_ptr object_value_; }; }; -// TODO(rsgowman): Expand this to roughly match the java class -// c.g.f.f.model.value.ObjectValue. Probably move it to a similar namespace as -// well. (FieldValue itself is also in the value package in java.) Also do the -// same with the other FooValue values that FieldValue can return. -struct ObjectValue { - // TODO(rsgowman): These will eventually be private. We do want the serializer - // to be able to directly access these (possibly implying 'friend' usage, or a - // getInternalValue() like java has.) - using Map = immutable::SortedMap; - Map internal_value; +/** A structured object value stored in Firestore. */ +class ObjectValue { + public: + explicit ObjectValue(FieldValue fv) : fv_(std::move(fv)) { + HARD_ASSERT(fv_.type() == FieldValue::Type::Object); + } - static ObjectValue::Map Empty() { - return Map(); + static ObjectValue Empty() { + return ObjectValue(FieldValue::EmptyObject()); } + + static ObjectValue FromMap(const FieldValue::Map& value); + static ObjectValue FromMap(FieldValue::Map&& value); + + /** + * Returns the value at the given path or absl::nullopt. If the path is empty, + * an identical copy of the FieldValue is returned. + * + * @param field_path the path to search. + * @return The value at the path or absl::nullopt if it doesn't exist. + */ + absl::optional Get(const FieldPath& field_path) const; + + /** + * Returns a FieldValue with the field at the named path set to value. + * Any absent parent of the field will also be created accordingly. + * + * @param field_path The field path to set. Cannot be empty. + * @param value The value to set. + * @return A new FieldValue with the field set. + */ + ObjectValue Set(const FieldPath& field_path, const FieldValue& value) const; + + /** + * Returns a FieldValue with the field path deleted. If there is no field at + * the specified path, the returned value is an identical copy. + * + * @param field_path The field path to remove. Cannot be empty. + * @return A new FieldValue with the field path removed. + */ + ObjectValue Delete(const FieldPath& field_path) const; + + // TODO(rsgowman): Add Value() method? + // + // Java has a value() method which returns a (non-immutable) java.util.Map, + // which is a copy of the immutable map, but with some fields (such as server + // timestamps) optionally resolved. Do we need the same here? + + const FieldValue::Map& GetInternalValue() const { + return *fv_.object_value_; + } + + private: + friend bool operator<(const ObjectValue& lhs, const ObjectValue& rhs); + + ObjectValue SetChild(const std::string& child_name, + const FieldValue& value) const; + + FieldValue fv_; }; -bool operator<(const ObjectValue::Map& lhs, const ObjectValue::Map& rhs); +bool operator<(const FieldValue::Map& lhs, const FieldValue::Map& rhs); /** Compares against another FieldValue. */ bool operator<(const FieldValue& lhs, const FieldValue& rhs); @@ -263,7 +273,7 @@ inline bool operator==(const FieldValue& lhs, const FieldValue& rhs) { /** Compares against another ObjectValue. */ inline bool operator<(const ObjectValue& lhs, const ObjectValue& rhs) { - return lhs.internal_value < rhs.internal_value; + return lhs.fv_ < rhs.fv_; } inline bool operator>(const ObjectValue& lhs, const ObjectValue& rhs) { diff --git a/Firestore/core/src/firebase/firestore/model/mutation.cc b/Firestore/core/src/firebase/firestore/model/mutation.cc index d2ef4ebc3d3..e863b34520a 100644 --- a/Firestore/core/src/firebase/firestore/model/mutation.cc +++ b/Firestore/core/src/firebase/firestore/model/mutation.cc @@ -21,6 +21,7 @@ #include "Firestore/core/src/firebase/firestore/model/document.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/field_value.h" #include "Firestore/core/src/firebase/firestore/model/no_document.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" @@ -54,12 +55,10 @@ bool Mutation::equal_to(const Mutation& other) const { } SetMutation::SetMutation(DocumentKey&& key, - FieldValue&& value, + ObjectValue&& value, Precondition&& precondition) : Mutation(std::move(key), std::move(precondition)), value_(std::move(value)) { - // TODO(rsgowman): convert param to ObjectValue instead of FieldValue? - HARD_ASSERT(value_.type() == FieldValue::Type::Object); } MaybeDocumentPtr SetMutation::ApplyToRemoteDocument( @@ -74,7 +73,7 @@ MaybeDocumentPtr SetMutation::ApplyToRemoteDocument( // the server has accepted the mutation so the precondition must have held. const SnapshotVersion& version = mutation_result.version(); - return absl::make_unique(FieldValue(value_), key(), version, + return absl::make_unique(ObjectValue(value_), key(), version, DocumentState::kCommittedMutations); } @@ -89,7 +88,7 @@ MaybeDocumentPtr SetMutation::ApplyToLocalView( } SnapshotVersion version = GetPostMutationVersion(maybe_doc.get()); - return absl::make_unique(FieldValue(value_), key(), version, + return absl::make_unique(ObjectValue(value_), key(), version, DocumentState::kLocalMutations); } @@ -99,14 +98,12 @@ bool SetMutation::equal_to(const Mutation& other) const { } PatchMutation::PatchMutation(DocumentKey&& key, - FieldValue&& value, + ObjectValue&& value, FieldMask&& mask, Precondition&& precondition) : Mutation(std::move(key), std::move(precondition)), value_(std::move(value)), mask_(std::move(mask)) { - // TODO(rsgowman): convert param to ObjectValue instead of FieldValue? - HARD_ASSERT(value_.type() == FieldValue::Type::Object); } MaybeDocumentPtr PatchMutation::ApplyToRemoteDocument( @@ -131,7 +128,7 @@ MaybeDocumentPtr PatchMutation::ApplyToRemoteDocument( } const SnapshotVersion& version = mutation_result.version(); - FieldValue new_data = PatchDocument(maybe_doc.get()); + ObjectValue new_data = PatchDocument(maybe_doc.get()); return absl::make_unique(std::move(new_data), key(), version, DocumentState::kCommittedMutations); } @@ -147,21 +144,20 @@ MaybeDocumentPtr PatchMutation::ApplyToLocalView( } SnapshotVersion version = GetPostMutationVersion(maybe_doc.get()); - FieldValue new_data = PatchDocument(maybe_doc.get()); + ObjectValue new_data = PatchDocument(maybe_doc.get()); return absl::make_unique(std::move(new_data), key(), version, DocumentState::kLocalMutations); } -FieldValue PatchMutation::PatchDocument(const MaybeDocument* maybe_doc) const { +ObjectValue PatchMutation::PatchDocument(const MaybeDocument* maybe_doc) const { if (maybe_doc && maybe_doc->type() == MaybeDocument::Type::Document) { return PatchObject(static_cast(maybe_doc)->data()); } else { - return PatchObject(FieldValue::EmptyObject()); + return PatchObject(ObjectValue::Empty()); } } -FieldValue PatchMutation::PatchObject(FieldValue obj) const { - HARD_ASSERT(obj.type() == FieldValue::Type::Object); +ObjectValue PatchMutation::PatchObject(ObjectValue obj) const { for (const FieldPath& path : mask_) { if (!path.empty()) { absl::optional new_value = value_.Get(path); diff --git a/Firestore/core/src/firebase/firestore/model/mutation.h b/Firestore/core/src/firebase/firestore/model/mutation.h index 08b4c35aeef..8e4ad562405 100644 --- a/Firestore/core/src/firebase/firestore/model/mutation.h +++ b/Firestore/core/src/firebase/firestore/model/mutation.h @@ -46,7 +46,7 @@ class MutationResult { public: MutationResult( SnapshotVersion&& version, - const std::shared_ptr>& transform_results) + const std::shared_ptr>& transform_results) : version_(std::move(version)), transform_results_(std::move(transform_results)) { } @@ -67,19 +67,19 @@ class MutationResult { /** * The resulting fields returned from the backend after a TransformMutation - * has been committed. Contains one FieldValue for each FieldTransform + * has been committed. Contains one ObjectValue for each FieldTransform * that was in the mutation. * * Will be null if the mutation was not a TransformMutation. */ - const std::shared_ptr>& transform_results() + const std::shared_ptr>& transform_results() const { return transform_results_; } private: const SnapshotVersion version_; - const std::shared_ptr> transform_results_; + const std::shared_ptr> transform_results_; }; /** @@ -217,7 +217,7 @@ inline bool operator!=(const Mutation& lhs, const Mutation& rhs) { class SetMutation : public Mutation { public: SetMutation(DocumentKey&& key, - FieldValue&& value, + ObjectValue&& value, Precondition&& precondition); Type type() const override { @@ -234,7 +234,7 @@ class SetMutation : public Mutation { const Timestamp& local_write_time) const override; /** Returns the object value to use when setting the document. */ - const FieldValue& value() const { + const ObjectValue& value() const { return value_; } @@ -242,7 +242,7 @@ class SetMutation : public Mutation { bool equal_to(const Mutation& other) const override; private: - const FieldValue value_; + const ObjectValue value_; }; /** @@ -261,7 +261,7 @@ class SetMutation : public Mutation { class PatchMutation : public Mutation { public: PatchMutation(DocumentKey&& key, - FieldValue&& value, + ObjectValue&& value, FieldMask&& mask, Precondition&& precondition); @@ -281,7 +281,7 @@ class PatchMutation : public Mutation { /** * Returns the fields and associated values to use when patching the document. */ - const FieldValue& value() const { + const ObjectValue& value() const { return value_; } @@ -297,10 +297,10 @@ class PatchMutation : public Mutation { bool equal_to(const Mutation& other) const override; private: - FieldValue PatchDocument(const MaybeDocument* maybe_doc) const; - FieldValue PatchObject(FieldValue obj) const; + ObjectValue PatchDocument(const MaybeDocument* maybe_doc) const; + ObjectValue PatchObject(ObjectValue obj) const; - const FieldValue value_; + const ObjectValue value_; const FieldMask mask_; }; diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc index fc727da2ef0..06703e2e310 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.cc +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -32,6 +32,7 @@ #include "Firestore/core/include/firebase/firestore/timestamp.h" #include "Firestore/core/src/firebase/firestore/model/document.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/field_value.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/nanopb/reader.h" @@ -101,8 +102,8 @@ std::vector Serializer::DecodeBytes(const pb_bytes_array_t* bytes) { namespace { -ObjectValue::Map DecodeMapValue(Reader* reader, - const google_firestore_v1_MapValue& map_value); +FieldValue::Map DecodeMapValue(Reader* reader, + const google_firestore_v1_MapValue& map_value); // There's no f:f::model equivalent of StructuredQuery, so we'll create our // own struct for decoding. We could use nanopb's struct, but it's slightly @@ -119,7 +120,7 @@ struct StructuredQuery { // TODO(rsgowman): other fields }; -ObjectValue::Map::value_type DecodeFieldsEntry( +FieldValue::Map::value_type DecodeFieldsEntry( Reader* reader, const google_firestore_v1_Document_FieldsEntry& fields) { std::string key = Serializer::DecodeString(fields.key); FieldValue value = Serializer::DecodeFieldValue(reader, fields.value); @@ -130,33 +131,32 @@ ObjectValue::Map::value_type DecodeFieldsEntry( return {}; } - return ObjectValue::Map::value_type{std::move(key), std::move(value)}; + return FieldValue::Map::value_type{std::move(key), std::move(value)}; } -ObjectValue::Map DecodeFields( +FieldValue::Map DecodeFields( Reader* reader, size_t count, const google_firestore_v1_Document_FieldsEntry* fields) { - ObjectValue::Map result; + FieldValue::Map result; for (size_t i = 0; i < count; i++) { - ObjectValue::Map::value_type kv = DecodeFieldsEntry(reader, fields[i]); + FieldValue::Map::value_type kv = DecodeFieldsEntry(reader, fields[i]); result = result.insert(std::move(kv.first), std::move(kv.second)); } return result; } -google_firestore_v1_MapValue EncodeMapValue( - const ObjectValue::Map& object_value_map) { +google_firestore_v1_MapValue EncodeMapValue(const ObjectValue& object_value) { google_firestore_v1_MapValue result{}; - size_t count = object_value_map.size(); + size_t count = object_value.GetInternalValue().size(); result.fields_count = static_cast(count); result.fields = MakeArray(count); int i = 0; - for (const auto& kv : object_value_map) { + for (const auto& kv : object_value.GetInternalValue()) { result.fields[i].key = Serializer::EncodeString(kv.first); result.fields[i].value = Serializer::EncodeFieldValue(kv.second); i++; @@ -165,9 +165,9 @@ google_firestore_v1_MapValue EncodeMapValue( return result; } -ObjectValue::Map DecodeMapValue(Reader* reader, - const google_firestore_v1_MapValue& map_value) { - ObjectValue::Map result; +FieldValue::Map DecodeMapValue(Reader* reader, + const google_firestore_v1_MapValue& map_value) { + FieldValue::Map result; for (size_t i = 0; i < map_value.fields_count; i++) { std::string key = Serializer::DecodeString(map_value.fields[i].key); @@ -310,8 +310,7 @@ google_firestore_v1_Value Serializer::EncodeFieldValue( case FieldValue::Type::Object: result.which_value_type = google_firestore_v1_Value_map_value_tag; - result.map_value = - EncodeMapValue(field_value.object_value().internal_value); + result.map_value = EncodeMapValue(ObjectValue(field_value)); break; default: @@ -416,11 +415,11 @@ google_firestore_v1_Document Serializer::EncodeDocument( result.name = EncodeString(EncodeKey(key)); // Encode Document.fields (unless it's empty) - size_t count = object_value.internal_value.size(); + size_t count = object_value.GetInternalValue().size(); result.fields_count = static_cast(count); result.fields = MakeArray(count); int i = 0; - for (const auto& kv : object_value.internal_value) { + for (const auto& kv : object_value.GetInternalValue()) { result.fields[i].key = EncodeString(kv.first); result.fields[i].value = EncodeFieldValue(kv.second); i++; @@ -457,7 +456,7 @@ std::unique_ptr Serializer::DecodeFoundDocument( "Tried to deserialize a found document from a missing document."); DocumentKey key = DecodeKey(reader, DecodeString(response.found.name)); - ObjectValue::Map value = + FieldValue::Map value = DecodeFields(reader, response.found.fields_count, response.found.fields); SnapshotVersion version = DecodeSnapshotVersion(reader, response.found.update_time); @@ -466,7 +465,7 @@ std::unique_ptr Serializer::DecodeFoundDocument( reader->Fail("Got a document response with no snapshot version"); } - return absl::make_unique(FieldValue::FromMap(std::move(value)), + return absl::make_unique(ObjectValue::FromMap(std::move(value)), std::move(key), std::move(version), DocumentState::kSynced); } @@ -492,12 +491,12 @@ std::unique_ptr Serializer::DecodeMissingDocument( std::unique_ptr Serializer::DecodeDocument( Reader* reader, const google_firestore_v1_Document& proto) const { - ObjectValue::Map fields_internal = + FieldValue::Map fields_internal = DecodeFields(reader, proto.fields_count, proto.fields); SnapshotVersion version = DecodeSnapshotVersion(reader, proto.update_time); return absl::make_unique( - FieldValue::FromMap(std::move(fields_internal)), + ObjectValue::FromMap(std::move(fields_internal)), DecodeKey(reader, DecodeString(proto.name)), std::move(version), DocumentState::kSynced); } @@ -514,16 +513,14 @@ google_firestore_v1_Write Serializer::EncodeMutation( case Mutation::Type::kSet: { result.which_operation = google_firestore_v1_Write_update_tag; result.update = EncodeDocument( - mutation.key(), - static_cast(mutation).value().object_value()); + mutation.key(), static_cast(mutation).value()); return result; } case Mutation::Type::kPatch: { result.which_operation = google_firestore_v1_Write_update_tag; auto patch_mutation = static_cast(mutation); - result.update = - EncodeDocument(mutation.key(), patch_mutation.value().object_value()); + result.update = EncodeDocument(mutation.key(), patch_mutation.value()); result.update_mask = EncodeDocumentMask(patch_mutation.mask()); return result; } @@ -565,7 +562,7 @@ std::unique_ptr Serializer::DecodeMutation( switch (mutation.which_operation) { case google_firestore_v1_Write_update_tag: { DocumentKey key = DecodeKey(reader, DecodeString(mutation.update.name)); - FieldValue value = FieldValue::FromMap(DecodeFields( + ObjectValue value = ObjectValue::FromMap(DecodeFields( reader, mutation.update.fields_count, mutation.update.fields)); FieldMask mask = DecodeDocumentMask(mutation.update_mask); if (mask.size() > 0) { diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h index 086db7d191f..9fb5246f2f8 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.h +++ b/Firestore/core/src/firebase/firestore/remote/serializer.h @@ -194,11 +194,6 @@ class Serializer { std::unique_ptr DecodeDocument( nanopb::Reader* reader, const google_firestore_v1_Document& proto) const; - static void EncodeObjectMap(const model::ObjectValue::Map& object_value_map, - uint32_t map_tag, - uint32_t key_tag, - uint32_t value_tag); - static google_protobuf_Timestamp EncodeVersion( const model::SnapshotVersion& version); @@ -223,10 +218,6 @@ class Serializer { nanopb::Reader* reader, const google_firestore_v1_BatchGetDocumentsResponse& response) const; - static void EncodeFieldsEntry(const model::ObjectValue::Map::value_type& kv, - uint32_t key_tag, - uint32_t value_tag); - std::string EncodeQueryPath(const model::ResourcePath& path) const; const model::DatabaseId& database_id_; diff --git a/Firestore/core/test/firebase/firestore/local/local_serializer_test.cc b/Firestore/core/test/firebase/firestore/local/local_serializer_test.cc index f7d739c461f..dac54256407 100644 --- a/Firestore/core/test/firebase/firestore/local/local_serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/local/local_serializer_test.cc @@ -58,6 +58,7 @@ using model::MaybeDocument; using model::Mutation; using model::MutationBatch; using model::NoDocument; +using model::ObjectValue; using model::PatchMutation; using model::Precondition; using model::SetMutation; @@ -258,8 +259,8 @@ TEST_F(LocalSerializerTest, EncodesMutationBatch) { {"num", FieldValue::FromInteger(1)}}); std::unique_ptr patch = absl::make_unique( Key("bar/baz"), - FieldValue::FromMap({{"a", FieldValue::FromString("b")}, - {"num", FieldValue::FromInteger(1)}}), + ObjectValue::FromMap({{"a", FieldValue::FromString("b")}, + {"num", FieldValue::FromInteger(1)}}), FieldMask({FieldPath({"a"})}), Precondition::Exists(true)); std::unique_ptr del = testutil::DeleteMutation("baz/quux"); diff --git a/Firestore/core/test/firebase/firestore/model/document_test.cc b/Firestore/core/test/firebase/firestore/model/document_test.cc index 0aaba50abbb..ae09b75825d 100644 --- a/Firestore/core/test/firebase/firestore/model/document_test.cc +++ b/Firestore/core/test/firebase/firestore/model/document_test.cc @@ -16,6 +16,7 @@ #include "Firestore/core/src/firebase/firestore/model/document.h" +#include "Firestore/core/src/firebase/firestore/model/field_value.h" #include "Firestore/core/src/firebase/firestore/model/unknown_document.h" #include "absl/strings/string_view.h" @@ -32,7 +33,7 @@ inline Document MakeDocument(const absl::string_view data, const Timestamp& timestamp, DocumentState document_state) { return Document( - FieldValue::FromMap({{"field", FieldValue::FromString(data.data())}}), + ObjectValue::FromMap({{"field", FieldValue::FromString(data.data())}}), DocumentKey::FromPathString(path.data()), SnapshotVersion(timestamp), document_state); } @@ -43,7 +44,7 @@ TEST(Document, Getter) { const Document& doc = MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), DocumentState::kLocalMutations); EXPECT_EQ(MaybeDocument::Type::Document, doc.type()); - EXPECT_EQ(FieldValue::FromMap({{"field", FieldValue::FromString("foo")}}), + EXPECT_EQ(ObjectValue::FromMap({{"field", FieldValue::FromString("foo")}}), doc.data()); EXPECT_EQ(DocumentKey::FromPathString("i/am/a/path"), doc.key()); EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)), doc.version()); @@ -74,11 +75,11 @@ TEST(Document, Comparison) { // Document and MaybeDocument will not equal. In particular, Document and // NoDocument will not equal, which I won't test here. - EXPECT_NE(Document(FieldValue(FieldValue::EmptyObject()), - DocumentKey::FromPathString("same/path"), - SnapshotVersion(Timestamp()), DocumentState::kSynced), - UnknownDocument(DocumentKey::FromPathString("same/path"), - SnapshotVersion(Timestamp()))); + EXPECT_NE( + Document(ObjectValue::Empty(), DocumentKey::FromPathString("same/path"), + SnapshotVersion(Timestamp()), DocumentState::kSynced), + UnknownDocument(DocumentKey::FromPathString("same/path"), + SnapshotVersion(Timestamp()))); } } // namespace model 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 fa3902d52f4..b1d1f8b70b5 100644 --- a/Firestore/core/test/firebase/firestore/model/field_value_test.cc +++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc @@ -189,19 +189,16 @@ TEST(FieldValue, ArrayType) { } TEST(FieldValue, ObjectType) { - const FieldValue empty = FieldValue::FromMap(ObjectValue::Empty()); - ObjectValue::Map object{{"null", FieldValue::Null()}, - {"true", FieldValue::True()}, - {"false", FieldValue::False()}}; + const ObjectValue empty = ObjectValue::Empty(); + FieldValue::Map object{{"null", FieldValue::Null()}, + {"true", FieldValue::True()}, + {"false", FieldValue::False()}}; // copy the map - const FieldValue small = FieldValue::FromMap(object); - ObjectValue::Map another_object{{"null", FieldValue::Null()}, - {"true", FieldValue::False()}}; + const ObjectValue small = ObjectValue::FromMap(object); + FieldValue::Map another_object{{"null", FieldValue::Null()}, + {"true", FieldValue::False()}}; // move the array - const FieldValue large = FieldValue::FromMap(std::move(another_object)); - EXPECT_EQ(Type::Object, empty.type()); - EXPECT_EQ(Type::Object, small.type()); - EXPECT_EQ(Type::Object, large.type()); + const ObjectValue large = ObjectValue::FromMap(std::move(another_object)); EXPECT_TRUE(empty < small); EXPECT_FALSE(small < empty); EXPECT_FALSE(small < small); @@ -334,18 +331,18 @@ TEST(FieldValue, Copy) { clone = null_value; EXPECT_EQ(FieldValue::Null(), clone); - const FieldValue object_value = FieldValue::FromMap(ObjectValue::Map{ - {"true", FieldValue::True()}, {"false", FieldValue::False()}}); + const FieldValue object_value = FieldValue::FromMap( + {{"true", FieldValue::True()}, {"false", FieldValue::False()}}); clone = object_value; - EXPECT_EQ(FieldValue::FromMap(ObjectValue::Map{ - {"true", FieldValue::True()}, {"false", FieldValue::False()}}), + EXPECT_EQ(FieldValue::FromMap( + {{"true", FieldValue::True()}, {"false", FieldValue::False()}}), clone); - EXPECT_EQ(FieldValue::FromMap(ObjectValue::Map{ - {"true", FieldValue::True()}, {"false", FieldValue::False()}}), + EXPECT_EQ(FieldValue::FromMap( + {{"true", FieldValue::True()}, {"false", FieldValue::False()}}), object_value); clone = *&clone; - EXPECT_EQ(FieldValue::FromMap(ObjectValue::Map{ - {"true", FieldValue::True()}, {"false", FieldValue::False()}}), + EXPECT_EQ(FieldValue::FromMap( + {{"true", FieldValue::True()}, {"false", FieldValue::False()}}), clone); clone = null_value; EXPECT_EQ(FieldValue::Null(), clone); @@ -425,11 +422,11 @@ TEST(FieldValue, Move) { clone = FieldValue::Null(); EXPECT_EQ(FieldValue::Null(), clone); - FieldValue object_value = FieldValue::FromMap(ObjectValue::Map{ - {"true", FieldValue::True()}, {"false", FieldValue::False()}}); + FieldValue object_value = FieldValue::FromMap( + {{"true", FieldValue::True()}, {"false", FieldValue::False()}}); clone = std::move(object_value); - EXPECT_EQ(FieldValue::FromMap(ObjectValue::Map{ - {"true", FieldValue::True()}, {"false", FieldValue::False()}}), + EXPECT_EQ(FieldValue::FromMap( + {{"true", FieldValue::True()}, {"false", FieldValue::False()}}), clone); clone = FieldValue::Null(); EXPECT_EQ(FieldValue::Null(), clone); @@ -489,13 +486,13 @@ TEST(FieldValue, CompareWithOperator) { TEST(FieldValue, Set) { // Set a field in an object. - const FieldValue value = FieldValue::FromMap({ + const ObjectValue value = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, {"b", FieldValue::FromMap({ {"ba", FieldValue::FromString("BA")}, })}, }); - const FieldValue expected = FieldValue::FromMap({ + const ObjectValue expected = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, {"b", FieldValue::FromMap({ {"ba", FieldValue::FromString("BA")}, @@ -508,10 +505,10 @@ TEST(FieldValue, Set) { TEST(FieldValue, SetRecursive) { // Set a field in a new object. - const FieldValue value = FieldValue::FromMap({ + const ObjectValue value = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, }); - const FieldValue expected = FieldValue::FromMap({ + const ObjectValue expected = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, {"b", FieldValue::FromMap({ {"bb", FieldValue::FromString("BB")}, @@ -522,14 +519,14 @@ TEST(FieldValue, SetRecursive) { } TEST(FieldValue, Delete) { - const FieldValue value = FieldValue::FromMap({ + const ObjectValue value = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, {"b", FieldValue::FromMap({ {"ba", FieldValue::FromString("BA")}, {"bb", FieldValue::FromString("BB")}, })}, }); - const FieldValue expected = FieldValue::FromMap({ + const ObjectValue expected = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, {"b", FieldValue::FromMap({ {"ba", FieldValue::FromString("BA")}, @@ -539,7 +536,7 @@ TEST(FieldValue, Delete) { } TEST(FieldValue, DeleteNothing) { - const FieldValue value = FieldValue::FromMap({ + const ObjectValue value = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, {"b", FieldValue::FromMap({ {"ba", FieldValue::FromString("BA")}, @@ -550,7 +547,7 @@ TEST(FieldValue, DeleteNothing) { } TEST(FieldValue, Get) { - const FieldValue value = FieldValue::FromMap({ + const ObjectValue value = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, {"b", FieldValue::FromMap({ {"ba", FieldValue::FromString("BA")}, @@ -563,7 +560,7 @@ TEST(FieldValue, Get) { } TEST(FieldValue, GetNothing) { - const FieldValue value = FieldValue::FromMap({ + const ObjectValue value = ObjectValue::FromMap({ {"a", FieldValue::FromString("A")}, {"b", FieldValue::FromMap({ {"ba", FieldValue::FromString("BA")}, diff --git a/Firestore/core/test/firebase/firestore/model/mutation_test.cc b/Firestore/core/test/firebase/firestore/model/mutation_test.cc index b688511d134..849ab616b93 100644 --- a/Firestore/core/test/firebase/firestore/model/mutation_test.cc +++ b/Firestore/core/test/firebase/firestore/model/mutation_test.cc @@ -114,7 +114,7 @@ TEST(Mutation, DeletesValuesFromTheFieldMask) { {"baz", FieldValue::FromString("baz-value")}})}})); std::unique_ptr patch = - PatchMutation("collection/key", ObjectValue::Empty(), {Field("foo.bar")}); + PatchMutation("collection/key", FieldValue::Map(), {Field("foo.bar")}); MaybeDocumentPtr patch_doc = patch->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc index a7221e68890..307a36e9e33 100644 --- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc @@ -36,6 +36,7 @@ #include "Firestore/Protos/cpp/google/firestore/v1/firestore.pb.h" #include "Firestore/core/include/firebase/firestore/firestore_errors.h" #include "Firestore/core/include/firebase/firestore/timestamp.h" +#include "Firestore/core/src/firebase/firestore/model/field_path.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/nanopb/reader.h" @@ -195,11 +196,10 @@ class SerializerTest : public ::testing::Test { std::vector EncodeDocument(Serializer* serializer, const DocumentKey& key, - const FieldValue& value) { + const ObjectValue& value) { std::vector bytes; Writer writer = Writer::Wrap(&bytes); - google_firestore_v1_Document proto = - serializer->EncodeDocument(key, value.object_value()); + google_firestore_v1_Document proto = serializer->EncodeDocument(key, value); writer.WriteNanopbMessage(google_firestore_v1_Document_fields, &proto); serializer->FreeNanopbMessage(google_firestore_v1_Document_fields, &proto); return bytes; @@ -316,7 +316,7 @@ class SerializerTest : public ::testing::Test { void ExpectSerializationRoundTrip( const DocumentKey& key, - const FieldValue& value, + const ObjectValue& value, const SnapshotVersion& update_time, const v1::BatchGetDocumentsResponse& proto) { std::vector bytes = EncodeDocument(&serializer, key, value); @@ -349,7 +349,7 @@ class SerializerTest : public ::testing::Test { void ExpectDeserializationRoundTrip( const DocumentKey& key, - const absl::optional value, + const absl::optional value, const SnapshotVersion& version, // either update_time or read_time const v1::BatchGetDocumentsResponse& proto) { size_t size = proto.ByteSizeLong(); @@ -797,7 +797,7 @@ TEST_F(SerializerTest, BadKey) { TEST_F(SerializerTest, EncodesEmptyDocument) { DocumentKey key = DocumentKey::FromPathString("path/to/the/doc"); - FieldValue empty_value = FieldValue::EmptyObject(); + ObjectValue empty_value = ObjectValue::Empty(); SnapshotVersion update_time = SnapshotVersion{{1234, 5678}}; v1::BatchGetDocumentsResponse proto; @@ -817,7 +817,7 @@ TEST_F(SerializerTest, EncodesEmptyDocument) { TEST_F(SerializerTest, EncodesNonEmptyDocument) { DocumentKey key = DocumentKey::FromPathString("path/to/the/doc"); - FieldValue fields = FieldValue::FromMap({ + ObjectValue fields = ObjectValue::FromMap({ {"foo", FieldValue::FromString("bar")}, {"two", FieldValue::FromInteger(2)}, {"nested", FieldValue::FromMap({ diff --git a/Firestore/core/test/firebase/firestore/testutil/testutil.cc b/Firestore/core/test/firebase/firestore/testutil/testutil.cc index e24185408b8..e08678b932b 100644 --- a/Firestore/core/test/firebase/firestore/testutil/testutil.cc +++ b/Firestore/core/test/firebase/firestore/testutil/testutil.cc @@ -24,15 +24,17 @@ namespace testutil { std::unique_ptr PatchMutation( absl::string_view path, - const model::ObjectValue::Map& values, + const model::FieldValue::Map& values, // TODO(rsgowman): Investigate changing update_mask to a set. const std::vector* update_mask) { - model::FieldValue object_value = model::FieldValue::EmptyObject(); + model::ObjectValue object_value = model::ObjectValue::Empty(); std::set object_mask; for (const auto& kv : values) { model::FieldPath field_path = Field(kv.first); object_mask.insert(field_path); + // TODO(rsgowman): This will abort if kv.second.string_value.type() != + // String if (kv.second.string_value() != kDeleteSentinel) { object_value = object_value.Set(field_path, kv.second); } diff --git a/Firestore/core/test/firebase/firestore/testutil/testutil.h b/Firestore/core/test/firebase/firestore/testutil/testutil.h index 0568ef7530b..07a1c8b8e81 100644 --- a/Firestore/core/test/firebase/firestore/testutil/testutil.h +++ b/Firestore/core/test/firebase/firestore/testutil/testutil.h @@ -80,9 +80,9 @@ inline model::SnapshotVersion Version(int64_t version) { inline model::Document Doc( absl::string_view key, int64_t version = 0, - const model::ObjectValue::Map& data = model::ObjectValue::Empty(), + const model::FieldValue::Map& data = model::FieldValue::Map(), model::DocumentState document_state = model::DocumentState::kSynced) { - return model::Document{model::FieldValue::FromMap(data), Key(key), + return model::Document{model::ObjectValue::FromMap(data), Key(key), Version(version), document_state}; } @@ -141,20 +141,20 @@ inline core::Query Query(absl::string_view path) { inline std::unique_ptr SetMutation( absl::string_view path, - const model::ObjectValue::Map& values = model::ObjectValue::Empty()) { + const model::FieldValue::Map& values = model::FieldValue::Map()) { return absl::make_unique( - Key(path), model::FieldValue::FromMap(values), + Key(path), model::ObjectValue::FromMap(values), model::Precondition::None()); } std::unique_ptr PatchMutation( absl::string_view path, - const model::ObjectValue::Map& values = model::ObjectValue::Empty(), + const model::FieldValue::Map& values = model::FieldValue::Map(), const std::vector* update_mask = nullptr); inline std::unique_ptr PatchMutation( absl::string_view path, - const model::ObjectValue::Map& values, + const model::FieldValue::Map& values, const std::vector& update_mask) { return PatchMutation(path, values, &update_mask); } From 3c3fe7927f9cada4819627e4a266929786f59caa Mon Sep 17 00:00:00 2001 From: dmandar Date: Tue, 19 Mar 2019 14:13:17 -0700 Subject: [PATCH 059/214] Fix for issue where matchesshortlinkformat was returning true for all links. (#2563) * Fix issue with matchesShortLinkFormat always returning true. * Fix style. --- .../DynamicLinks/Tests/FIRDynamicLinksTest.m | 107 ++++++++++++++++-- .../DynamicLinks/Utilities/FDLUtilities.m | 21 ++-- 2 files changed, 106 insertions(+), 22 deletions(-) diff --git a/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m b/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m index 92a4cef2590..1eb5e227585 100644 --- a/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m +++ b/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m @@ -77,6 +77,7 @@ - (BOOL)setUpWithLaunchOptions:(nullable NSDictionary *)launchOptions clientID:(NSString *)clientID urlScheme:(nullable NSString *)urlScheme userDefaults:(nullable NSUserDefaults *)userDefaults; +- (BOOL)canParseUniversalLinkURL:(nullable NSURL *)url; @end @interface FakeShortLinkResolver : FIRDynamicLinkNetworking @@ -774,13 +775,90 @@ - (void)testUniversalLinkWithSubdomain_DeepLinkWithParameters { XCTAssertEqualObjects(dynamicLink.url.absoluteString, parsedDeepLinkString); } -- (void)testMatchesUnversalLinkWithLongDurableLink { - NSString *urlString = - @"https://sample.page.link?link=https://google.com/test&ibi=com.google.sample&ius=79306483"; - NSURL *url = [NSURL URLWithString:urlString]; - BOOL matchesShort = [self.service matchesShortLinkFormat:url]; +- (void)testMatchesShortLinkFormat { + NSArray *urlStrings = + @[ @"https://test.app.goo.gl/xyz", @"https://test.app.goo.gl/xyz?link=" ]; + + for (NSString *urlString in urlStrings) { + NSURL *url = [NSURL URLWithString:urlString]; + BOOL matchesShortLinkFormat = [self.service matchesShortLinkFormat:url]; + + XCTAssertTrue(matchesShortLinkFormat, + @"Non-DDL domain URL matched short link format with URL: %@", url); + } +} + +// Custom domain entries in plist file: +// https://google.com +// https://google.com/one +// https://a.firebase.com/mypath +- (void)testFailMatchesShortLinkFormatForCustomDomains { + NSArray *urlStrings = @[ + @"https://google.com", + @"https://google.com?link=", + @"https://a.firebase.com", + @"https://a.firebase.com/mypath?link=", + ]; + + for (NSString *urlString in urlStrings) { + NSURL *url = [NSURL URLWithString:urlString]; + BOOL matchesShortLinkFormat = [self.service matchesShortLinkFormat:url]; + + XCTAssertFalse(matchesShortLinkFormat, + @"Non-DDL domain URL matched short link format with URL: %@", url); + } +} + +// Custom domain entries in plist file: +// https://google.com +// https://google.com/one +// https://a.firebase.com/mypath +- (void)testPassMatchesShortLinkFormatForCustomDomains { + NSArray *urlStrings = @[ + @"https://google.com/xyz", @"https://google.com/xyz/?link=", @"https://google.com/xyz?link=", + @"https://google.com/one/xyz", @"https://google.com/one/xyz?link=", + @"https://google.com/one?utm_campaignlink=", @"https://google.com/mylink", + @"https://google.com/one/mylink", @"https://a.firebase.com/mypath/mylink" + ]; + + for (NSString *urlString in urlStrings) { + NSURL *url = [NSURL URLWithString:urlString]; + BOOL matchesShortLinkFormat = [self.service matchesShortLinkFormat:url]; + + XCTAssertTrue(matchesShortLinkFormat, + @"Non-DDL domain URL matched short link format with URL: %@", url); + } +} - XCTAssertFalse(matchesShort, @"Long Durable Link should not match short link format"); +- (void)testPassMatchesShortLinkFormat { + NSArray *urlStrings = @[ + @"https://test.app.goo.gl/xyz", + @"https://test.app.goo.gl/xyz?link=", + ]; + + for (NSString *urlString in urlStrings) { + NSURL *url = [NSURL URLWithString:urlString]; + BOOL matchesShortLinkFormat = [self.service matchesShortLinkFormat:url]; + + XCTAssertTrue(matchesShortLinkFormat, + @"Non-DDL domain URL matched short link format with URL: %@", url); + } +} + +- (void)testFailMatchesShortLinkFormat { + NSArray *urlStrings = @[ + @"https://test.app.goo.gl", @"https://test.app.goo.gl?link=", @"https://test.app.goo.gl/", + @"https://sample.page.link?link=https://google.com/test&ibi=com.google.sample&ius=79306483" + @"https://sample.page.link/?link=https://google.com/test&ibi=com.google.sample&ius=79306483" + ]; + + for (NSString *urlString in urlStrings) { + NSURL *url = [NSURL URLWithString:urlString]; + BOOL matchesShortLinkFormat = [self.service matchesShortLinkFormat:url]; + + XCTAssertFalse(matchesShortLinkFormat, + @"Non-DDL domain URL matched short link format with URL: %@", url); + } } - (void)testMatchesUnversalLinkWithShortDurableLink { @@ -1073,19 +1151,26 @@ - (void)testValidCustomDomainNames { NSArray *urlStrings = @[ @"https://google.com/mylink", // Short FDL starting with 'https://google.com' @"https://google.com/one", // Short FDL starting with 'https://google.com' - @"https://google.com/?link=abcd", // Long FDL starting with 'https://google.com' - @"https://google.com/one/mylink", // Long FDL starting with 'https://google.com/one' + @"https://google.com/one/mylink", // Short FDL starting with 'https://google.com/one' @"https://a.firebase.com/mypath/mylink", // Short FDL starting https://a.firebase.com/mypath + ]; + + NSArray *longFDLURLStrings = @[ @"https://a.firebase.com/mypath/?link=abcd&test=1", // Long FDL starting with // https://a.firebase.com/mypath + @"https://google.com/?link=abcd", // Long FDL starting with 'https://google.com' ]; - for (NSString *urlString in urlStrings) { NSURL *url = [NSURL URLWithString:urlString]; BOOL matchesShortLinkFormat = [self.service matchesShortLinkFormat:url]; - XCTAssertTrue(matchesShortLinkFormat, - @"Non-DDL domain URL matched short link format with URL: %@", url); + XCTAssertTrue(matchesShortLinkFormat, @"URL did not validate as short link: %@", url); + } + for (NSString *urlString in longFDLURLStrings) { + NSURL *url = [NSURL URLWithString:urlString]; + BOOL matchesLongLinkFormat = [self.service canParseUniversalLinkURL:url]; + + XCTAssertTrue(matchesLongLinkFormat, @"URL did not validate as long link: %@", url); } } diff --git a/Firebase/DynamicLinks/Utilities/FDLUtilities.m b/Firebase/DynamicLinks/Utilities/FDLUtilities.m index 564507fb14a..4201de0a2e1 100644 --- a/Firebase/DynamicLinks/Utilities/FDLUtilities.m +++ b/Firebase/DynamicLinks/Utilities/FDLUtilities.m @@ -211,12 +211,11 @@ BOOL FIRDLIsURLForWhiteListedCustomDomain(NSURL *_Nullable URL) { options:NSCaseInsensitiveSearch | NSAnchoredSearch] .location) == 0) { // The (short) URL needs to be longer than the domainURIPrefix, it's first character after - // the domainURIPrefix needs to be '/' or '?' and should be followed by at-least one more + // the domainURIPrefix needs to be '/' and should be followed by at-least one more // character. if (urlStr.length > domainURIPrefixStr.length + 1 && - ([urlStr characterAtIndex:domainURIPrefixStr.length] == '/' || - [urlStr characterAtIndex:domainURIPrefixStr.length] == '?')) { - // Check if there are any more '/' after the first '/' or '?' trailing the + ([urlStr characterAtIndex:domainURIPrefixStr.length] == '/')) { + // Check if there are any more '/' after the first '/'trailing the // domainURIPrefix. This does not apply to unique match links copied from the clipboard. // The clipboard links will have '?link=' after the domainURIPrefix. NSString *urlWithoutDomainURIPrefix = @@ -249,17 +248,17 @@ BOOL FIRDLCanParseUniversalLinkURL(NSURL *_Nullable URL) { } BOOL FIRDLMatchesShortLinkFormat(NSURL *URL) { - // Short Durable Link URLs always have a path, except for certain custom domain URLs e.g. - // 'https://google.com?link=abcd' will not have a path component. - // FIRDLIsURLForWhiteListedCustomDomain implicitely checks for path component in custom domain - // URLs. - BOOL hasPath = URL.path.length > 0 || FIRDLIsURLForWhiteListedCustomDomain(URL); + // Short Durable Link URLs always have a path. + BOOL hasPath = URL.path.length > 0; + BOOL matchesRegularExpression = + ([URL.path rangeOfString:@"/[^/]+" options:NSRegularExpressionSearch].location != NSNotFound); // Must be able to parse (also checks if the URL conforms to *.app.goo.gl/* or goo.gl/app/*) - BOOL canParse = FIRDLCanParseUniversalLinkURL(URL); + BOOL canParse = FIRDLCanParseUniversalLinkURL(URL) | FIRDLIsURLForWhiteListedCustomDomain(URL); + ; // Path cannot be prefixed with /link/dismiss BOOL isDismiss = [[URL.path lowercaseString] hasPrefix:@"/link/dismiss"]; - return hasPath && !isDismiss && canParse; + return hasPath && matchesRegularExpression && !isDismiss && canParse; } NSString *FIRDLMatchTypeStringFromServerString(NSString *_Nullable serverMatchTypeString) { From ef6c538748137af52de8a2e5c3fd96e638701987 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 19 Mar 2019 17:29:18 -0700 Subject: [PATCH 060/214] Release 5.19.0 (#2589) * Update versions for Release 5.19.0 * update the changelog (#2583) * Revert #2491 (#2582) --- Example/Podfile | 2 +- Firebase/Core/FIROptions.m | 4 ++-- FirebaseAuth.podspec | 2 +- FirebaseCore.podspec | 4 ++-- FirebaseDatabase.podspec | 2 +- FirebaseDynamicLinks.podspec | 2 +- FirebaseFirestore.podspec | 2 +- FirebaseFunctions.podspec | 2 +- FirebaseInAppMessagingDisplay.podspec | 2 +- FirebaseInstanceID.podspec | 2 +- FirebaseMessaging.podspec | 2 +- FirebaseStorage.podspec | 2 +- Firestore/Example/Podfile | 2 +- Releases/Manifests/5.19.0.json | 12 ++++++++++++ SymbolCollisionTest/Podfile | 2 +- 15 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 Releases/Manifests/5.19.0.json diff --git a/Example/Podfile b/Example/Podfile index ece18d95b4d..f8016c949ab 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -19,7 +19,7 @@ target 'Core_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.18.0' + pod 'Firebase/CoreOnly', '5.19.0' target 'Core_Tests_iOS' do inherit! :search_paths diff --git a/Firebase/Core/FIROptions.m b/Firebase/Core/FIROptions.m index 0650420738d..75c7706aee6 100644 --- a/Firebase/Core/FIROptions.m +++ b/Firebase/Core/FIROptions.m @@ -41,8 +41,8 @@ // Library version ID. NSString *const kFIRLibraryVersionID = @"5" // Major version (one or more digits) - @"03" // Minor version (exactly 2 digits) - @"01" // Build number (exactly 2 digits) + @"04" // Minor version (exactly 2 digits) + @"00" // Build number (exactly 2 digits) @"000"; // Fixed "000" // Plist file name. NSString *const kServiceInfoFileName = @"GoogleService-Info"; diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index 06f09f98838..9dd85a9c168 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAuth' - s.version = '5.4.0' + s.version = '5.4.1' s.summary = 'The official iOS client for Firebase Authentication (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 7a390876349..5d72a48d899 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCore' - s.version = '5.3.1' + s.version = '5.4.0' s.summary = 'Firebase Core for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC @@ -33,7 +33,7 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration s.pod_target_xcconfig = { 'GCC_C_LANGUAGE_STANDARD' => 'c99', 'GCC_PREPROCESSOR_DEFINITIONS' => - 'FIRCore_VERSION=' + s.version.to_s + ' Firebase_VERSION=5.18.0', + 'FIRCore_VERSION=' + s.version.to_s + ' Firebase_VERSION=5.19.0', 'OTHER_CFLAGS' => '-fno-autolink' } end diff --git a/FirebaseDatabase.podspec b/FirebaseDatabase.podspec index bed3ef657ab..062db75bd7a 100644 --- a/FirebaseDatabase.podspec +++ b/FirebaseDatabase.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseDatabase' - s.version = '5.1.0' + s.version = '5.1.1' s.summary = 'Firebase Open Source Libraries for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/FirebaseDynamicLinks.podspec b/FirebaseDynamicLinks.podspec index a5ea8074890..067a07e2125 100644 --- a/FirebaseDynamicLinks.podspec +++ b/FirebaseDynamicLinks.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseDynamicLinks' - s.version = '3.4.1' + s.version = '3.4.2' s.summary = 'Firebase DynamicLinks for iOS' s.description = <<-DESC diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 8bed72adbd7..fc32ab23caa 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFirestore' - s.version = '1.0.2' + s.version = '1.1.0' s.summary = 'Google Cloud Firestore for iOS' s.description = <<-DESC diff --git a/FirebaseFunctions.podspec b/FirebaseFunctions.podspec index e2d427de7c3..a708b44b5b0 100644 --- a/FirebaseFunctions.podspec +++ b/FirebaseFunctions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFunctions' - s.version = '2.3.0' + s.version = '2.4.0' s.summary = 'Cloud Functions for Firebase iOS SDK.' s.description = <<-DESC diff --git a/FirebaseInAppMessagingDisplay.podspec b/FirebaseInAppMessagingDisplay.podspec index 84ead8247ee..50f5eb76725 100644 --- a/FirebaseInAppMessagingDisplay.podspec +++ b/FirebaseInAppMessagingDisplay.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInAppMessagingDisplay' - s.version = '0.13.0' + s.version = '0.13.1' s.summary = 'Firebase In-App Messaging UI for iOS' s.description = <<-DESC diff --git a/FirebaseInstanceID.podspec b/FirebaseInstanceID.podspec index d48b224cac2..e9b917603d8 100644 --- a/FirebaseInstanceID.podspec +++ b/FirebaseInstanceID.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInstanceID' - s.version = '3.7.0' + s.version = '3.8.0' s.summary = 'Firebase InstanceID for iOS' s.description = <<-DESC diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index 6bbd0999323..b49ff324106 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMessaging' - s.version = '3.3.2' + s.version = '3.4.0' s.summary = 'Firebase Messaging for iOS' s.description = <<-DESC diff --git a/FirebaseStorage.podspec b/FirebaseStorage.podspec index 71a996c030f..d9a9441142c 100644 --- a/FirebaseStorage.podspec +++ b/FirebaseStorage.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseStorage' - s.version = '3.1.0' + s.version = '3.1.1' s.summary = 'Firebase Storage for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index 9d719eb0f9b..00b6faf61c8 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -14,7 +14,7 @@ target 'Firestore_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.18.0' + pod 'Firebase/CoreOnly', '5.19.0' pod 'FirebaseAuth', :path => '../../' pod 'FirebaseAuthInterop', :path => '../../' diff --git a/Releases/Manifests/5.19.0.json b/Releases/Manifests/5.19.0.json new file mode 100644 index 00000000000..b8d333d612e --- /dev/null +++ b/Releases/Manifests/5.19.0.json @@ -0,0 +1,12 @@ +{ + "FirebaseAuth":"5.4.1", + "FirebaseCore":"5.4.0", + "FirebaseDatabase":"5.1.1", + "FirebaseDynamicLinks":"3.4.2", + "FirebaseFirestore":"1.1.0", + "FirebaseFunctions":"2.4.0", + "FirebaseInAppMessagingDisplay":"0.13.1", + "FirebaseInstanceID":"3.8.0", + "FirebaseMessaging":"3.4.0", + "FirebaseStorage":"3.1.1" +} diff --git a/SymbolCollisionTest/Podfile b/SymbolCollisionTest/Podfile index 5252216278c..d2ae9ceaa29 100644 --- a/SymbolCollisionTest/Podfile +++ b/SymbolCollisionTest/Podfile @@ -6,7 +6,7 @@ target 'SymbolCollisionTest' do # use_frameworks! # Firebase Pods - pod 'Firebase', '5.18.0' + pod 'Firebase', '5.19.0' pod 'FirebaseAnalytics' pod 'FirebaseAuth' pod 'FirebaseCore' From 7babc0a7f611f0047b28b9b5c83329013ac6430c Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 20 Mar 2019 08:04:57 -0700 Subject: [PATCH 061/214] Port FIRQuerySnapshot to C++ (#2581) --- .../Tests/API/FIRQuerySnapshotTests.mm | 17 ++- Firestore/Example/Tests/API/FSTAPIHelpers.mm | 11 +- Firestore/Source/API/FIRQuery.mm | 21 ++-- .../Source/API/FIRQuerySnapshot+Internal.h | 20 +++- Firestore/Source/API/FIRQuerySnapshot.mm | 108 ++++++----------- Firestore/Source/Core/FSTFirestoreClient.mm | 15 ++- .../firebase/firestore/api/query_snapshot.h | 113 ++++++++++++++++++ .../firebase/firestore/api/query_snapshot.mm | 70 +++++++++++ 8 files changed, 261 insertions(+), 114 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/api/query_snapshot.h create mode 100644 Firestore/core/src/firebase/firestore/api/query_snapshot.mm diff --git a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm index beb02e48ff0..edec5367b56 100644 --- a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm +++ b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm @@ -94,7 +94,7 @@ - (void)testIncludeMetadataChanges { DocumentViewChange{doc2New, DocumentViewChange::Type::kModified}, }; - FIRFirestore *firestore = FSTTestFirestore(); + Firestore *firestore = FSTTestFirestore().wrapped; FSTQuery *query = FSTTestQuery("foo"); ViewSnapshot viewSnapshot{query, newDocuments, @@ -104,21 +104,20 @@ - (void)testIncludeMetadataChanges { /*from_cache=*/false, /*sync_state_changed=*/true, /*excludes_metadata_changes=*/false}; - FIRSnapshotMetadata *metadata = [[FIRSnapshotMetadata alloc] initWithPendingWrites:NO - fromCache:NO]; - FIRQuerySnapshot *snapshot = [FIRQuerySnapshot snapshotWithFirestore:firestore - originalQuery:query - snapshot:std::move(viewSnapshot) - metadata:metadata]; + SnapshotMetadata metadata(/*pending_writes=*/false, /*from_cache=*/false); + FIRQuerySnapshot *snapshot = [[FIRQuerySnapshot alloc] initWithFirestore:firestore + originalQuery:query + snapshot:std::move(viewSnapshot) + metadata:std::move(metadata)]; FIRQueryDocumentSnapshot *doc1Snap = - [[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore.wrapped + [[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore documentKey:doc1New.key document:doc1New fromCache:false hasPendingWrites:false]; FIRQueryDocumentSnapshot *doc2Snap = - [[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore.wrapped + [[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore documentKey:doc2New.key document:doc2New fromCache:false diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.mm b/Firestore/Example/Tests/API/FSTAPIHelpers.mm index 98642ba1320..bcab27f7bc4 100644 --- a/Firestore/Example/Tests/API/FSTAPIHelpers.mm +++ b/Firestore/Example/Tests/API/FSTAPIHelpers.mm @@ -97,8 +97,7 @@ NSDictionary *> *docsToAdd, bool hasPendingWrites, bool fromCache) { - FIRSnapshotMetadata *metadata = - [[FIRSnapshotMetadata alloc] initWithPendingWrites:hasPendingWrites fromCache:fromCache]; + SnapshotMetadata metadata(hasPendingWrites, fromCache); FSTDocumentSet *oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[]); DocumentKeySet mutatedKeys; for (NSString *key in oldDocs) { @@ -133,10 +132,10 @@ fromCache, /*sync_state_changed=*/true, /*excludes_metadata_changes=*/false}; - return [FIRQuerySnapshot snapshotWithFirestore:FSTTestFirestore() - originalQuery:FSTTestQuery(path) - snapshot:std::move(viewSnapshot) - metadata:metadata]; + return [[FIRQuerySnapshot alloc] initWithFirestore:FSTTestFirestore().wrapped + originalQuery:FSTTestQuery(path) + snapshot:std::move(viewSnapshot) + metadata:std::move(metadata)]; } NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 29f5da27499..33f8857d46f 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -171,7 +171,7 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)source - (id) addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions listener:(FIRQuerySnapshotBlock)listener { - FIRFirestore *firestore = self.firestore; + Firestore *firestore = self.firestore.wrapped; FSTQuery *query = self.query; ViewSnapshotHandler snapshotHandler = [listener, firestore, @@ -181,15 +181,12 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)source return; } ViewSnapshot snapshot = maybe_snapshot.ValueOrDie(); + SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache()); - FIRSnapshotMetadata *metadata = - [[FIRSnapshotMetadata alloc] initWithPendingWrites:snapshot.has_pending_writes() - fromCache:snapshot.from_cache()]; - - listener([FIRQuerySnapshot snapshotWithFirestore:firestore - originalQuery:query - snapshot:std::move(snapshot) - metadata:metadata], + listener([[FIRQuerySnapshot alloc] initWithFirestore:firestore + originalQuery:query + snapshot:std::move(snapshot) + metadata:std::move(metadata)], nil); }; @@ -198,9 +195,9 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)source snapshotHandler:std::move(snapshotHandler)]; FSTQueryListener *internalListener = - [firestore.client listenToQuery:query - options:internalOptions - viewSnapshotHandler:[asyncListener asyncSnapshotHandler]]; + [firestore->client() listenToQuery:query + options:internalOptions + viewSnapshotHandler:[asyncListener asyncSnapshotHandler]]; return [[FSTListenerRegistration alloc] initWithClient:self.firestore.client asyncListener:asyncListener internalListener:internalListener]; diff --git a/Firestore/Source/API/FIRQuerySnapshot+Internal.h b/Firestore/Source/API/FIRQuerySnapshot+Internal.h index f6812f54d34..37c953d7d33 100644 --- a/Firestore/Source/API/FIRQuerySnapshot+Internal.h +++ b/Firestore/Source/API/FIRQuerySnapshot+Internal.h @@ -16,6 +16,9 @@ #import "FIRQuerySnapshot.h" +#include "Firestore/core/src/firebase/firestore/api/firestore.h" +#include "Firestore/core/src/firebase/firestore/api/query_snapshot.h" +#include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" @class FIRFirestore; @@ -23,15 +26,22 @@ @class FSTDocumentSet; @class FSTQuery; +using firebase::firestore::api::Firestore; +using firebase::firestore::api::QuerySnapshot; +using firebase::firestore::api::SnapshotMetadata; +using firebase::firestore::core::ViewSnapshot; + NS_ASSUME_NONNULL_BEGIN /** Internal FIRQuerySnapshot API we don't want exposed in our public header files. */ -@interface FIRQuerySnapshot (Internal) +@interface FIRQuerySnapshot (/* Init */) + +- (instancetype)initWithSnapshot:(QuerySnapshot &&)snapshot NS_DESIGNATED_INITIALIZER; -+ (instancetype)snapshotWithFirestore:(FIRFirestore *)firestore - originalQuery:(FSTQuery *)query - snapshot:(firebase::firestore::core::ViewSnapshot)snapshot - metadata:(FIRSnapshotMetadata *)metadata; +- (instancetype)initWithFirestore:(Firestore *)firestore + originalQuery:(FSTQuery *)query + snapshot:(ViewSnapshot &&)snapshot + metadata:(SnapshotMetadata)metadata; @end diff --git a/Firestore/Source/API/FIRQuerySnapshot.mm b/Firestore/Source/API/FIRQuerySnapshot.mm index 942a7e684ed..837055612d3 100644 --- a/Firestore/Source/API/FIRQuerySnapshot.mm +++ b/Firestore/Source/API/FIRQuerySnapshot.mm @@ -23,6 +23,7 @@ #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRQuery+Internal.h" +#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentSet.h" @@ -31,123 +32,82 @@ #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" using firebase::firestore::api::Firestore; +using firebase::firestore::api::QuerySnapshot; using firebase::firestore::core::ViewSnapshot; NS_ASSUME_NONNULL_BEGIN -@interface FIRQuerySnapshot () - -- (instancetype)initWithFirestore:(FIRFirestore *)firestore - originalQuery:(FSTQuery *)query - snapshot:(ViewSnapshot &&)snapshot - metadata:(FIRSnapshotMetadata *)metadata; - -@property(nonatomic, strong, readonly) FIRFirestore *firestore; -@property(nonatomic, strong, readonly) FSTQuery *originalQuery; -- (const ViewSnapshot &)snapshot; - -@end - -@implementation FIRQuerySnapshot (Internal) - -+ (instancetype)snapshotWithFirestore:(FIRFirestore *)firestore - originalQuery:(FSTQuery *)query - snapshot:(ViewSnapshot)snapshot - metadata:(FIRSnapshotMetadata *)metadata { - return [[FIRQuerySnapshot alloc] initWithFirestore:firestore - originalQuery:query - snapshot:std::move(snapshot) - metadata:metadata]; -} +@implementation FIRQuerySnapshot { + QuerySnapshot _snapshot; -@end + FIRSnapshotMetadata *_cached_metadata; -@implementation FIRQuerySnapshot { // Cached value of the documents property. NSArray *_documents; // Cached value of the documentChanges property. NSArray *_documentChanges; BOOL _documentChangesIncludeMetadataChanges; - - ViewSnapshot _snapshot; } -- (instancetype)initWithFirestore:(FIRFirestore *)firestore - originalQuery:(FSTQuery *)query - snapshot:(ViewSnapshot &&)snapshot - metadata:(FIRSnapshotMetadata *)metadata { +- (instancetype)initWithSnapshot:(QuerySnapshot &&)snapshot { if (self = [super init]) { - _firestore = firestore; - _originalQuery = query; _snapshot = std::move(snapshot); - _metadata = metadata; - _documentChangesIncludeMetadataChanges = NO; } return self; } -- (const ViewSnapshot &)snapshot { - return _snapshot; +- (instancetype)initWithFirestore:(Firestore *)firestore + originalQuery:(FSTQuery *)query + snapshot:(ViewSnapshot &&)snapshot + metadata:(SnapshotMetadata)metadata { + QuerySnapshot wrapped(firestore, query, std::move(snapshot), std::move(metadata)); + return [self initWithSnapshot:std::move(wrapped)]; } // NSObject Methods - (BOOL)isEqual:(nullable id)other { - if (other == self) return YES; - if (![[other class] isEqual:[self class]]) return NO; + if (![other isKindOfClass:[FIRQuerySnapshot class]]) return NO; - return [self isEqualToSnapshot:other]; + FIRQuerySnapshot *otherSnapshot = other; + return _snapshot == otherSnapshot->_snapshot; } -- (BOOL)isEqualToSnapshot:(nullable FIRQuerySnapshot *)snapshot { - if (self == snapshot) return YES; - if (snapshot == nil) return NO; +- (NSUInteger)hash { + return _snapshot.Hash(); +} - return [self.firestore isEqual:snapshot.firestore] && - [self.originalQuery isEqual:snapshot.originalQuery] && _snapshot == snapshot.snapshot && - [self.metadata isEqual:snapshot.metadata]; +- (FIRQuery *)query { + FIRFirestore *firestore = [FIRFirestore recoverFromFirestore:_snapshot.firestore()]; + return [FIRQuery referenceWithQuery:_snapshot.internal_query() firestore:firestore]; } -- (NSUInteger)hash { - NSUInteger hash = [self.firestore hash]; - hash = hash * 31u + [self.originalQuery hash]; - hash = hash * 31u + _snapshot.Hash(); - hash = hash * 31u + [self.metadata hash]; - return hash; +- (FIRSnapshotMetadata *)metadata { + if (!_cached_metadata) { + _cached_metadata = [[FIRSnapshotMetadata alloc] initWithMetadata:_snapshot.metadata()]; + } + return _cached_metadata; } @dynamic empty; -- (FIRQuery *)query { - return [FIRQuery referenceWithQuery:self.originalQuery firestore:self.firestore]; -} - - (BOOL)isEmpty { - return _snapshot.documents().isEmpty; + return _snapshot.empty(); } // This property is exposed as an NSInteger instead of an NSUInteger since (as of Xcode 8.1) // Swift bridges NSUInteger as UInt, and we want to avoid forcing Swift users to cast their ints // where we can. See cr/146959032 for additional context. - (NSInteger)count { - return _snapshot.documents().count; + return static_cast(_snapshot.size()); } - (NSArray *)documents { if (!_documents) { - FSTDocumentSet *documentSet = _snapshot.documents(); - Firestore *firestore = self.firestore.wrapped; - BOOL fromCache = self.metadata.fromCache; - NSMutableArray *result = [NSMutableArray array]; - for (FSTDocument *document in documentSet.documentEnumerator) { - [result addObject:[[FIRQueryDocumentSnapshot alloc] - initWithFirestore:firestore - documentKey:document.key - document:document - fromCache:fromCache - hasPendingWrites:_snapshot.mutated_keys().contains(document.key)]]; - } + _snapshot.ForEachDocument([&result](DocumentSnapshot snapshot) { + [result addObject:[[FIRQueryDocumentSnapshot alloc] initWithSnapshot:std::move(snapshot)]]; + }); _documents = result; } @@ -160,16 +120,16 @@ - (NSInteger)count { - (NSArray *)documentChangesWithIncludeMetadataChanges: (BOOL)includeMetadataChanges { - if (includeMetadataChanges && _snapshot.excludes_metadata_changes()) { + if (includeMetadataChanges && _snapshot.view_snapshot().excludes_metadata_changes()) { FSTThrowInvalidArgument( @"To include metadata changes with your document changes, you must call " @"addSnapshotListener(includeMetadataChanges: true)."); } if (!_documentChanges || _documentChangesIncludeMetadataChanges != includeMetadataChanges) { - _documentChanges = [FIRDocumentChange documentChangesForSnapshot:_snapshot + _documentChanges = [FIRDocumentChange documentChangesForSnapshot:_snapshot.view_snapshot() includeMetadataChanges:includeMetadataChanges - firestore:self.firestore.wrapped]; + firestore:_snapshot.firestore()]; _documentChangesIncludeMetadataChanges = includeMetadataChanges; } return _documentChanges; diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index e0706fbd851..35d593bf318 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -25,6 +25,7 @@ #import "FIRFirestoreSettings.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" @@ -371,14 +372,12 @@ - (void)getDocumentsFromLocalCache:(FIRQuery *)query HARD_ASSERT(viewChange.snapshot.has_value(), "Expected a snapshot"); ViewSnapshot snapshot = std::move(viewChange.snapshot).value(); - FIRSnapshotMetadata *metadata = - [[FIRSnapshotMetadata alloc] initWithPendingWrites:snapshot.has_pending_writes() - fromCache:snapshot.from_cache()]; - - FIRQuerySnapshot *result = [FIRQuerySnapshot snapshotWithFirestore:query.firestore - originalQuery:query.query - snapshot:std::move(snapshot) - metadata:metadata]; + SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache()); + + FIRQuerySnapshot *result = [[FIRQuerySnapshot alloc] initWithFirestore:query.firestore.wrapped + originalQuery:query.query + snapshot:std::move(snapshot) + metadata:std::move(metadata)]; if (completion) { self->_userExecutor->Execute([=] { completion(result, nil); }); diff --git a/Firestore/core/src/firebase/firestore/api/query_snapshot.h b/Firestore/core/src/firebase/firestore/api/query_snapshot.h new file mode 100644 index 00000000000..006e8421a91 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/query_snapshot.h @@ -0,0 +1,113 @@ +/* + * Copyright 2019 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_API_QUERY_SNAPSHOT_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_QUERY_SNAPSHOT_H_ + +#if !defined(__OBJC__) +#error "This header only supports Objective-C++" +#endif // !defined(__OBJC__) + +#import + +#include +#include + +#import "Firestore/Source/Model/FSTDocumentSet.h" + +#include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" +#include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" +#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" + +NS_ASSUME_NONNULL_BEGIN + +@class FSTQuery; + +namespace firebase { +namespace firestore { +namespace api { + +/** + * A `QuerySnapshot` contains zero or more `DocumentSnapshot` objects. + */ +class QuerySnapshot { + public: + QuerySnapshot() = default; + + QuerySnapshot(Firestore* firestore, + FSTQuery* query, + core::ViewSnapshot&& snapshot, + SnapshotMetadata metadata) + : firestore_(firestore), + internal_query_(query), + snapshot_(std::move(snapshot)), + metadata_(std::move(metadata)) { + } + + size_t Hash() const; + + /** + * Indicates whether this `QuerySnapshot` is empty (contains no documents). + */ + bool empty() const { + return static_cast(snapshot_.documents().isEmpty); + } + + /** The count of documents in this `QuerySnapshot`. */ + size_t size() const { + return static_cast(snapshot_.documents().count); + } + + Firestore* firestore() const { + return firestore_; + } + + FSTQuery* internal_query() const { + return internal_query_; + } + + const core::ViewSnapshot& view_snapshot() const { + return snapshot_; + } + + /** + * Metadata about this snapshot, concerning its source and if it has local + * modifications. + */ + const SnapshotMetadata& metadata() const { + return metadata_; + } + + /** Iterates over the `DocumentSnapshots` that make up this query snapshot. */ + void ForEachDocument( + const std::function& callback) const; + + friend bool operator==(const QuerySnapshot& lhs, const QuerySnapshot& rhs); + + private: + Firestore* firestore_ = nullptr; + FSTQuery* internal_query_ = nil; + core::ViewSnapshot snapshot_; + SnapshotMetadata metadata_; +}; + +} // namespace api +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_QUERY_SNAPSHOT_H_ diff --git a/Firestore/core/src/firebase/firestore/api/query_snapshot.mm b/Firestore/core/src/firebase/firestore/api/query_snapshot.mm new file mode 100644 index 00000000000..f3f9302d89f --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/query_snapshot.mm @@ -0,0 +1,70 @@ +/* + * Copyright 2019 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/api/query_snapshot.h" + +#include + +#import "Firestore/Source/API/FIRDocumentChange+Internal.h" +#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/API/FIRQuery+Internal.h" +#import "Firestore/Source/Core/FSTQuery.h" +#import "Firestore/Source/Model/FSTDocument.h" +#import "Firestore/Source/Model/FSTDocumentSet.h" +#import "Firestore/Source/Util/FSTUsageValidation.h" + +#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace api { + +namespace objc = util::objc; +using api::Firestore; +using core::ViewSnapshot; + +bool operator==(const QuerySnapshot& lhs, const QuerySnapshot& rhs) { + return lhs.firestore_ == rhs.firestore_ && + objc::Equals(lhs.internal_query_, rhs.internal_query_) && + lhs.snapshot_ == rhs.snapshot_ && lhs.metadata_ == rhs.metadata_; +} + +size_t QuerySnapshot::Hash() const { + return util::Hash(firestore_, internal_query_, snapshot_, metadata_); +} + +void QuerySnapshot::ForEachDocument( + const std::function& callback) const { + FSTDocumentSet* documentSet = snapshot_.documents(); + bool from_cache = metadata_.from_cache(); + + for (FSTDocument* document in documentSet.documentEnumerator) { + bool has_pending_writes = snapshot_.mutated_keys().contains(document.key); + DocumentSnapshot snap(firestore_, document.key, document, from_cache, + has_pending_writes); + callback(std::move(snap)); + } +} + +} // namespace api +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END From 6259a9897b1e8dcfa8ec5936f555a21235f6f51f Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 20 Mar 2019 08:38:37 -0700 Subject: [PATCH 062/214] Show qualified usage of Timestamp (#2594) --- Firestore/Swift/Tests/API/BasicCompileTests.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Firestore/Swift/Tests/API/BasicCompileTests.swift b/Firestore/Swift/Tests/API/BasicCompileTests.swift index fb117caf981..71cff227dee 100644 --- a/Firestore/Swift/Tests/API/BasicCompileTests.swift +++ b/Firestore/Swift/Tests/API/BasicCompileTests.swift @@ -19,7 +19,7 @@ import Foundation import XCTest -import FirebaseFirestore +import Firebase class BasicCompileTests: XCTestCase { func testCompiled() { @@ -90,7 +90,7 @@ func makeRefs(database db: Firestore) -> (CollectionReference, DocumentReference } func makeQuery(collection collectionRef: CollectionReference) -> Query { - var query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") + let query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") .whereField("age", isGreaterThanOrEqualTo: 24) .whereField("tags", arrayContains: "active") .whereField(FieldPath(["tags"]), arrayContains: "active") @@ -413,7 +413,11 @@ func types() { let _: Firestore let _: FirestoreSettings let _: GeoPoint + let _: Firebase.GeoPoint + let _: FirebaseFirestore.GeoPoint let _: Timestamp + let _: Firebase.Timestamp + let _: FirebaseFirestore.Timestamp let _: ListenerRegistration let _: Query let _: QuerySnapshot From 38496c450c3cb85713d7f5186ca24139ef3a1a9e Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 20 Mar 2019 10:29:02 -0700 Subject: [PATCH 063/214] Add DelayedConstructor (#2579) * Add util::DelayedConstructor * Run sync_project on any Firestore change since start * Use DelayedConstructor in a few places to show how it works --- .../Firestore.xcodeproj/project.pbxproj | 4 + .../Tests/Core/FSTQueryListenerTests.mm | 10 +- Firestore/Source/API/FIRFirestore.mm | 11 +- .../firebase/firestore/util/CMakeLists.txt | 1 + .../firestore/util/delayed_constructor.h | 131 +++++++++++++++ .../firebase/firestore/util/CMakeLists.txt | 1 + .../util/delayed_constructor_test.cc | 149 ++++++++++++++++++ scripts/check.sh | 2 +- 8 files changed, 296 insertions(+), 13 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/util/delayed_constructor.h create mode 100644 Firestore/core/test/firebase/firestore/util/delayed_constructor_test.cc diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 5f649b14c09..0f3be773fd4 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -166,6 +166,7 @@ 6E59498D20F55BA800ECD9A5 /* FuzzingResources in Resources */ = {isa = PBXBuildFile; fileRef = 6ED6DEA120F5502700FC6076 /* FuzzingResources */; }; 6E8302E021022309003E1EA3 /* FSTFuzzTestFieldPath.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6E8302DF21022309003E1EA3 /* FSTFuzzTestFieldPath.mm */; }; 6EA39FDE20FE820E008D461F /* FSTFuzzTestSerializer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EA39FDD20FE820E008D461F /* FSTFuzzTestSerializer.mm */; }; + 6EC28BB8C38E3FD126F68211 /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; 6EDD3B4620BF247500C33877 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6EDD3B4820BF247500C33877 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6EDD3B4920BF247500C33877 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; @@ -560,6 +561,7 @@ B9C261C26C5D311E1E3C0CB9 /* query_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = query_test.cc; sourceTree = ""; }; BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8522DE226C467C54E6788D8 /* mutation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = mutation_test.cc; sourceTree = ""; }; + D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = delayed_constructor_test.cc; sourceTree = ""; }; D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = perf_spec_test.json; sourceTree = ""; }; D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRNumericTransformTests.mm; sourceTree = ""; }; @@ -746,6 +748,7 @@ 548DB928200D59F600E00ABC /* comparison_test.cc */, B67BF448216EB43000CA9097 /* create_noop_connectivity_monitor.cc */, B67BF447216EB42F00CA9097 /* create_noop_connectivity_monitor.h */, + D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */, B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */, B6FB4687208F9B9100554BA2 /* executor_std_test.cc */, B6FB4688208F9B9100554BA2 /* executor_test.cc */, @@ -2058,6 +2061,7 @@ ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */, AB38D93020236E21000A432D /* database_info_test.cc in Sources */, 546854AA20A36867004BDBD5 /* datastore_test.mm in Sources */, + 6EC28BB8C38E3FD126F68211 /* delayed_constructor_test.cc in Sources */, 544129DD21C2DDC800EFB9CC /* document.pb.cc in Sources */, B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */, AB6B908420322E4D00CC290A /* document_test.cc in Sources */, diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index 064ef9caee0..91fbecb03f3 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -33,10 +33,10 @@ #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/remote/remote_event.h" +#include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h" #include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" #include "Firestore/core/src/firebase/firestore/util/status.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" -#include "absl/memory/memory.h" using firebase::firestore::FirestoreErrorCode; using firebase::firestore::core::DocumentViewChange; @@ -45,6 +45,7 @@ using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::OnlineState; using firebase::firestore::remote::TargetChange; +using firebase::firestore::util::DelayedConstructor; using firebase::firestore::util::ExecutorLibdispatch; using firebase::firestore::util::Status; using firebase::firestore::util::StatusOr; @@ -55,15 +56,12 @@ @interface FSTQueryListenerTests : XCTestCase @end @implementation FSTQueryListenerTests { - std::unique_ptr _executor; + DelayedConstructor _executor; FSTListenOptions *_includeMetadataChanges; } - (void)setUp { - // TODO(varconst): moving this test to C++, it should be possible to store Executor as a value, - // not a pointer, and initialize it in the constructor. - _executor = absl::make_unique( - dispatch_queue_create("FSTQueryListenerTests Queue", DISPATCH_QUEUE_SERIAL)); + _executor.Init(dispatch_queue_create("FSTQueryListenerTests Queue", DISPATCH_QUEUE_SERIAL)); _includeMetadataChanges = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES includeDocumentMetadataChanges:YES waitForSyncWhenOnline:NO]; diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index cffe3ea36e5..e4761b1f7ba 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -39,6 +39,7 @@ #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/util/async_queue.h" +#include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h" #include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" @@ -48,6 +49,7 @@ using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::model::DatabaseId; using firebase::firestore::util::AsyncQueue; +using firebase::firestore::util::DelayedConstructor; NS_ASSUME_NONNULL_BEGIN @@ -62,9 +64,7 @@ @interface FIRFirestore () @end @implementation FIRFirestore { - // `std::mutex` member variable makes `core::Firestore` unmovable. - - std::unique_ptr _firestore; + DelayedConstructor _firestore; } + (NSMutableDictionary *)instances { @@ -144,9 +144,8 @@ - (instancetype)initWithProjectID:(std::string)projectID workerQueue:(std::unique_ptr)workerQueue firebaseApp:(FIRApp *)app { if (self = [super init]) { - _firestore = absl::make_unique( - std::move(projectID), std::move(database), std::move(persistenceKey), - std::move(credentialsProvider), std::move(workerQueue), (__bridge void *)self); + _firestore.Init(std::move(projectID), std::move(database), std::move(persistenceKey), + std::move(credentialsProvider), std::move(workerQueue), (__bridge void *)self); _app = app; diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index 63c696ccf50..28b29ec6aa9 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -245,6 +245,7 @@ cc_library( comparison.cc comparison.h config.h + delayed_constructor.h hashing.h iterator_adaptors.h objc_compatibility.h diff --git a/Firestore/core/src/firebase/firestore/util/delayed_constructor.h b/Firestore/core/src/firebase/firestore/util/delayed_constructor.h new file mode 100644 index 00000000000..9c00e515158 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/delayed_constructor.h @@ -0,0 +1,131 @@ +/* + * Copyright 2019 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_UTIL_DELAYED_CONSTRUCTOR_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_DELAYED_CONSTRUCTOR_H_ + +#include +#include + +namespace firebase { +namespace firestore { +namespace util { + +// DelayedConstructor is a wrapper around an object of type T that +// +// * stores the object of type T inline inside DelayedConstructor; +// * initially does not call T's constructor, leaving storage uninitialized; +// * calls the constructor when you call Init(); +// * provides access to the object of type T like a pointer via ->, *, and +// get(); and +// * calls T's destructor as usual. +// +// This is useful for embedding objects of type T inside Objective-C objects +// when T has no default constructor. +// +// Objective-C separates allocation from initialization which is different from +// the way C++ does it. A C++ object embedded in an Objective-C object is +// normally default constructed then assigned a value later. This doesn't work +// for classes that have no default constructor. +// +// DelayedConstructor does not count or otherwise check that Init is only +// called once. For best results call Init() from the Objective-C class's +// designated initializer. +// +// Note that DelayedConstructor makes no guarantees about the state of the +// storage backing it before Init() is called. However, Objective-C objects are +// zero filled during allocation, so as a member of an Objective-C object, the +// default state will be zero-filled. +// +// Normally this doesn't matter, but DelayedConstructor unconditionally invokes +// T's destructor, even if you don't call Init(). This may cause problems in +// Objective-C classes where the initializer is designed to return an instance +// other than self. It's best to avoid such instance switching techniques in +// combination with DelayedConstructor, but it is possible: either ensure that +// T's destructor handles the zero-filled case correctly, or call Init() before +// switching instances. +template +class DelayedConstructor { + public: + typedef T element_type; + + /** + * Default constructor does nothing. + */ + DelayedConstructor() { + } + + /** + * Forwards arguments to the T's constructor: calls T(args...). + * + * This overload is disabled when it might collide with copy/move. + */ + template ::type...), + void(DelayedConstructor)>::value, + int>::type = 0> + void Init(Ts&&... args) { + new (&space_) T(std::forward(args)...); + } + + /** + * Forwards copy and move construction for T. + */ + void Init(const T& x) { + new (&space_) T(x); + } + void Init(T&& x) { + new (&space_) T(std::move(x)); + } + + // No copying. + DelayedConstructor(const DelayedConstructor&) = delete; + DelayedConstructor& operator=(const DelayedConstructor&) = delete; + + ~DelayedConstructor() { + get()->~T(); + } + + // Pretend to be a smart pointer to T. + T& operator*() { + return *get(); + } + T* operator->() { + return get(); + } + T* get() { + return reinterpret_cast(&space_); + } + const T& operator*() const { + return *get(); + } + const T* operator->() const { + return get(); + } + const T* get() const { + return reinterpret_cast(&space_); + } + + private: + typename std::aligned_storage::type space_; +}; + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_DELAYED_CONSTRUCTOR_H_ diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt index 92698a74938..860578f4895 100644 --- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt @@ -150,6 +150,7 @@ cc_test( autoid_test.cc bits_test.cc comparison_test.cc + delayed_constructor_test.cc hashing_test.cc iterator_adaptors_test.cc ordered_code_test.cc diff --git a/Firestore/core/test/firebase/firestore/util/delayed_constructor_test.cc b/Firestore/core/test/firebase/firestore/util/delayed_constructor_test.cc new file mode 100644 index 00000000000..56d0ee09442 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/delayed_constructor_test.cc @@ -0,0 +1,149 @@ +/* + * 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. + */ + +#include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h" + +#include + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace util { + +TEST(DelayedConstructorTest, NoDefaultConstructor) { + static int constructed = 0; + + struct NoDefault { + NoDefault() = delete; + NoDefault(const NoDefault&) = delete; + + explicit NoDefault(int) { + constructed += 1; + } + }; + + DelayedConstructor value; + EXPECT_EQ(0, constructed); + + value.Init(0); + EXPECT_EQ(1, constructed); +} + +TEST(DelayedConstructorTest, NonCopyableType) { + static int constructed = 0; + + struct NonCopyable { + NonCopyable() { + constructed += 1; + } + NonCopyable(const NonCopyable&) = delete; + }; + + DelayedConstructor value; + EXPECT_EQ(0, constructed); + + value.Init(); + EXPECT_EQ(1, constructed); +} + +TEST(DelayedConstructorTest, CopyableType) { + static int constructed = 0; + + struct Copyable { + Copyable() = delete; + Copyable(const Copyable&) { + constructed += 1; + } + + // Backdoor to construct a value without exposing a default constructor + explicit Copyable(int) { + } + }; + + DelayedConstructor value; + EXPECT_EQ(0, constructed); + + value.Init(Copyable(0)); + EXPECT_EQ(1, constructed); +} + +TEST(DelayedConstructorTest, MoveOnlyType) { + static int constructed = 0; + + struct MoveOnly { + MoveOnly() = delete; + MoveOnly(MoveOnly&&) { + constructed += 1; + } + + // Backdoor to construct a value without exposing a default constructor + explicit MoveOnly(int) { + } + }; + + DelayedConstructor value; + EXPECT_EQ(0, constructed); + + value.Init(MoveOnly(0)); + EXPECT_EQ(1, constructed); +} + +TEST(DelayedConstructorTest, CallsDestructor) { + static int constructed = 0; + static int destructed = 0; + + struct Counter { + Counter() { + constructed += 1; + } + + ~Counter() { + destructed += 1; + } + }; + + { + DelayedConstructor value; + EXPECT_EQ(0, constructed); + EXPECT_EQ(0, destructed); + + value.Init(); + EXPECT_EQ(1, constructed); + EXPECT_EQ(0, destructed); + } + + EXPECT_EQ(1, constructed); + EXPECT_EQ(1, destructed); +} + +TEST(DelayedConstructorTest, SingleConstructorArg) { + DelayedConstructor str; + str.Init("foo"); + + EXPECT_EQ(*str, std::string("foo")); +} + +TEST(DelayedConstructorTest, MultipleConstructorArgs) { + DelayedConstructor str; + str.Init(3, 'a'); + + EXPECT_EQ(*str, std::string("aaa")); +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/scripts/check.sh b/scripts/check.sh index 04ac2384aac..ad5c18ab0eb 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -203,7 +203,7 @@ fi # If there are changes to the Firestore project, ensure they're ordered # correctly to minimize conflicts. -if ! git diff --quiet -- Firestore/Example/Firestore.xcodeproj; then +if ! git diff --quiet "${START_SHA}" -- Firestore; then "${top_dir}/scripts/sync_project.rb" if ! git diff --quiet; then maybe_commit "sync_project.rb generated changes" From 811251d71009a0fa432fad32b31a671232e6a7af Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 20 Mar 2019 13:48:51 -0700 Subject: [PATCH 064/214] Exclude sub-packages of abseil that are problematic (#2597) --- FirebaseFirestore.podspec | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index fc32ab23caa..644ec427ace 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -95,10 +95,19 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, 'Firestore/third_party/abseil-cpp/**/*.cc' ] ss.exclude_files = [ + # Exclude tests and benchmarks from the framework. 'Firestore/third_party/abseil-cpp/**/*_benchmark.cc', 'Firestore/third_party/abseil-cpp/**/*test*.cc', 'Firestore/third_party/abseil-cpp/absl/hash/internal/print_hash_of.cc', - 'Firestore/third_party/abseil-cpp/absl/synchronization/internal/mutex_nonprod.cc', + + # Avoid the debugging package which uses code that isn't portable to + # ARM (see stack_consumption.cc) and uses syscalls not available on + # tvOS (e.g. sigaltstack). + 'Firestore/third_party/abseil-cpp/absl/debugging/**/*.cc', + + # Exclude the synchronization package because it's dead weight: we don't + # write the kind of heavily threaded code that might benefit from it. + 'Firestore/third_party/abseil-cpp/absl/synchronization/**/*.cc', ] ss.library = 'c++' From dd0ae1de4ccc17c9c7cd441f14816c851d1b4257 Mon Sep 17 00:00:00 2001 From: christibbs <43829046+christibbs@users.noreply.github.com> Date: Wed, 20 Mar 2019 14:12:40 -0700 Subject: [PATCH 065/214] Temporarily make FIAM tests non-required (#2596) * Update .travis.yml Temporarily make FIAM tests non required due to flake * Remove whitespace * Organization * Spaces * whitespace * Follow allow_failures spec --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5ad2c59dc30..b0ab6b686f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -216,6 +216,8 @@ jobs: - PROJECT=Firestore PLATFORM=iOS METHOD=xcodebuild SANITIZERS=asan - env: - PROJECT=Firestore PLATFORM=iOS METHOD=xcodebuild SANITIZERS=tsan + - env: + - PROJECT=InAppMessaging PLATFORM=iOS METHOD=xcodebuild # TODO(varconst): enable if it's possible to make this flag work on build # stages. It's supposed to avoid waiting for jobs that are allowed to fail From 95cdd5d6031c420cf4f2935939d67ac53a11aa8d Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 20 Mar 2019 14:54:27 -0700 Subject: [PATCH 066/214] Update tvOS and macOS README (#2598) --- README.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 903fc9fc76b..ad8de0fa481 100644 --- a/README.md +++ b/README.md @@ -169,10 +169,9 @@ very grateful! We'd like to empower as many developers as we can to be able to participate in the Firebase community. ### macOS and tvOS -FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseFunctions and FirebaseStorage now compile, run -unit tests, and work on macOS and tvOS, thanks to contributions from the community. There are a few -tweaks needed, like ensuring iOS-only, macOS-only, or tvOS-only code is correctly guarded with checks -for `TARGET_OS_IOS`, `TARGET_OS_OSX` and `TARGET_OS_TV`. +Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, +FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on macOS and tvOS. +FirebaseFirestore is availiable for macOS and FirebaseMessaging for tvOS. For tvOS, checkout the [Sample](Example/tvOSSample). @@ -181,11 +180,19 @@ actively developed primarily for iOS. While we can catch basic unit test issues may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). -For installation instructions, see [above](README.md#accessing-firebase-source-snapshots). +Note that the Firebase pod is not available for macOS and tvOS. -Note that the Firebase pod is not available for macOS and tvOS. Install a selection of the -`FirebaseAuth`, `FirebaseCore`, `FirebaseDatabase`, `FirebaseFunctions`, and `FirebaseStorage` -CocoaPods. +To install, add a subset of the following to the Podfile: + +``` +pod 'FirebaseAuth' +pod 'FirebaseCore' +pod 'FirebaseDatabase' +pod 'FirebaseFirestore' # Only iOS and macOS +pod 'FirebaseFunctions' +pod 'FirebaseMessaging' # Only iOS and tvOS +pod 'FirebaseStorage' +``` ## Roadmap From 438d49a0e025a4999f7a020be4c187e7cbfa3484 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 21 Mar 2019 06:29:19 -0700 Subject: [PATCH 067/214] Update Carthage instructions for FirebaseMLNLSmartReplyBinary (#2599) --- Carthage.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Carthage.md b/Carthage.md index 1ee4f8da2e7..4a6c33bda3a 100644 --- a/Carthage.md +++ b/Carthage.md @@ -44,6 +44,7 @@ binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseInvitesBinary.jso binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMessagingBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMLModelInterpreterBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMLNLLanguageIDBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMLNLSmartReplyBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMLNaturalLanguageBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMLVisionBarcodeModelBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMLVisionBinary.json" From ed2d74cfb6a11475dd25811a27537aac9072f2d1 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Thu, 21 Mar 2019 09:46:27 -0700 Subject: [PATCH 068/214] b/62961699: Use :nodoc: to prevent methods from showing up in reference docs. (#2600) --- Firestore/Source/Public/FIRCollectionReference.h | 2 +- Firestore/Source/Public/FIRDocumentChange.h | 2 +- Firestore/Source/Public/FIRDocumentReference.h | 2 +- Firestore/Source/Public/FIRDocumentSnapshot.h | 4 ++-- Firestore/Source/Public/FIRFieldValue.h | 2 +- Firestore/Source/Public/FIRFirestore.h | 2 +- Firestore/Source/Public/FIRGeoPoint.h | 2 +- Firestore/Source/Public/FIRQuery.h | 2 +- Firestore/Source/Public/FIRQuerySnapshot.h | 2 +- Firestore/Source/Public/FIRSnapshotMetadata.h | 2 +- Firestore/Source/Public/FIRTimestamp.h | 2 +- Firestore/Source/Public/FIRTransaction.h | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Firestore/Source/Public/FIRCollectionReference.h b/Firestore/Source/Public/FIRCollectionReference.h index bc9a56af932..39be000578e 100644 --- a/Firestore/Source/Public/FIRCollectionReference.h +++ b/Firestore/Source/Public/FIRCollectionReference.h @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(CollectionReference) @interface FIRCollectionReference : FIRQuery -/** */ +/** :nodoc: */ - (id)init __attribute__((unavailable("FIRCollectionReference cannot be created directly."))); /** ID of the referenced collection. */ diff --git a/Firestore/Source/Public/FIRDocumentChange.h b/Firestore/Source/Public/FIRDocumentChange.h index 47170673068..3e4a0120d28 100644 --- a/Firestore/Source/Public/FIRDocumentChange.h +++ b/Firestore/Source/Public/FIRDocumentChange.h @@ -40,7 +40,7 @@ typedef NS_ENUM(NSInteger, FIRDocumentChangeType) { NS_SWIFT_NAME(DocumentChange) @interface FIRDocumentChange : NSObject -/** */ +/** :nodoc: */ - (id)init __attribute__((unavailable("FIRDocumentChange cannot be created directly."))); /** The type of change that occurred (added, modified, or removed). */ diff --git a/Firestore/Source/Public/FIRDocumentReference.h b/Firestore/Source/Public/FIRDocumentReference.h index 216a8dc93f4..c110bcd10e7 100644 --- a/Firestore/Source/Public/FIRDocumentReference.h +++ b/Firestore/Source/Public/FIRDocumentReference.h @@ -37,7 +37,7 @@ typedef void (^FIRDocumentSnapshotBlock)(FIRDocumentSnapshot *_Nullable snapshot NS_SWIFT_NAME(DocumentReference) @interface FIRDocumentReference : NSObject -/** */ +/** :nodoc: */ - (instancetype)init __attribute__((unavailable("FIRDocumentReference cannot be created directly."))); diff --git a/Firestore/Source/Public/FIRDocumentSnapshot.h b/Firestore/Source/Public/FIRDocumentSnapshot.h index 669fe07dca6..9a3f61b19a2 100644 --- a/Firestore/Source/Public/FIRDocumentSnapshot.h +++ b/Firestore/Source/Public/FIRDocumentSnapshot.h @@ -58,7 +58,7 @@ typedef NS_ENUM(NSInteger, FIRServerTimestampBehavior) { NS_SWIFT_NAME(DocumentSnapshot) @interface FIRDocumentSnapshot : NSObject -/** */ +/** :nodoc: */ - (instancetype)init __attribute__((unavailable("FIRDocumentSnapshot cannot be created directly."))); @@ -151,7 +151,7 @@ NS_SWIFT_NAME(DocumentSnapshot) NS_SWIFT_NAME(QueryDocumentSnapshot) @interface FIRQueryDocumentSnapshot : FIRDocumentSnapshot -/** */ +/** :nodoc: */ - (instancetype)init __attribute__((unavailable("FIRQueryDocumentSnapshot cannot be created directly."))); diff --git a/Firestore/Source/Public/FIRFieldValue.h b/Firestore/Source/Public/FIRFieldValue.h index 7973157515b..24d1007a5e0 100644 --- a/Firestore/Source/Public/FIRFieldValue.h +++ b/Firestore/Source/Public/FIRFieldValue.h @@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(FieldValue) @interface FIRFieldValue : NSObject -/** */ +/** :nodoc: */ - (instancetype)init NS_UNAVAILABLE; /** Used with updateData() to mark a field for deletion. */ diff --git a/Firestore/Source/Public/FIRFirestore.h b/Firestore/Source/Public/FIRFirestore.h index 968a7511c2a..cd9302c4cd3 100644 --- a/Firestore/Source/Public/FIRFirestore.h +++ b/Firestore/Source/Public/FIRFirestore.h @@ -34,7 +34,7 @@ NS_SWIFT_NAME(Firestore) @interface FIRFirestore : NSObject #pragma mark - Initializing -/** */ +/** :nodoc: */ - (instancetype)init __attribute__((unavailable("Use a static constructor method."))); /** diff --git a/Firestore/Source/Public/FIRGeoPoint.h b/Firestore/Source/Public/FIRGeoPoint.h index ee7a7ea3d05..290b2b45ec2 100644 --- a/Firestore/Source/Public/FIRGeoPoint.h +++ b/Firestore/Source/Public/FIRGeoPoint.h @@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(GeoPoint) @interface FIRGeoPoint : NSObject -/** */ +/** :nodoc: */ - (instancetype)init NS_UNAVAILABLE; /** diff --git a/Firestore/Source/Public/FIRQuery.h b/Firestore/Source/Public/FIRQuery.h index 2b02a3c4270..436c185b4a2 100644 --- a/Firestore/Source/Public/FIRQuery.h +++ b/Firestore/Source/Public/FIRQuery.h @@ -35,7 +35,7 @@ typedef void (^FIRQuerySnapshotBlock)(FIRQuerySnapshot *_Nullable snapshot, */ NS_SWIFT_NAME(Query) @interface FIRQuery : NSObject -/** */ +/** :nodoc: */ - (id)init __attribute__((unavailable("FIRQuery cannot be created directly."))); /** The `FIRFirestore` for the Firestore database (useful for performing transactions, etc.). */ diff --git a/Firestore/Source/Public/FIRQuerySnapshot.h b/Firestore/Source/Public/FIRQuerySnapshot.h index 6a7e60ddcef..268598fc0b1 100644 --- a/Firestore/Source/Public/FIRQuerySnapshot.h +++ b/Firestore/Source/Public/FIRQuerySnapshot.h @@ -31,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(QuerySnapshot) @interface FIRQuerySnapshot : NSObject -/** */ +/** :nodoc: */ - (id)init __attribute__((unavailable("FIRQuerySnapshot cannot be created directly."))); /** diff --git a/Firestore/Source/Public/FIRSnapshotMetadata.h b/Firestore/Source/Public/FIRSnapshotMetadata.h index 8e4e6a3c647..043d8198acb 100644 --- a/Firestore/Source/Public/FIRSnapshotMetadata.h +++ b/Firestore/Source/Public/FIRSnapshotMetadata.h @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(SnapshotMetadata) @interface FIRSnapshotMetadata : NSObject -/** */ +/** :nodoc: */ - (instancetype)init NS_UNAVAILABLE; /** diff --git a/Firestore/Source/Public/FIRTimestamp.h b/Firestore/Source/Public/FIRTimestamp.h index bf4aff47e05..cea316b9887 100644 --- a/Firestore/Source/Public/FIRTimestamp.h +++ b/Firestore/Source/Public/FIRTimestamp.h @@ -33,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(Timestamp) @interface FIRTimestamp : NSObject -/** */ +/** :nodoc: */ - (instancetype)init NS_UNAVAILABLE; /** diff --git a/Firestore/Source/Public/FIRTransaction.h b/Firestore/Source/Public/FIRTransaction.h index e53414d37af..ede0fb9d422 100644 --- a/Firestore/Source/Public/FIRTransaction.h +++ b/Firestore/Source/Public/FIRTransaction.h @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(Transaction) @interface FIRTransaction : NSObject -/** */ +/** :nodoc: */ - (id)init __attribute__((unavailable("FIRTransaction cannot be created directly."))); /** From 15786267530a9fe3c7c891285c9992ebd4f91759 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 21 Mar 2019 10:22:05 -0700 Subject: [PATCH 069/214] Remove stale roadmap items (#2605) --- ROADMAP.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 1b6601a4bc8..07acdf00bf7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,21 +4,6 @@ The Firebase team plans to open source more of Firebase components. -## Continuous Integration - -* [Stabilize Travis](https://github.com/firebase/firebase-ios-sdk/issues/102) -* [Verify Objective-C style guide compliance](https://github.com/firebase/firebase-ios-sdk/issues/103) - -## Samples and Integration Tests - -Add more samples to better demonstrate the capabilities of Firebase and help -developers onboard. - -## Xcode 9 Workflow - -[Ensure Firebase open source development works well with Xcode 9's git and -GitHub features](https://github.com/firebase/firebase-ios-sdk/issues/101). - ## Other Check out the [issue list](https://github.com/firebase/firebase-ios-sdk/issues) From 1e08a55afd7295cd851ffac95a40d2b5ab55e306 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Thu, 21 Mar 2019 13:33:14 -0400 Subject: [PATCH 070/214] GULLogger - count errors and warnigns (#2601) --- .../Example/Tests/Logger/GULLoggerTest.m | 81 +++++++++++++++++++ GoogleUtilities/Logger/GULLogger.m | 73 +++++++++++++++++ GoogleUtilities/Logger/Private/GULLogger.h | 25 ++++++ 3 files changed, 179 insertions(+) diff --git a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m index 9483bbcb5f9..a9e0486cf94 100644 --- a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m +++ b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m @@ -32,6 +32,8 @@ extern BOOL getGULLoggerDebugMode(void); +extern NSUserDefaults *getGULLoggerUsetDefaults(void); + static NSString *const kMessageCode = @"I-COR000001"; @interface GULLoggerTest : XCTestCase @@ -153,6 +155,85 @@ - (void)testGULLoggerLevelValues { XCTAssertEqual(GULLoggerLevelDebug, ASL_LEVEL_DEBUG); } +- (void)testGetErrorWarningNumberBeforeLogDontCrash { + GULResetLogger(); + + XCTestExpectation *getErrorCountExpectation = + [self expectationWithDescription:@"getErrorCountExpectation"]; + XCTestExpectation *getWarningsCountExpectation = + [self expectationWithDescription:@"getWarningsCountExpectation"]; + + GULNumberOfErrorsLogged(^(NSInteger count) { + [getErrorCountExpectation fulfill]; + }); + + GULNumberOfWarningsLogged(^(NSInteger count) { + [getWarningsCountExpectation fulfill]; + }); + + [self waitForExpectations:@[ getErrorCountExpectation, getWarningsCountExpectation ] + timeout:0.5 + enforceOrder:YES]; +} + +- (void)testErrorNumberIncrement { + [getGULLoggerUsetDefaults() setInteger:10 forKey:kGULLoggerErrorCountKey]; + + GULLogError(@"my service", NO, kMessageCode, @"Message."); + + XCTestExpectation *getErrorCountExpectation = + [self expectationWithDescription:@"getErrorCountExpectation"]; + + GULNumberOfErrorsLogged(^(NSInteger count) { + [getErrorCountExpectation fulfill]; + XCTAssertEqual(count, 11); + }); + + [self waitForExpectationsWithTimeout:0.5 handler:NULL]; +} + +- (void)testWarningNumberIncrement { + [getGULLoggerUsetDefaults() setInteger:5 forKey:kGULLoggerWarningCountKey]; + + GULLogWarning(@"my service", NO, kMessageCode, @"Message."); + + XCTestExpectation *getWarningsCountExpectation = + [self expectationWithDescription:@"getWarningsCountExpectation"]; + + GULNumberOfWarningsLogged(^(NSInteger count) { + [getWarningsCountExpectation fulfill]; + XCTAssertEqual(count, 6); + }); + + [self waitForExpectationsWithTimeout:0.5 handler:NULL]; +} + +- (void)testResetIssuesCount { + [getGULLoggerUsetDefaults() setInteger:3 forKey:kGULLoggerErrorCountKey]; + [getGULLoggerUsetDefaults() setInteger:4 forKey:kGULLoggerWarningCountKey]; + + GULResetNumberOfIssuesLogged(); + + XCTestExpectation *getErrorCountExpectation = + [self expectationWithDescription:@"getErrorCountExpectation"]; + XCTestExpectation *getWarningsCountExpectation = + [self expectationWithDescription:@"getWarningsCountExpectation"]; + + GULNumberOfErrorsLogged(^(NSInteger count) { + [getErrorCountExpectation fulfill]; + XCTAssertEqual(count, 0); + }); + + GULNumberOfWarningsLogged(^(NSInteger count) { + [getWarningsCountExpectation fulfill]; + XCTAssertEqual(count, 0); + }); + + [self waitForExpectations:@[ getErrorCountExpectation, getWarningsCountExpectation ] + timeout:0.5 + enforceOrder:YES]; +} + // Helper functions. - (BOOL)logExists { [self drainGULClientQueue]; diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index 495e5830bb0..4e8feafce8f 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -19,6 +19,9 @@ #import #import "Public/GULLoggerLevel.h" +NSString *const kGULLoggerErrorCountKey = @"kGULLoggerErrorCountKey"; +NSString *const kGULLoggerWarningCountKey = @"kGULLoggerWarningCountKey"; + /// ASL client facility name used by GULLogger. const char *kGULLoggerASLClientFacilityName = "com.google.utilities.logger"; @@ -43,6 +46,8 @@ static NSRegularExpression *sMessageCodeRegex; #endif +void GULIncrementLogCountForLevel(GULLoggerLevel level); + void GULLoggerInitializeASL(void) { dispatch_once(&sGULLoggerOnceToken, ^{ NSInteger majorOSVersion = [[GULAppEnvironmentUtil systemVersion] integerValue]; @@ -149,6 +154,10 @@ void GULLogBasic(GULLoggerLevel level, NSString *message, va_list args_ptr) { GULLoggerInitializeASL(); + + // Keep count of how many errors and warnings are triggered. + GULIncrementLogCountForLevel(level); + if (!(level <= sGULLoggerMaximumLevel || sGULLoggerDebugMode || forceLog)) { return; } @@ -194,6 +203,70 @@ void GULLogBasic(GULLoggerLevel level, #undef GUL_MAKE_LOGGER +#pragma mark - Number of errors and warnings + +NSUserDefaults *getGULLoggerUsetDefaults(void) { + static NSUserDefaults *_userDefaults = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"GoogleUtilities.Logger.GULLogger"]; + }); + + return _userDefaults; +} + +dispatch_queue_t getGULLoggerCounterQueue(void) { + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("GoogleUtilities.GULLogger.counterQueue", DISPATCH_QUEUE_SERIAL); + dispatch_set_target_queue(queue, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); + }); + + return queue; +} + +void GULAsyncGetUserDefaultsIntegerForKey(NSString *key, void (^completion)(NSInteger)) { + dispatch_async(getGULLoggerCounterQueue(), ^{ + NSInteger count = [getGULLoggerUsetDefaults() integerForKey:key]; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(count); + }); + }); +} + +void GULNumberOfErrorsLogged(void (^completion)(NSInteger)) { + GULAsyncGetUserDefaultsIntegerForKey(kGULLoggerErrorCountKey, completion); +} + +void GULNumberOfWarningsLogged(void (^completion)(NSInteger)) { + GULAsyncGetUserDefaultsIntegerForKey(kGULLoggerWarningCountKey, completion); +} + +void GULResetNumberOfIssuesLogged(void) { + dispatch_async(getGULLoggerCounterQueue(), ^{ + [getGULLoggerUsetDefaults() setInteger:0 forKey:kGULLoggerErrorCountKey]; + [getGULLoggerUsetDefaults() setInteger:0 forKey:kGULLoggerWarningCountKey]; + }); +} + +void GULIncrementUserDefaultsIntegerForKey(NSString *key) { + NSUserDefaults *defaults = getGULLoggerUsetDefaults(); + NSInteger errorCount = [defaults integerForKey:key]; + [defaults setInteger:errorCount + 1 forKey:key]; +} + +void GULIncrementLogCountForLevel(GULLoggerLevel level) { + dispatch_async(getGULLoggerCounterQueue(), ^{ + if (level == GULLoggerLevelError) { + GULIncrementUserDefaultsIntegerForKey(kGULLoggerErrorCountKey); + } else if (level == GULLoggerLevelWarning) { + GULIncrementUserDefaultsIntegerForKey(kGULLoggerWarningCountKey); + } + }); +} + #pragma mark - GULLoggerWrapper @implementation GULLoggerWrapper diff --git a/GoogleUtilities/Logger/Private/GULLogger.h b/GoogleUtilities/Logger/Private/GULLogger.h index ff425768681..453de4bd955 100644 --- a/GoogleUtilities/Logger/Private/GULLogger.h +++ b/GoogleUtilities/Logger/Private/GULLogger.h @@ -25,6 +25,16 @@ NS_ASSUME_NONNULL_BEGIN */ typedef NSString *const GULLoggerService; +/** + * The key used to store the logger's error count. + */ +extern NSString *const kGULLoggerErrorCountKey; + +/** + * The key used to store the logger's warning count. + */ +extern NSString *const kGULLoggerWarningCountKey; + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -129,6 +139,21 @@ extern void GULLogDebug(GULLoggerService service, NSString *message, ...) NS_FORMAT_FUNCTION(4, 5); +/** + * Retrieve the number of errors that have been logged since the stat was last reset. + */ +extern void GULNumberOfErrorsLogged(void (^completion)(NSInteger)); + +/** + * Retrieve the number of warnings that have been logged since the stat was last reset. + */ +extern void GULNumberOfWarningsLogged(void (^completion)(NSInteger)); + +/** + * Reset number of errors and warnings that have been logged to 0. + */ +extern void GULResetNumberOfIssuesLogged(void); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus From 38815b03f646db79ce648a8ff6ae051a5db6257f Mon Sep 17 00:00:00 2001 From: Gil Date: Thu, 21 Mar 2019 14:48:55 -0700 Subject: [PATCH 071/214] Add xcgmock (#2595) * Add operator<< to a few types * Add xcgmock * Use gmock matchers in XCTest * Add gmock to integration test header search paths --- .../Firestore.xcodeproj/project.pbxproj | 22 +- .../Tests/Core/FSTQueryListenerTests.mm | 97 ++++---- Firestore/Example/Tests/Core/FSTViewTests.mm | 43 ++-- .../include/firebase/firestore/timestamp.h | 3 + .../firebase/firestore/core/view_snapshot.h | 2 + .../firebase/firestore/core/view_snapshot.mm | 6 + .../core/src/firebase/firestore/timestamp.cc | 9 +- .../src/firebase/firestore/util/status.cc | 5 + .../core/src/firebase/firestore/util/status.h | 1 + .../firestore/testutil/CMakeLists.txt | 1 + .../firebase/firestore/testutil/xcgmock.h | 213 ++++++++++++++++++ .../firestore/testutil/xcgmock_test.mm | 64 ++++++ 12 files changed, 385 insertions(+), 81 deletions(-) create mode 100644 Firestore/core/test/firebase/firestore/testutil/xcgmock.h create mode 100644 Firestore/core/test/firebase/firestore/testutil/xcgmock_test.mm diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 0f3be773fd4..b004d7fed79 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 36FD4CE79613D18BC783C55B /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; 3B843E4C1F3A182900548890 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; }; 4AA4ABE36065DB79CD76DD8D /* Pods_Firestore_Benchmarks_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F694C3CE4B77B3C0FA4BBA53 /* Pods_Firestore_Benchmarks_iOS.framework */; }; + 4D1F46B2DD91198C8867C04C /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; 54131E9720ADE679001DF3FF /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; 544129DA21C2DDC800EFB9CC /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; 544129DB21C2DDC800EFB9CC /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; @@ -182,6 +183,7 @@ 84DBE646DCB49305879D3500 /* nanopb_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 8C82D4D3F9AB63E79CC52DC8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */; }; + 9794E074439ABE5457E60F35 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; AB380CFE201A2F4500D97691 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; @@ -326,6 +328,7 @@ 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = ""; }; 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.release.xcconfig"; sourceTree = ""; }; 403DBF6EFB541DFD01582AA3 /* path_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = path_test.cc; sourceTree = ""; }; + 4425A513895DEC60325A139E /* xcgmock_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = xcgmock_test.mm; sourceTree = ""; }; 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = hard_assert_test.cc; sourceTree = ""; }; 54131E9620ADE678001DF3FF /* string_format_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_format_test.cc; sourceTree = ""; }; 544129D021C2DDC800EFB9CC /* query.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = query.pb.h; sourceTree = ""; }; @@ -559,6 +562,7 @@ B6FB468A208F9B9100554BA2 /* executor_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = executor_test.h; sourceTree = ""; }; B79CA87A1A01FC5329031C9B /* Pods_Firestore_FuzzTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_FuzzTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B9C261C26C5D311E1E3C0CB9 /* query_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = query_test.cc; sourceTree = ""; }; + BA6E5B9D53CCF301F58A62D7 /* xcgmock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = xcgmock.h; sourceTree = ""; }; BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8522DE226C467C54E6788D8 /* mutation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = mutation_test.cc; sourceTree = ""; }; D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = delayed_constructor_test.cc; sourceTree = ""; }; @@ -715,6 +719,8 @@ 5467FB07203E6A44009C9584 /* app_testing.mm */, 54A0352820A3B3BD003E0143 /* testutil.cc */, 54A0352920A3B3BD003E0143 /* testutil.h */, + BA6E5B9D53CCF301F58A62D7 /* xcgmock.h */, + 4425A513895DEC60325A139E /* xcgmock_test.mm */, ); path = testutil; sourceTree = ""; @@ -2138,6 +2144,7 @@ ABC1D7DE2023A05300BA84F0 /* user_test.cc in Sources */, B68FC0E521F6848700A7055C /* watch_change_test.mm in Sources */, 544129DE21C2DDC800EFB9CC /* write.pb.cc in Sources */, + 9794E074439ABE5457E60F35 /* xcgmock_test.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2185,6 +2192,7 @@ 5492E0442021457E00B64F25 /* XCTestCase+Await.mm in Sources */, EBFC611B1BF195D0EC710AF4 /* app_testing.mm in Sources */, CA989C0E6020C372A62B7062 /* testutil.cc in Sources */, + 4D1F46B2DD91198C8867C04C /* xcgmock_test.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2670,12 +2678,12 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"", "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", "\"${PODS_ROOT}/GoogleTest/googletest/include\"", "\"${PODS_ROOT}/leveldb-library/include\"", - "\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"", - "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", "\"${PODS_ROOT}/ProtobufCpp/src\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; @@ -2748,12 +2756,12 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"", "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", "\"${PODS_ROOT}/GoogleTest/googletest/include\"", "\"${PODS_ROOT}/leveldb-library/include\"", - "\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"", - "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", "\"${PODS_ROOT}/ProtobufCpp/src\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; @@ -3044,8 +3052,9 @@ "$(inherited)", "\"${PODS_ROOT}/../../..\"", "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", - "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; OTHER_LDFLAGS = ( @@ -3081,8 +3090,9 @@ "$(inherited)", "\"${PODS_ROOT}/../../..\"", "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", - "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; OTHER_LDFLAGS = ( diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index 91fbecb03f3..15ebfcaf995 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -37,6 +37,7 @@ #include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" #include "Firestore/core/src/firebase/firestore/util/status.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" +#include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h" using firebase::firestore::FirestoreErrorCode; using firebase::firestore::core::DocumentViewChange; @@ -49,9 +50,28 @@ using firebase::firestore::util::ExecutorLibdispatch; using firebase::firestore::util::Status; using firebase::firestore::util::StatusOr; +using testing::ElementsAre; +using testing::IsEmpty; NS_ASSUME_NONNULL_BEGIN +namespace { + +ViewSnapshot ExcludingMetadataChanges(const ViewSnapshot &snapshot) { + return ViewSnapshot{ + snapshot.query(), + snapshot.documents(), + snapshot.old_documents(), + snapshot.document_changes(), + snapshot.mutated_keys(), + snapshot.from_cache(), + snapshot.sync_state_changed(), + /*excludes_metadata_changes=*/true, + }; +} + +} // namespace + @interface FSTQueryListenerTests : XCTestCase @end @@ -67,20 +87,6 @@ - (void)setUp { waitForSyncWhenOnline:NO]; } -- (ViewSnapshot)setExcludesMetadataChanges:(bool)excludesMetadataChanges - snapshot:(const ViewSnapshot &)snapshot { - return ViewSnapshot{ - snapshot.query(), - snapshot.documents(), - snapshot.old_documents(), - snapshot.document_changes(), - snapshot.mutated_keys(), - snapshot.from_cache(), - snapshot.sync_state_changed(), - excludesMetadataChanges, - }; -} - - (void)testRaisesCollectionEvents { std::vector accum; std::vector otherAccum; @@ -109,9 +115,9 @@ - (void)testRaisesCollectionEvents { [listener queryDidChangeViewSnapshot:snap2]; [otherListener queryDidChangeViewSnapshot:snap2]; - XCTAssertTrue((accum == std::vector{snap1, snap2})); - XCTAssertTrue((accum[0].document_changes() == std::vector{change1, change2})); - XCTAssertTrue(accum[1].document_changes() == std::vector{change3}); + XC_ASSERT_THAT(accum, ElementsAre(snap1, snap2)); + XC_ASSERT_THAT(accum[0].document_changes(), ElementsAre(change1, change2)); + XC_ASSERT_THAT(accum[1].document_changes(), ElementsAre(change3)); ViewSnapshot expectedSnap2{ snap2.query(), @@ -122,7 +128,7 @@ - (void)testRaisesCollectionEvents { snap2.from_cache(), /*sync_state_changed=*/true, /*excludes_metadata_changes=*/true}; - XCTAssertTrue((otherAccum == std::vector{expectedSnap2})); + XC_ASSERT_THAT(otherAccum, ElementsAre(expectedSnap2)); } - (void)testRaisesErrorEvent { @@ -137,7 +143,7 @@ - (void)testRaisesErrorEvent { Status testError{FirestoreErrorCode::Unauthenticated, "Some info"}; [listener queryDidError:testError]; - XCTAssertTrue((accum == std::vector{testError})); + XC_ASSERT_THAT(accum, ElementsAre(testError)); } - (void)testRaisesEventForEmptyCollectionAfterSync { @@ -153,10 +159,10 @@ - (void)testRaisesEventForEmptyCollectionAfterSync { ViewSnapshot snap2 = FSTTestApplyChanges(view, @[], FSTTestTargetChangeMarkCurrent()).value(); [listener queryDidChangeViewSnapshot:snap1]; - XCTAssertTrue(accum.empty()); + XC_ASSERT_THAT(accum, IsEmpty()); [listener queryDidChangeViewSnapshot:snap2]; - XCTAssertTrue((accum == std::vector{snap2})); + XC_ASSERT_THAT(accum, ElementsAre(snap2)); } - (void)testMutingAsyncListenerPreventsAllSubsequentEvents { @@ -193,7 +199,7 @@ - (void)testMutingAsyncListenerPreventsAllSubsequentEvents { }]; // We should get the first snapshot but not the second. - XCTAssertTrue((accum == std::vector{viewSnapshot1})); + XC_ASSERT_THAT(accum, ElementsAre(viewSnapshot1)); } - (void)testDoesNotRaiseEventsForMetadataChangesUnlessSpecified { @@ -225,10 +231,9 @@ - (void)testDoesNotRaiseEventsForMetadataChangesUnlessSpecified { [fullListener queryDidChangeViewSnapshot:snap2]; // state change event [fullListener queryDidChangeViewSnapshot:snap3]; // doc2 update - XCTAssertTrue((filteredAccum == - std::vector{[self setExcludesMetadataChanges:true snapshot:snap1], - [self setExcludesMetadataChanges:true snapshot:snap3]})); - XCTAssertTrue((fullAccum == std::vector{snap1, snap2, snap3})); + XC_ASSERT_THAT(filteredAccum, + ElementsAre(ExcludingMetadataChanges(snap1), ExcludingMetadataChanges(snap3))); + XC_ASSERT_THAT(fullAccum, ElementsAre(snap1, snap2, snap3)); } - (void)testRaisesDocumentMetadataEventsOnlyWhenSpecified { @@ -270,18 +275,15 @@ - (void)testRaisesDocumentMetadataEventsOnlyWhenSpecified { [fullListener queryDidChangeViewSnapshot:snap2]; [fullListener queryDidChangeViewSnapshot:snap3]; - XCTAssertTrue((filteredAccum == - std::vector{[self setExcludesMetadataChanges:true snapshot:snap1], - [self setExcludesMetadataChanges:true snapshot:snap3]})); - XCTAssertTrue( - (filteredAccum[0].document_changes() == std::vector{change1, change2})); - XCTAssertTrue((filteredAccum[1].document_changes() == std::vector{change4})); - - XCTAssertTrue((fullAccum == std::vector{snap1, snap2, snap3})); - XCTAssertTrue( - (fullAccum[0].document_changes() == std::vector{change1, change2})); - XCTAssertTrue((fullAccum[1].document_changes() == std::vector{change3})); - XCTAssertTrue((fullAccum[2].document_changes() == std::vector{change4})); + XC_ASSERT_THAT(filteredAccum, + ElementsAre(ExcludingMetadataChanges(snap1), ExcludingMetadataChanges(snap3))); + XC_ASSERT_THAT(filteredAccum[0].document_changes(), ElementsAre(change1, change2)); + XC_ASSERT_THAT(filteredAccum[1].document_changes(), ElementsAre(change4)); + + XC_ASSERT_THAT(fullAccum, ElementsAre(snap1, snap2, snap3)); + XC_ASSERT_THAT(fullAccum[0].document_changes(), ElementsAre(change1, change2)); + XC_ASSERT_THAT(fullAccum[1].document_changes(), ElementsAre(change3)); + XC_ASSERT_THAT(fullAccum[2].document_changes(), ElementsAre(change4)); } - (void)testRaisesQueryMetadataEventsOnlyWhenHasPendingWritesOnTheQueryChanges { @@ -326,10 +328,9 @@ - (void)testRaisesQueryMetadataEventsOnlyWhenHasPendingWritesOnTheQueryChanges { snap4.sync_state_changed(), /*excludes_metadata_changes=*/true // This test excludes document metadata changes }; - XCTAssertTrue( - (fullAccum == std::vector{[self setExcludesMetadataChanges:true snapshot:snap1], - [self setExcludesMetadataChanges:true snapshot:snap3], - expectedSnap4})); + + XC_ASSERT_THAT(fullAccum, ElementsAre(ExcludingMetadataChanges(snap1), + ExcludingMetadataChanges(snap3), expectedSnap4)); } - (void)testMetadataOnlyDocumentChangesAreFilteredOutWhenIncludeDocumentMetadataChangesIsFalse { @@ -363,9 +364,7 @@ - (void)testMetadataOnlyDocumentChangesAreFilteredOutWhenIncludeDocumentMetadata snap2.from_cache(), snap2.sync_state_changed(), /*excludes_metadata_changes=*/true}; - XCTAssertTrue((filteredAccum == std::vector{[self setExcludesMetadataChanges:true - snapshot:snap1], - expectedSnap2})); + XC_ASSERT_THAT(filteredAccum, ElementsAre(ExcludingMetadataChanges(snap1), expectedSnap2)); } - (void)testWillWaitForSyncIfOnline { @@ -405,7 +404,7 @@ - (void)testWillWaitForSyncIfOnline { /*from_cache=*/false, /*sync_state_changed=*/true, /*excludes_metadata_changes=*/true}; - XCTAssertTrue((events == std::vector{expectedSnap})); + XC_ASSERT_THAT(events, ElementsAre(expectedSnap)); } - (void)testWillRaiseInitialEventWhenGoingOffline { @@ -452,7 +451,7 @@ - (void)testWillRaiseInitialEventWhenGoingOffline { /*from_cache=*/true, /*sync_state_changed=*/false, /*excludes_metadata_changes=*/true}; - XCTAssertTrue((events == std::vector{expectedSnap1, expectedSnap2})); + XC_ASSERT_THAT(events, ElementsAre(expectedSnap1, expectedSnap2)); } - (void)testWillRaiseInitialEventWhenGoingOfflineAndThereAreNoDocs { @@ -479,7 +478,7 @@ - (void)testWillRaiseInitialEventWhenGoingOfflineAndThereAreNoDocs { /*from_cache=*/true, /*sync_state_changed=*/true, /*excludes_metadata_changes=*/true}; - XCTAssertTrue((events == std::vector{expectedSnap})); + XC_ASSERT_THAT(events, ElementsAre(expectedSnap)); } - (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { @@ -505,7 +504,7 @@ - (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { /*from_cache=*/true, /*sync_state_changed=*/true, /*excludes_metadata_changes=*/true}; - XCTAssertTrue((events == std::vector{expectedSnap})); + XC_ASSERT_THAT(events, ElementsAre(expectedSnap)); } - (FSTQueryListener *)listenToQuery:(FSTQuery *)query handler:(ViewSnapshotHandler &&)handler { diff --git a/Firestore/Example/Tests/Core/FSTViewTests.mm b/Firestore/Example/Tests/Core/FSTViewTests.mm index 2d451a2c6ed..2bd80aab592 100644 --- a/Firestore/Example/Tests/Core/FSTViewTests.mm +++ b/Firestore/Example/Tests/Core/FSTViewTests.mm @@ -32,6 +32,7 @@ #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" +#include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h" #include "absl/types/optional.h" namespace testutil = firebase::firestore::testutil; @@ -39,6 +40,7 @@ using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::ResourcePath; using firebase::firestore::model::DocumentKeySet; +using testing::ElementsAre; NS_ASSUME_NONNULL_BEGIN @@ -217,11 +219,10 @@ - (void)testUpdatesDocumentsBasedOnQueryWithFilter { XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ newDoc4, doc1, newDoc2 ])); - XCTAssertTrue((snapshot.document_changes() == - std::vector{ - DocumentViewChange{doc3, DocumentViewChange::Type::kRemoved}, - DocumentViewChange{newDoc4, DocumentViewChange::Type::kAdded}, - DocumentViewChange{newDoc2, DocumentViewChange::Type::kAdded}})); + XC_ASSERT_THAT(snapshot.document_changes(), + ElementsAre(DocumentViewChange{doc3, DocumentViewChange::Type::kRemoved}, + DocumentViewChange{newDoc4, DocumentViewChange::Type::kAdded}, + DocumentViewChange{newDoc2, DocumentViewChange::Type::kAdded})); XCTAssertTrue(snapshot.from_cache()); XCTAssertFalse(snapshot.sync_state_changed()); @@ -303,11 +304,9 @@ - (void)testDoesntReportChangesForDocumentBeyondLimitOfQuery { XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ doc1, doc3 ])); - XCTAssertTrue((snapshot.document_changes() == - std::vector{ - DocumentViewChange{doc2, DocumentViewChange::Type::kRemoved}, - DocumentViewChange{doc3, DocumentViewChange::Type::kAdded}, - })); + XC_ASSERT_THAT(snapshot.document_changes(), + ElementsAre(DocumentViewChange{doc2, DocumentViewChange::Type::kRemoved}, + DocumentViewChange{doc3, DocumentViewChange::Type::kAdded})); XCTAssertFalse(snapshot.from_cache()); XCTAssertTrue(snapshot.sync_state_changed()); @@ -694,28 +693,24 @@ - (void)testSuppressesWriteAcknowledgementIfWatchHasNotCaughtUp { [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2 ])]; FSTViewChange *viewChange = [view applyChangesToDocuments:changes]; - XCTAssertTrue((viewChange.snapshot.value().document_changes() == - std::vector{ - DocumentViewChange{doc1, DocumentViewChange::Type::kAdded}, - DocumentViewChange{doc2, DocumentViewChange::Type::kAdded}, - })); + XC_ASSERT_THAT(viewChange.snapshot.value().document_changes(), + ElementsAre(DocumentViewChange{doc1, DocumentViewChange::Type::kAdded}, + DocumentViewChange{doc2, DocumentViewChange::Type::kAdded})); changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1Committed, doc2Modified ])]; viewChange = [view applyChangesToDocuments:changes]; // The 'doc1Committed' update is suppressed - XCTAssertTrue((viewChange.snapshot.value().document_changes() == - std::vector{ - DocumentViewChange{doc2Modified, DocumentViewChange::Type::kModified}, - })); + XC_ASSERT_THAT( + viewChange.snapshot.value().document_changes(), + ElementsAre(DocumentViewChange{doc2Modified, DocumentViewChange::Type::kModified})); changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1Acknowledged, doc2Acknowledged ])]; viewChange = [view applyChangesToDocuments:changes]; - XCTAssertTrue((viewChange.snapshot.value().document_changes() == - std::vector{ - DocumentViewChange{doc1Acknowledged, DocumentViewChange::Type::kModified}, - DocumentViewChange{doc2Acknowledged, DocumentViewChange::Type::kMetadata}, - })); + XC_ASSERT_THAT( + viewChange.snapshot.value().document_changes(), + ElementsAre(DocumentViewChange{doc1Acknowledged, DocumentViewChange::Type::kModified}, + DocumentViewChange{doc2Acknowledged, DocumentViewChange::Type::kMetadata})); } @end diff --git a/Firestore/core/include/firebase/firestore/timestamp.h b/Firestore/core/include/firebase/firestore/timestamp.h index 1736981ed34..fe4ac8f318f 100644 --- a/Firestore/core/include/firebase/firestore/timestamp.h +++ b/Firestore/core/include/firebase/firestore/timestamp.h @@ -19,6 +19,7 @@ #include #include +#include #include #if !defined(_STLPORT_VERSION) @@ -148,6 +149,8 @@ class Timestamp { * don't rely on the format of the string. */ std::string ToString() const; + friend std::ostream& operator<<(std::ostream& out, + const Timestamp& timestamp); private: // Checks that the number of seconds is within the supported date range, and diff --git a/Firestore/core/src/firebase/firestore/core/view_snapshot.h b/Firestore/core/src/firebase/firestore/core/view_snapshot.h index 51306ba5c4a..502f4afb4e7 100644 --- a/Firestore/core/src/firebase/firestore/core/view_snapshot.h +++ b/Firestore/core/src/firebase/firestore/core/view_snapshot.h @@ -22,6 +22,7 @@ #endif // !defined(__OBJC__) #include +#include #include #include #include @@ -177,6 +178,7 @@ class ViewSnapshot { } std::string ToString() const; + friend std::ostream& operator<<(std::ostream& out, const ViewSnapshot& value); size_t Hash() const; private: diff --git a/Firestore/core/src/firebase/firestore/core/view_snapshot.mm b/Firestore/core/src/firebase/firestore/core/view_snapshot.mm index f063983375f..66e58fddd94 100644 --- a/Firestore/core/src/firebase/firestore/core/view_snapshot.mm +++ b/Firestore/core/src/firebase/firestore/core/view_snapshot.mm @@ -16,6 +16,8 @@ #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include + #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentSet.h" @@ -178,6 +180,10 @@ mutated_keys().size(), sync_state_changed(), excludes_metadata_changes()); } +std::ostream& operator<<(std::ostream& out, const ViewSnapshot& value) { + return out << value.ToString(); +} + size_t ViewSnapshot::Hash() const { // Note: We are omitting `mutated_keys_` from the hash, since we don't have a // straightforward way to compute its hash value. Since `ViewSnapshot` is diff --git a/Firestore/core/src/firebase/firestore/timestamp.cc b/Firestore/core/src/firebase/firestore/timestamp.cc index c35fca10ed1..1b67e9ea247 100644 --- a/Firestore/core/src/firebase/firestore/timestamp.cc +++ b/Firestore/core/src/firebase/firestore/timestamp.cc @@ -17,6 +17,7 @@ #include "Firestore/core/include/firebase/firestore/timestamp.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "absl/strings/str_cat.h" namespace firebase { @@ -84,8 +85,12 @@ Timestamp Timestamp::FromTimePoint( #endif // !defined(_STLPORT_VERSION) std::string Timestamp::ToString() const { - return std::string("Timestamp(seconds=") + std::to_string(seconds_) + - ", nanoseconds=" + std::to_string(nanoseconds_) + ")"; + return absl::StrCat("Timestamp(seconds=", seconds_, + ", nanoseconds=", nanoseconds_, ")"); +} + +std::ostream& operator<<(std::ostream& out, const Timestamp& timestamp) { + return out << timestamp.ToString(); } void Timestamp::ValidateBounds() const { diff --git a/Firestore/core/src/firebase/firestore/util/status.cc b/Firestore/core/src/firebase/firestore/util/status.cc index 838d9fc605c..a8ff913a028 100644 --- a/Firestore/core/src/firebase/firestore/util/status.cc +++ b/Firestore/core/src/firebase/firestore/util/status.cc @@ -127,6 +127,11 @@ std::string Status::ToString() const { } } +std::ostream& operator<<(std::ostream& out, const Status& status) { + out << status.ToString(); + return out; +} + void Status::IgnoreError() const { // no-op } diff --git a/Firestore/core/src/firebase/firestore/util/status.h b/Firestore/core/src/firebase/firestore/util/status.h index 2d1e32904c6..d49089ee0d6 100644 --- a/Firestore/core/src/firebase/firestore/util/status.h +++ b/Firestore/core/src/firebase/firestore/util/status.h @@ -100,6 +100,7 @@ class ABSL_MUST_USE_RESULT Status { /// \brief Return a string representation of this status suitable for /// printing. Returns the string `"OK"` for success. std::string ToString() const; + friend std::ostream& operator<<(std::ostream& out, const Status& status); // Ignores any errors. This method does nothing except potentially suppress // complaints from any tools that are checking that errors are not dropped on diff --git a/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt b/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt index 9d6e4a72515..6d548d943ac 100644 --- a/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt @@ -17,6 +17,7 @@ cc_library( SOURCES app_testing.h app_testing.mm + xcgmock.h DEPENDS FirebaseCore GoogleUtilities diff --git a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h new file mode 100644 index 00000000000..a4fdc2c8342 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h @@ -0,0 +1,213 @@ +/* + * Copyright 2019 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_TEST_FIREBASE_FIRESTORE_TESTUTIL_XCGMOCK_H_ +#define FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_XCGMOCK_H_ + +#if !defined(__OBJC__) +#error "This header only supports Objective-C++" +#endif // !defined(__OBJC__) + +#import + +#include +#include +#include +#include + +#import "Firestore/Source/Model/FSTDocument.h" + +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "gmock/gmock.h" + +namespace firebase { +namespace firestore { +namespace testutil { + +template +class XcTestRecorder { + public: + XcTestRecorder(M matcher, XCTestCase* test_case, const char* file, int line) + : formatter_(std::move(matcher)), + test_case_(test_case), + file_(file), + line_(line) { + } + + template + void Match(const char* text, const T& value) const { + testing::AssertionResult result = formatter_(text, value); + if (!result) { + RecordFailure(result.message()); + } + } + + void RecordFailure(const char* message) const { + [test_case_ + recordFailureWithDescription:[NSString stringWithUTF8String:message] + inFile:[NSString stringWithUTF8String:file_] + atLine:line_ + expected:YES]; + } + + private: + testing::internal::PredicateFormatterFromMatcher formatter_; + + XCTestCase* test_case_; + const char* file_; + int line_; +}; + +template +XcTestRecorder MakeXcTestRecorder(M matcher, + XCTestCase* test_case, + const char* file, + int line) { + return XcTestRecorder(std::move(matcher), test_case, file, line); +} + +#define XC_ASSERT_THAT(actual, matcher) \ + do { \ + auto recorder = firebase::firestore::testutil::MakeXcTestRecorder( \ + matcher, self, __FILE__, __LINE__); \ + recorder.Match(#actual, actual); \ + } while (0) + +/** + * Prints the -description of an Objective-C object to the given ostream. + */ +inline void ObjcPrintTo(id value, std::ostream* os) { + // Force the result type to NSString* or we can't resolve MakeString. + NSString* description = [value description]; + *os << util::MakeString(description); +} + +} // namespace testutil +} // namespace firestore +} // namespace firebase + +#define OBJC_PRINT_TO(objc_class) \ + @class objc_class; \ + inline void PrintTo(objc_class* value, std::ostream* os) { \ + firebase::firestore::testutil::ObjcPrintTo(value, os); \ + } + +// Define overloads for Objective-C types. Note that each type must be +// explicitly overloaded here because `id` cannot be implicitly converted to +// void* under ARC. If `id` could be converted to void*, then a single overload +// of `operator<<` would be sufficient. + +// Select Foundation types +OBJC_PRINT_TO(NSObject); +OBJC_PRINT_TO(NSArray); +OBJC_PRINT_TO(NSDictionary); +OBJC_PRINT_TO(NSNumber); +OBJC_PRINT_TO(NSString); + +// Declare all Firestore Objective-C classes printable. +// +// Regenerate with: +// find Firestore/Source -name \*.h \ +// | xargs sed -n '/@interface/{ s/<.*//; p; }' \ +// | awk '{ print "OBJC_PRINT_TO(" $2 ");" }' \ +// | sort -u + +OBJC_PRINT_TO(FIRCollectionReference); +OBJC_PRINT_TO(FIRDocumentChange); +OBJC_PRINT_TO(FIRDocumentReference); +OBJC_PRINT_TO(FIRDocumentSnapshot); +OBJC_PRINT_TO(FIRFieldPath); +OBJC_PRINT_TO(FIRFieldValue); +OBJC_PRINT_TO(FIRFirestore); +OBJC_PRINT_TO(FIRFirestoreSettings); +OBJC_PRINT_TO(FIRGeoPoint); +OBJC_PRINT_TO(FIRQuery); +OBJC_PRINT_TO(FIRQueryDocumentSnapshot); +OBJC_PRINT_TO(FIRQuerySnapshot); +OBJC_PRINT_TO(FIRSnapshotMetadata); +OBJC_PRINT_TO(FIRTimestamp); +OBJC_PRINT_TO(FIRTransaction); +OBJC_PRINT_TO(FIRWriteBatch); +OBJC_PRINT_TO(FSTArrayRemoveFieldValue); +OBJC_PRINT_TO(FSTArrayUnionFieldValue); +OBJC_PRINT_TO(FSTArrayValue); +OBJC_PRINT_TO(FSTAsyncQueryListener); +OBJC_PRINT_TO(FSTBlobValue); +OBJC_PRINT_TO(FSTBooleanValue); +OBJC_PRINT_TO(FSTBound); +OBJC_PRINT_TO(FSTDeleteFieldValue); +OBJC_PRINT_TO(FSTDeleteMutation); +OBJC_PRINT_TO(FSTDeletedDocument); +OBJC_PRINT_TO(FSTDocument); +OBJC_PRINT_TO(FSTDocumentKey); +OBJC_PRINT_TO(FSTDocumentKeyReference); +OBJC_PRINT_TO(FSTDocumentSet); +OBJC_PRINT_TO(FSTDoubleValue); +OBJC_PRINT_TO(FSTEventManager); +OBJC_PRINT_TO(FSTFieldValue); +OBJC_PRINT_TO(FSTFieldValueOptions); +OBJC_PRINT_TO(FSTFilter); +OBJC_PRINT_TO(FSTFirestoreClient); +OBJC_PRINT_TO(FSTFirestoreComponent); +OBJC_PRINT_TO(FSTGeoPointValue); +OBJC_PRINT_TO(FSTIntegerValue); +OBJC_PRINT_TO(FSTLRUGarbageCollector); +OBJC_PRINT_TO(FSTLevelDB); +OBJC_PRINT_TO(FSTLevelDBLRUDelegate); +OBJC_PRINT_TO(FSTLimboDocumentChange); +OBJC_PRINT_TO(FSTListenOptions); +OBJC_PRINT_TO(FSTListenerRegistration); +OBJC_PRINT_TO(FSTLocalDocumentsView); +OBJC_PRINT_TO(FSTLocalSerializer); +OBJC_PRINT_TO(FSTLocalStore); +OBJC_PRINT_TO(FSTLocalViewChanges); +OBJC_PRINT_TO(FSTLocalWriteResult); +OBJC_PRINT_TO(FSTMaybeDocument); +OBJC_PRINT_TO(FSTMemoryEagerReferenceDelegate); +OBJC_PRINT_TO(FSTMemoryLRUReferenceDelegate); +OBJC_PRINT_TO(FSTMemoryPersistence); +OBJC_PRINT_TO(FSTMutation); +OBJC_PRINT_TO(FSTMutationBatch); +OBJC_PRINT_TO(FSTMutationBatchResult); +OBJC_PRINT_TO(FSTMutationResult); +OBJC_PRINT_TO(FSTNanFilter); +OBJC_PRINT_TO(FSTNullFilter); +OBJC_PRINT_TO(FSTNullValue); +OBJC_PRINT_TO(FSTNumberValue); +OBJC_PRINT_TO(FSTNumericIncrementFieldValue); +OBJC_PRINT_TO(FSTObjectValue); +OBJC_PRINT_TO(FSTPatchMutation); +OBJC_PRINT_TO(FSTQuery); +OBJC_PRINT_TO(FSTQueryData); +OBJC_PRINT_TO(FSTQueryListener); +OBJC_PRINT_TO(FSTReferenceValue); +OBJC_PRINT_TO(FSTRelationFilter); +OBJC_PRINT_TO(FSTSerializerBeta); +OBJC_PRINT_TO(FSTServerTimestampFieldValue); +OBJC_PRINT_TO(FSTServerTimestampValue); +OBJC_PRINT_TO(FSTSetMutation); +OBJC_PRINT_TO(FSTSortOrder); +OBJC_PRINT_TO(FSTStringValue); +OBJC_PRINT_TO(FSTSyncEngine); +OBJC_PRINT_TO(FSTTimestampValue); +OBJC_PRINT_TO(FSTTransformMutation); +OBJC_PRINT_TO(FSTUnknownDocument); +OBJC_PRINT_TO(FSTUserDataConverter); +OBJC_PRINT_TO(FSTView); +OBJC_PRINT_TO(FSTViewChange); +OBJC_PRINT_TO(FSTViewDocumentChanges); + +#endif // FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_XCGMOCK_H_ diff --git a/Firestore/core/test/firebase/firestore/testutil/xcgmock_test.mm b/Firestore/core/test/firebase/firestore/testutil/xcgmock_test.mm new file mode 100644 index 00000000000..5bac8b38d0d --- /dev/null +++ b/Firestore/core/test/firebase/firestore/testutil/xcgmock_test.mm @@ -0,0 +1,64 @@ +/* + * Copyright 2019 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/test/firebase/firestore/testutil/xcgmock.h" + +#import "Firestore/Source/Core/FSTQuery.h" + +#include "Firestore/core/src/firebase/firestore/util/status.h" +#include "Firestore/core/src/firebase/firestore/util/to_string.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace testutil { + +TEST(XcGmockTest, NSArrayPrints) { + std::string expected = util::ToString(@[ @"value" ]); + + EXPECT_EQ(expected, testing::PrintToString(@[ @"value" ])); +} + +TEST(XcGmockTest, NSNumberPrints) { + EXPECT_EQ("1", testing::PrintToString(@1)); +} + +// TODO(wilhuff): make this actually work! +// For whatever reason, this prints like a pointer. +TEST(XcGmockTest, DISABLED_NSStringPrints) { + EXPECT_EQ("value", testing::PrintToString(@"value")); +} + +TEST(XcGmockTest, FSTNullFilterPrints) { + FSTNullFilter* filter = + [[FSTNullFilter alloc] initWithField:model::FieldPath({"field"})]; + EXPECT_EQ("field IS NULL", testing::PrintToString(filter)); +} + +TEST(XcGmockTest, StatusPrints) { + util::Status status(FirestoreErrorCode::NotFound, "missing foo"); + EXPECT_EQ("Not found: missing foo", testing::PrintToString(status)); +} + +TEST(XcGmockTest, TimestampPrints) { + Timestamp timestamp(32, 42); + EXPECT_EQ("Timestamp(seconds=32, nanoseconds=42)", + testing::PrintToString(timestamp)); +} + +} // namespace testutil +} // namespace firestore +} // namespace firebase From c4527b16619a2b3dc5857d19883853a58e41e00b Mon Sep 17 00:00:00 2001 From: Gil Date: Thu, 21 Mar 2019 15:25:29 -0700 Subject: [PATCH 072/214] Rework gRPC certificate loading one last time (#2606) --- Firestore/CHANGELOG.md | 1 + .../AppIcon.appiconset/Contents.json | 7 +- .../Example/App/macOS_example/AppDelegate.m | 1 - .../Firestore.xcodeproj/project.pbxproj | 6 +- .../grpc_root_certificate_finder_apple.mm | 120 +++++++++++++----- 5 files changed, 99 insertions(+), 36 deletions(-) diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index e92b663da5b..1ff2bf5c443 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,4 +1,5 @@ # Unreleased +- [fixed] Fixed the way gRPC certificates are loaded on macOS (#2604). # 1.1.0 - [feature] Added `FieldValue.increment()`, which can be used in diff --git a/Firestore/Example/App/iOS/Images.xcassets/AppIcon.appiconset/Contents.json b/Firestore/Example/App/iOS/Images.xcassets/AppIcon.appiconset/Contents.json index d7070bc5c02..d8db8d65fd7 100644 --- a/Firestore/Example/App/iOS/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Firestore/Example/App/iOS/Images.xcassets/AppIcon.appiconset/Contents.json @@ -84,10 +84,15 @@ "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } -} +} \ No newline at end of file diff --git a/Firestore/Example/App/macOS_example/AppDelegate.m b/Firestore/Example/App/macOS_example/AppDelegate.m index 5a852fd41cc..0c753a7eed7 100644 --- a/Firestore/Example/App/macOS_example/AppDelegate.m +++ b/Firestore/Example/App/macOS_example/AppDelegate.m @@ -35,7 +35,6 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // do the timestamp fix FIRFirestoreSettings *settings = db.settings; - settings.timestampsInSnapshotsEnabled = true; db.settings = settings; // create a doc diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index b004d7fed79..91147e35e1f 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -565,7 +565,7 @@ BA6E5B9D53CCF301F58A62D7 /* xcgmock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = xcgmock.h; sourceTree = ""; }; BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8522DE226C467C54E6788D8 /* mutation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = mutation_test.cc; sourceTree = ""; }; - D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = delayed_constructor_test.cc; sourceTree = ""; }; + D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = delayed_constructor_test.cc; sourceTree = ""; }; D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = perf_spec_test.json; sourceTree = ""; }; D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRNumericTransformTests.mm; sourceTree = ""; }; @@ -2944,8 +2944,6 @@ "\"leveldb\"", "-framework", "\"nanopb\"", - "-framework", - "\"openssl\"", "-ObjC", ); PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-macOS"; @@ -3020,8 +3018,6 @@ "\"leveldb\"", "-framework", "\"nanopb\"", - "-framework", - "\"openssl\"", "-ObjC", ); PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-macOS"; diff --git a/Firestore/core/src/firebase/firestore/remote/grpc_root_certificate_finder_apple.mm b/Firestore/core/src/firebase/firestore/remote/grpc_root_certificate_finder_apple.mm index 4cde6ea75dd..b7e0db4b0f5 100644 --- a/Firestore/core/src/firebase/firestore/remote/grpc_root_certificate_finder_apple.mm +++ b/Firestore/core/src/firebase/firestore/remote/grpc_root_certificate_finder_apple.mm @@ -16,14 +16,15 @@ #include "Firestore/core/src/firebase/firestore/remote/grpc_root_certificate_finder.h" +#import + #include #include "Firestore/core/src/firebase/firestore/util/filesystem.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" - -#import "Firestore/Source/Core/FSTFirestoreClient.h" +#include "absl/strings/str_cat.h" namespace firebase { namespace firestore { @@ -34,45 +35,106 @@ using util::StatusOr; using util::StringFormat; +namespace { + +/** + * Finds the roots.pem certificate file in the given resource bundle and logs + * the outcome. + * + * @param bundle The bundle to check. Can be a nested bundle in Resources or + * an app or framework bundle to look in directly. + * @param parent The parent bundle of the bundle to search. Used for logging. + */ +NSString* _Nullable FindCertFileInResourceBundle(NSBundle* _Nullable bundle, + NSBundle* _Nullable parent) { + if (!bundle) return nil; + + NSString* path = [bundle pathForResource:@"roots" ofType:@"pem"]; + if (util::LogIsDebugEnabled()) { + std::string message = + absl::StrCat("roots.pem ", path ? "found " : "not found ", "in bundle ", + util::MakeString([bundle bundleIdentifier])); + if (parent) { + absl::StrAppend(&message, " (in parent ", + util::MakeString([parent bundleIdentifier]), ")"); + } + LOG_DEBUG("%s", message); + } + + return path; +} + +/** + * Finds gRPCCertificates.bundle inside the given parent, if it exists. + * + * This function exists mostly to handle differences in platforms. + * On iOS, resources are nested directly within the top-level of the parent + * bundle, but on macOS this will actually be in Contents/Resources. + * + * @param parent A framework or app bundle to check. + * @return The nested gRPCCertificates.bundle if found, otherwise nil. + */ +NSBundle* _Nullable FindCertBundleInParent(NSBundle* _Nullable parent) { + if (!parent) return nil; + + NSString* path = [parent pathForResource:@"gRPCCertificates" + ofType:@"bundle"]; + if (!path) return nil; + + return [[NSBundle alloc] initWithPath:path]; +} + +NSBundle* _Nullable FindFirestoreFrameworkBundle() { + // Load FIRFirestore reflectively to avoid a circular reference at build time. + Class firestore_class = objc_getClass("FIRFirestore"); + if (!firestore_class) return nil; + + return [NSBundle bundleForClass:firestore_class]; +} + +/** + * Finds the path to the roots.pem certificates file, wherever it may be. + * + * Carthage users will find roots.pem inside gRPCCertificates.bundle in + * the main bundle. + * + * There have been enough variations and workarounds posted on this that + * this also accepts the roots.pem file outside gRPCCertificates.bundle. + */ NSString* FindPathToCertificatesFile() { - // Certificates file might be present in either the gRPC-C++ bundle or (for + // Certificates file might be present in either the gRPC-C++ framework or (for // some projects) in the main bundle. NSBundle* bundles[] = { - // Try to load certificates bundled by gRPC-C++. + // CocoaPods: try to load from the gRPC-C++ Framework. [NSBundle bundleWithIdentifier:@"org.cocoapods.grpcpp"], - // Users manually adding resources to the project may add the - // certificate to the main application bundle. Note that `mainBundle` is - // nil for unit tests of library projects, so it cannot fully substitute - // for checking the framework bundle. + + // Carthage: try to load from the FirebaseFirestore.framework + FindFirestoreFrameworkBundle(), + + // Carthage and manual projects: users manually adding resources to the + // project may add the certificate to the main application bundle. Note + // that `mainBundle` is nil for unit tests of library projects. [NSBundle mainBundle], }; - // search for the roots.pem file in each of these resource locations - NSString* possibleResources[] = { - @"gRPCCertificates.bundle/roots", - @"roots", - }; + NSString* path = nil; - for (NSBundle* bundle : bundles) { - if (!bundle) { - continue; - } + for (NSBundle* parent : bundles) { + if (!parent) continue; - for (NSString* resource : possibleResources) { - NSString* path = [bundle pathForResource:resource ofType:@"pem"]; - if (path) { - LOG_DEBUG("%s.pem found in bundle %s", resource, - [bundle bundleIdentifier]); - return path; - } else { - LOG_DEBUG("%s.pem not found in bundle %s", resource, - [bundle bundleIdentifier]); - } - } + NSBundle* certs_bundle = FindCertBundleInParent(parent); + path = FindCertFileInResourceBundle(certs_bundle, parent); + if (path) break; + + path = FindCertFileInResourceBundle(parent, nil); + if (path) break; } - return nil; + + return path; } +} // namespace + std::string LoadGrpcRootCertificate() { NSString* path = FindPathToCertificatesFile(); HARD_ASSERT( From 378f4dbd24a1c9617fe0fb2ac51f5bc0390b4ba1 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Fri, 22 Mar 2019 14:30:43 -0400 Subject: [PATCH 073/214] Add support to [de]serialize missing FV types to the remote serializer (#2588) Add support for [de]serializing double/blob/GeoPoint/Array FieldValues --- .../firebase/firestore/model/field_value.h | 20 +++ .../firebase/firestore/remote/serializer.cc | 143 +++++++++++++++--- .../firebase/firestore/remote/serializer.h | 11 ++ .../firestore/remote/serializer_test.cc | 141 ++++++++++++++++- 4 files changed, 287 insertions(+), 28 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h index 0aeb2d316e2..c7137c12185 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.h +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -117,6 +117,11 @@ class FieldValue { return integer_value_; } + double double_value() const { + HARD_ASSERT(tag_ == Type::Double); + return double_value_; + } + Timestamp timestamp_value() const { HARD_ASSERT(tag_ == Type::Timestamp); return *timestamp_value_; @@ -127,6 +132,21 @@ class FieldValue { return *string_value_; } + const std::vector& blob_value() const { + HARD_ASSERT(tag_ == Type::Blob); + return *blob_value_; + } + + const GeoPoint& geo_point_value() const { + HARD_ASSERT(tag_ == Type::GeoPoint); + return *geo_point_value_; + } + + const std::vector& array_value() const { + HARD_ASSERT(tag_ == Type::Array); + return *array_value_; + } + /** factory methods. */ static FieldValue Null(); static FieldValue True(); diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc index 06703e2e310..9c0c008caea 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.cc +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -286,38 +286,62 @@ google_firestore_v1_Value Serializer::EncodeFieldValue( case FieldValue::Type::Null: result.which_value_type = google_firestore_v1_Value_null_value_tag; result.null_value = google_protobuf_NullValue_NULL_VALUE; - break; + return result; case FieldValue::Type::Boolean: result.which_value_type = google_firestore_v1_Value_boolean_value_tag; result.boolean_value = field_value.boolean_value(); - break; + return result; case FieldValue::Type::Integer: result.which_value_type = google_firestore_v1_Value_integer_value_tag; result.integer_value = field_value.integer_value(); - break; + return result; - case FieldValue::Type::String: - result.which_value_type = google_firestore_v1_Value_string_value_tag; - result.string_value = EncodeString(field_value.string_value()); - break; + case FieldValue::Type::Double: + result.which_value_type = google_firestore_v1_Value_double_value_tag; + result.double_value = field_value.double_value(); + return result; case FieldValue::Type::Timestamp: result.which_value_type = google_firestore_v1_Value_timestamp_value_tag; result.timestamp_value = EncodeTimestamp(field_value.timestamp_value()); - break; + return result; + + case FieldValue::Type::ServerTimestamp: + // TODO(rsgowman): Implement + abort(); + + case FieldValue::Type::String: + result.which_value_type = google_firestore_v1_Value_string_value_tag; + result.string_value = EncodeString(field_value.string_value()); + return result; + + case FieldValue::Type::Blob: + result.which_value_type = google_firestore_v1_Value_bytes_value_tag; + result.bytes_value = EncodeBytes(field_value.blob_value()); + return result; + + case FieldValue::Type::Reference: + // TODO(rsgowman): Implement + abort(); + + case FieldValue::Type::GeoPoint: + result.which_value_type = google_firestore_v1_Value_geo_point_value_tag; + result.geo_point_value = EncodeGeoPoint(field_value.geo_point_value()); + return result; + + case FieldValue::Type::Array: + result.which_value_type = google_firestore_v1_Value_array_value_tag; + result.array_value = EncodeArray(field_value.array_value()); + return result; case FieldValue::Type::Object: result.which_value_type = google_firestore_v1_Value_map_value_tag; result.map_value = EncodeMapValue(ObjectValue(field_value)); - break; - - default: - // TODO(rsgowman): implement the other types - abort(); + return result; } - return result; + UNREACHABLE(); } FieldValue Serializer::DecodeFieldValue(Reader* reader, @@ -342,27 +366,38 @@ FieldValue Serializer::DecodeFieldValue(Reader* reader, case google_firestore_v1_Value_integer_value_tag: return FieldValue::FromInteger(msg.integer_value); - case google_firestore_v1_Value_string_value_tag: - return FieldValue::FromString(DecodeString(msg.string_value)); + case google_firestore_v1_Value_double_value_tag: + return FieldValue::FromDouble(msg.double_value); case google_firestore_v1_Value_timestamp_value_tag: { return FieldValue::FromTimestamp( DecodeTimestamp(reader, msg.timestamp_value)); } - case google_firestore_v1_Value_map_value_tag: { - return FieldValue::FromMap(DecodeMapValue(reader, msg.map_value)); + case google_firestore_v1_Value_string_value_tag: + return FieldValue::FromString(DecodeString(msg.string_value)); + + case google_firestore_v1_Value_bytes_value_tag: { + std::vector bytes = DecodeBytes(msg.bytes_value); + return FieldValue::FromBlob(bytes.data(), bytes.size()); } - case google_firestore_v1_Value_double_value_tag: - case google_firestore_v1_Value_bytes_value_tag: case google_firestore_v1_Value_reference_value_tag: - case google_firestore_v1_Value_geo_point_value_tag: - case google_firestore_v1_Value_array_value_tag: // TODO(b/74243929): Implement remaining types. HARD_FAIL("Unhandled message field number (tag): %i.", msg.which_value_type); + case google_firestore_v1_Value_geo_point_value_tag: + return FieldValue::FromGeoPoint( + DecodeGeoPoint(reader, msg.geo_point_value)); + + case google_firestore_v1_Value_array_value_tag: + return FieldValue::FromArray(DecodeArray(reader, msg.array_value)); + + case google_firestore_v1_Value_map_value_tag: { + return FieldValue::FromMap(DecodeMapValue(reader, msg.map_value)); + } + default: reader->Fail(StringFormat("Invalid type while decoding FieldValue: %s", msg.which_value_type)); @@ -667,7 +702,7 @@ google_firestore_v1_DocumentMask Serializer::EncodeDocumentMask( google_firestore_v1_DocumentMask result{}; size_t count = mask.size(); - HARD_ASSERT(count <= std::numeric_limits::max(), + HARD_ASSERT(count <= PB_SIZE_MAX, "Unable to encode specified document mask. Too many fields."); result.field_paths_count = static_cast(count); result.field_paths = MakeArray(count); @@ -816,7 +851,7 @@ Timestamp Serializer::DecodeTimestamp( reader->Fail( "Invalid message: timestamp beyond the earliest supported date"); } else if (TimestampInternal::Max().seconds() < timestamp_proto.seconds) { - reader->Fail("Invalid message: timestamp behond the latest supported date"); + reader->Fail("Invalid message: timestamp beyond the latest supported date"); } else if (timestamp_proto.nanos < 0 || timestamp_proto.nanos > 999999999) { reader->Fail( "Invalid message: timestamp nanos must be between 0 and 999999999"); @@ -826,6 +861,66 @@ Timestamp Serializer::DecodeTimestamp( return Timestamp{timestamp_proto.seconds, timestamp_proto.nanos}; } +/* static */ +google_type_LatLng Serializer::EncodeGeoPoint(const GeoPoint& geo_point_value) { + google_type_LatLng result{}; + result.latitude = geo_point_value.latitude(); + result.longitude = geo_point_value.longitude(); + return result; +} + +/* static */ +GeoPoint Serializer::DecodeGeoPoint(nanopb::Reader* reader, + const google_type_LatLng& latlng_proto) { + // The GeoPoint ctor will assert if we provide values outside the valid range. + // However, since we're decoding, a single corrupt byte could cause this to + // occur, so we'll verify the ranges before passing them in since we'd rather + // not abort in these situations. + double latitude = latlng_proto.latitude; + double longitude = latlng_proto.longitude; + if (std::isnan(latitude) || latitude < -90 || 90 < latitude) { + reader->Fail("Invalid message: Latitude must be in the range of [-90, 90]"); + } else if (std::isnan(longitude) || longitude < -180 || 180 < longitude) { + reader->Fail( + "Invalid message: Latitude must be in the range of [-180, 180]"); + } + + if (!reader->status().ok()) return GeoPoint(); + return GeoPoint(latitude, longitude); +} + +/* static */ +google_firestore_v1_ArrayValue Serializer::EncodeArray( + const std::vector& array_value) { + google_firestore_v1_ArrayValue result{}; + + size_t count = array_value.size(); + HARD_ASSERT(count <= PB_SIZE_MAX, + "Unable to encode specified array. Too many entries."); + result.values_count = count; + result.values = MakeArray(count); + + size_t i = 0; + for (const FieldValue& fv : array_value) { + result.values[i++] = EncodeFieldValue(fv); + } + + return result; +} + +/* static */ +std::vector Serializer::DecodeArray( + nanopb::Reader* reader, const google_firestore_v1_ArrayValue& array_proto) { + std::vector result; + result.reserve(array_proto.values_count); + + for (size_t i = 0; i < array_proto.values_count; i++) { + result.push_back(DecodeFieldValue(reader, array_proto.values[i])); + } + + return result; +} + } // namespace remote } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h index 9fb5246f2f8..df8c2a12ddc 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.h +++ b/Firestore/core/src/firebase/firestore/remote/serializer.h @@ -25,6 +25,7 @@ #include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h" #include "Firestore/Protos/nanopb/google/firestore/v1/firestore.nanopb.h" +#include "Firestore/Protos/nanopb/google/type/latlng.nanopb.h" #include "Firestore/core/src/firebase/firestore/core/query.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document.h" @@ -210,6 +211,16 @@ class Serializer { nanopb::Reader* reader, const google_firestore_v1_Target_QueryTarget& proto); + static google_type_LatLng EncodeGeoPoint(const GeoPoint& geo_point_value); + static GeoPoint DecodeGeoPoint(nanopb::Reader* reader, + const google_type_LatLng& latlng_proto); + + static google_firestore_v1_ArrayValue EncodeArray( + const std::vector& array_value); + static std::vector DecodeArray( + nanopb::Reader* reader, + const google_firestore_v1_ArrayValue& array_proto); + private: std::unique_ptr DecodeFoundDocument( nanopb::Reader* reader, diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc index 307a36e9e33..ec1c410a636 100644 --- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc @@ -232,6 +232,16 @@ class SerializerTest : public ::testing::Test { return proto; } + v1::Value ValueProto(double d) { + std::vector bytes = + EncodeFieldValue(&serializer, FieldValue::FromDouble(d)); + v1::Value proto; + bool ok = + proto.ParseFromArray(bytes.data(), static_cast(bytes.size())); + EXPECT_TRUE(ok); + return proto; + } + v1::Value ValueProto(const char* s) { return ValueProto(std::string(s)); } @@ -256,6 +266,36 @@ class SerializerTest : public ::testing::Test { return proto; } + v1::Value ValueProto(const std::vector& blob) { + std::vector bytes = EncodeFieldValue( + &serializer, FieldValue::FromBlob(blob.data(), blob.size())); + v1::Value proto; + bool ok = + proto.ParseFromArray(bytes.data(), static_cast(bytes.size())); + EXPECT_TRUE(ok); + return proto; + } + + v1::Value ValueProto(const GeoPoint& geo_point) { + std::vector bytes = + EncodeFieldValue(&serializer, FieldValue::FromGeoPoint(geo_point)); + v1::Value proto; + bool ok = + proto.ParseFromArray(bytes.data(), static_cast(bytes.size())); + EXPECT_TRUE(ok); + return proto; + } + + v1::Value ValueProto(const std::vector& array) { + std::vector bytes = + EncodeFieldValue(&serializer, FieldValue::FromArray(array)); + v1::Value proto; + bool ok = + proto.ParseFromArray(bytes.data(), static_cast(bytes.size())); + EXPECT_TRUE(ok); + return proto; + } + /** * Creates entries in the proto that we don't care about. * @@ -424,6 +464,40 @@ TEST_F(SerializerTest, EncodesIntegers) { } } +TEST_F(SerializerTest, EncodesDoubles) { + // Not technically required at all. But if we run into a platform where this + // is false, then we'll have to eliminate a few of our test cases in this + // test. + static_assert(std::numeric_limits::is_iec559, + "IEC559/IEEE764 floating point required"); + + std::vector cases{-std::numeric_limits::infinity(), + std::numeric_limits::lowest(), + std::numeric_limits::min() - 1.0, + -2.0, + -1.1, + -1.0, + -std::numeric_limits::epsilon(), + -std::numeric_limits::min(), + -std::numeric_limits::denorm_min(), + -0.0, + 0.0, + std::numeric_limits::denorm_min(), + std::numeric_limits::min(), + std::numeric_limits::epsilon(), + 1.0, + 1.1, + 2.0, + std::numeric_limits::max() + 1.0, + std::numeric_limits::max(), + std::numeric_limits::infinity()}; + + for (double double_value : cases) { + FieldValue model = FieldValue::FromDouble(double_value); + ExpectRoundTrip(model, ValueProto(double_value), FieldValue::Type::Double); + } +} + TEST_F(SerializerTest, EncodesString) { std::vector cases{ "", @@ -464,6 +538,54 @@ TEST_F(SerializerTest, EncodesTimestamps) { } } +TEST_F(SerializerTest, EncodesBlobs) { + std::vector> cases{ + {}, + {0, 1, 2, 3}, + {0xff, 0x00, 0xff, 0x00}, + }; + + for (const std::vector& blob_value : cases) { + FieldValue model = + FieldValue::FromBlob(blob_value.data(), blob_value.size()); + ExpectRoundTrip(model, ValueProto(blob_value), FieldValue::Type::Blob); + } +} + +TEST_F(SerializerTest, EncodesGeoPoint) { + std::vector cases{ + {1.23, 4.56}, + }; + + for (const GeoPoint& geo_value : cases) { + FieldValue model = FieldValue::FromGeoPoint(geo_value); + ExpectRoundTrip(model, ValueProto(geo_value), FieldValue::Type::GeoPoint); + } +} + +TEST_F(SerializerTest, EncodesArray) { + std::vector> cases{ + // Empty Array. + {}, + // Typical Array. + {FieldValue::FromBoolean(true), FieldValue::FromString("foo")}, + // Nested Array. NB: the protos explicitly state that directly nested + // arrays are not allowed, however arrays *can* contain a map which + // contains another array. + {FieldValue::FromString("foo"), + FieldValue::FromMap( + {{"nested array", + FieldValue::FromArray( + {FieldValue::FromString("nested array value 1"), + FieldValue::FromString("nested array value 2")})}}), + FieldValue::FromString("bar")}}; + + for (const std::vector& array_value : cases) { + FieldValue model = FieldValue::FromArray(array_value); + ExpectRoundTrip(model, ValueProto(array_value), FieldValue::Type::Array); + } +} + TEST_F(SerializerTest, EncodesEmptyMap) { FieldValue model = FieldValue::EmptyObject(); @@ -476,13 +598,13 @@ TEST_F(SerializerTest, EncodesEmptyMap) { TEST_F(SerializerTest, EncodesNestedObjects) { FieldValue model = FieldValue::FromMap({ {"b", FieldValue::True()}, - // TODO(rsgowman): add doubles (once they're supported) - // {"d", FieldValue::DoubleValue(std::numeric_limits::max())}, + {"d", FieldValue::FromDouble(std::numeric_limits::max())}, {"i", FieldValue::FromInteger(1)}, {"n", FieldValue::Null()}, {"s", FieldValue::FromString("foo")}, - // TODO(rsgowman): add arrays (once they're supported) - // {"a", [2, "bar", {"b", false}]}, + {"a", FieldValue::FromArray( + {FieldValue::FromInteger(2), FieldValue::FromString("bar"), + FieldValue::FromMap({{"b", FieldValue::False()}})})}, {"o", FieldValue::FromMap({ {"d", FieldValue::FromInteger(100)}, {"nested", FieldValue::FromMap({ @@ -506,13 +628,24 @@ TEST_F(SerializerTest, EncodesNestedObjects) { (*middle_fields)["d"] = ValueProto(int64_t{100}); (*middle_fields)["nested"] = inner_proto; + v1::Value array_proto; + *array_proto.mutable_array_value()->add_values() = ValueProto(int64_t{2}); + *array_proto.mutable_array_value()->add_values() = ValueProto("bar"); + v1::Value array_inner_proto; + google::protobuf::Map* array_inner_fields = + array_inner_proto.mutable_map_value()->mutable_fields(); + (*array_inner_fields)["b"] = ValueProto(false); + *array_proto.mutable_array_value()->add_values() = array_inner_proto; + v1::Value proto; google::protobuf::Map* fields = proto.mutable_map_value()->mutable_fields(); (*fields)["b"] = ValueProto(true); + (*fields)["d"] = ValueProto(std::numeric_limits::max()); (*fields)["i"] = ValueProto(int64_t{1}); (*fields)["n"] = ValueProto(nullptr); (*fields)["s"] = ValueProto("foo"); + (*fields)["a"] = array_proto; (*fields)["o"] = middle_proto; ExpectRoundTrip(model, proto, FieldValue::Type::Object); From c6da7d616149e171715ac85403095853a10bbcfc Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Fri, 22 Mar 2019 14:50:15 -0400 Subject: [PATCH 074/214] GULLogger - issue count synchronous getters (#2610) --- .../Example/Tests/Logger/GULLoggerTest.m | 113 ++++++++---------- GoogleUtilities/Logger/GULLogger.m | 69 +++++++---- GoogleUtilities/Logger/Private/GULLogger.h | 6 +- 3 files changed, 95 insertions(+), 93 deletions(-) diff --git a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m index a9e0486cf94..35109e71518 100644 --- a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m +++ b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m @@ -32,15 +32,15 @@ extern BOOL getGULLoggerDebugMode(void); -extern NSUserDefaults *getGULLoggerUsetDefaults(void); +extern CFStringRef getGULLoggerUsetDefaultsSuiteName(void); +extern dispatch_queue_t getGULLoggerCounterQueue(void); static NSString *const kMessageCode = @"I-COR000001"; @interface GULLoggerTest : XCTestCase @property(nonatomic) NSString *randomLogString; - -@property(nonatomic, strong) NSUserDefaults *defaults; +@property(nonatomic) NSUserDefaults *loggerDefaults; @end @@ -50,14 +50,18 @@ - (void)setUp { [super setUp]; GULResetLogger(); - // Stub NSUserDefaults for cleaner testing. - _defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.google.logger_test"]; + self.loggerDefaults = [[NSUserDefaults alloc] + initWithSuiteName:CFBridgingRelease(getGULLoggerUsetDefaultsSuiteName())]; } - (void)tearDown { - [super tearDown]; + // Make sure all async operations have finished before starting a new test. + [self drainQueue:getGULClientQueue()]; + [self drainQueue:getGULLoggerCounterQueue()]; - _defaults = nil; + self.loggerDefaults = nil; + + [super tearDown]; } - (void)testMessageCodeFormat { @@ -158,93 +162,58 @@ - (void)testGULLoggerLevelValues { - (void)testGetErrorWarningNumberBeforeLogDontCrash { GULResetLogger(); - XCTestExpectation *getErrorCountExpectation = - [self expectationWithDescription:@"getErrorCountExpectation"]; - XCTestExpectation *getWarningsCountExpectation = - [self expectationWithDescription:@"getWarningsCountExpectation"]; - - GULNumberOfErrorsLogged(^(NSInteger count) { - [getErrorCountExpectation fulfill]; - }); - - GULNumberOfWarningsLogged(^(NSInteger count) { - [getWarningsCountExpectation fulfill]; - }); - - [self waitForExpectations:@[ getErrorCountExpectation, getWarningsCountExpectation ] - timeout:0.5 - enforceOrder:YES]; + XCTAssertNoThrow(GULNumberOfErrorsLogged()); + XCTAssertNoThrow(GULNumberOfWarningsLogged()); } - (void)testErrorNumberIncrement { - [getGULLoggerUsetDefaults() setInteger:10 forKey:kGULLoggerErrorCountKey]; + [self.loggerDefaults setInteger:10 forKey:kGULLoggerErrorCountKey]; GULLogError(@"my service", NO, kMessageCode, @"Message."); - XCTestExpectation *getErrorCountExpectation = - [self expectationWithDescription:@"getErrorCountExpectation"]; - - GULNumberOfErrorsLogged(^(NSInteger count) { - [getErrorCountExpectation fulfill]; - XCTAssertEqual(count, 11); - }); - - [self waitForExpectationsWithTimeout:0.5 handler:NULL]; + [self drainQueue:getGULLoggerCounterQueue()]; + XCTAssertEqual(GULNumberOfErrorsLogged(), 11); } - (void)testWarningNumberIncrement { - [getGULLoggerUsetDefaults() setInteger:5 forKey:kGULLoggerWarningCountKey]; + [self.loggerDefaults setInteger:5 forKey:kGULLoggerWarningCountKey]; GULLogWarning(@"my service", NO, kMessageCode, @"Message."); - XCTestExpectation *getWarningsCountExpectation = - [self expectationWithDescription:@"getWarningsCountExpectation"]; - - GULNumberOfWarningsLogged(^(NSInteger count) { - [getWarningsCountExpectation fulfill]; - XCTAssertEqual(count, 6); - }); - - [self waitForExpectationsWithTimeout:0.5 handler:NULL]; + [self drainQueue:getGULLoggerCounterQueue()]; + XCTAssertEqual(GULNumberOfWarningsLogged(), 6); } - (void)testResetIssuesCount { - [getGULLoggerUsetDefaults() setInteger:3 forKey:kGULLoggerErrorCountKey]; - [getGULLoggerUsetDefaults() setInteger:4 forKey:kGULLoggerWarningCountKey]; + [self.loggerDefaults setInteger:3 forKey:kGULLoggerErrorCountKey]; + [self.loggerDefaults setInteger:4 forKey:kGULLoggerWarningCountKey]; GULResetNumberOfIssuesLogged(); - XCTestExpectation *getErrorCountExpectation = - [self expectationWithDescription:@"getErrorCountExpectation"]; - XCTestExpectation *getWarningsCountExpectation = - [self expectationWithDescription:@"getWarningsCountExpectation"]; - - GULNumberOfErrorsLogged(^(NSInteger count) { - [getErrorCountExpectation fulfill]; - XCTAssertEqual(count, 0); - }); - - GULNumberOfWarningsLogged(^(NSInteger count) { - [getWarningsCountExpectation fulfill]; - XCTAssertEqual(count, 0); - }); + XCTAssertEqual(GULNumberOfErrorsLogged(), 0); + XCTAssertEqual(GULNumberOfWarningsLogged(), 0); +} - [self waitForExpectations:@[ getErrorCountExpectation, getWarningsCountExpectation ] - timeout:0.5 - enforceOrder:YES]; +- (void)testNumberOfIssuesLoggedNoDeadlock { + [self dispatchSyncNestedDispatchCount:100 + queue:getGULLoggerCounterQueue() + block:^{ + XCTAssertNoThrow(GULNumberOfErrorsLogged()); + XCTAssertNoThrow(GULNumberOfWarningsLogged()); + }]; } // Helper functions. - (BOOL)logExists { - [self drainGULClientQueue]; + [self drainQueue:getGULClientQueue()]; NSString *correctMsg = [NSString stringWithFormat:@"%@[%@] %@", @"my service", kMessageCode, self.randomLogString]; return [self messageWasLogged:correctMsg]; } -- (void)drainGULClientQueue { +- (void)drainQueue:(dispatch_queue_t)queue { dispatch_semaphore_t workerSemaphore = dispatch_semaphore_create(0); - dispatch_async(getGULClientQueue(), ^{ + dispatch_barrier_async(queue, ^{ dispatch_semaphore_signal(workerSemaphore); }); dispatch_semaphore_wait(workerSemaphore, DISPATCH_TIME_FOREVER); @@ -272,5 +241,19 @@ - (BOOL)messageWasLogged:(NSString *)message { #pragma clang pop } +- (void)dispatchSyncNestedDispatchCount:(NSInteger)count + queue:(dispatch_queue_t)queue + block:(dispatch_block_t)block { + if (count < 0) { + return; + } + + dispatch_sync(queue, ^{ + [self dispatchSyncNestedDispatchCount:count - 1 queue:queue block:block]; + block(); + NSLog(@"%@, depth: %ld", NSStringFromSelector(_cmd), (long)count); + }); +} + @end #endif diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index 4e8feafce8f..7a88af8cf5e 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -203,23 +203,41 @@ void GULLogBasic(GULLoggerLevel level, #undef GUL_MAKE_LOGGER -#pragma mark - Number of errors and warnings +#pragma mark - User defaults -NSUserDefaults *getGULLoggerUsetDefaults(void) { - static NSUserDefaults *_userDefaults = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"GoogleUtilities.Logger.GULLogger"]; - }); +// NSUserDefaults cannot be used due to a bug described in GULUserDefaults +// GULUserDefaults cannot be used because GULLogger is a dependency for GULUserDefaults +// We have to use C API deireclty here - return _userDefaults; +CFStringRef getGULLoggerUsetDefaultsSuiteName(void) { + return (__bridge CFStringRef) @"GoogleUtilities.Logger.GULLogger"; } +NSInteger GULGetUserDefaultsIntegerForKey(NSString *key) { + id value = (__bridge_transfer id)CFPreferencesCopyAppValue((__bridge CFStringRef)key, + getGULLoggerUsetDefaultsSuiteName()); + if (![value isKindOfClass:[NSNumber class]]) { + return 0; + } + + return [(NSNumber *)value integerValue]; +} + +void GULLoggerUserDefaultsSetIntegerForKey(NSInteger count, NSString *key) { + NSNumber *countNumber = @(count); + CFPreferencesSetAppValue((__bridge CFStringRef)key, (__bridge CFNumberRef)countNumber, + getGULLoggerUsetDefaultsSuiteName()); + CFPreferencesAppSynchronize(getGULLoggerUsetDefaultsSuiteName()); +} + +#pragma mark - Number of errors and warnings + dispatch_queue_t getGULLoggerCounterQueue(void) { static dispatch_queue_t queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("GoogleUtilities.GULLogger.counterQueue", DISPATCH_QUEUE_SERIAL); + queue = + dispatch_queue_create("GoogleUtilities.GULLogger.counterQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); }); @@ -227,38 +245,37 @@ dispatch_queue_t getGULLoggerCounterQueue(void) { return queue; } -void GULAsyncGetUserDefaultsIntegerForKey(NSString *key, void (^completion)(NSInteger)) { - dispatch_async(getGULLoggerCounterQueue(), ^{ - NSInteger count = [getGULLoggerUsetDefaults() integerForKey:key]; - dispatch_async(dispatch_get_main_queue(), ^{ - completion(count); - }); +NSInteger GULSyncGetUserDefaultsIntegerForKey(NSString *key) { + __block NSInteger integerValue = 0; + dispatch_sync(getGULLoggerCounterQueue(), ^{ + integerValue = GULGetUserDefaultsIntegerForKey(key); }); + + return integerValue; } -void GULNumberOfErrorsLogged(void (^completion)(NSInteger)) { - GULAsyncGetUserDefaultsIntegerForKey(kGULLoggerErrorCountKey, completion); +NSInteger GULNumberOfErrorsLogged(void) { + return GULSyncGetUserDefaultsIntegerForKey(kGULLoggerErrorCountKey); } -void GULNumberOfWarningsLogged(void (^completion)(NSInteger)) { - GULAsyncGetUserDefaultsIntegerForKey(kGULLoggerWarningCountKey, completion); +NSInteger GULNumberOfWarningsLogged(void) { + return GULSyncGetUserDefaultsIntegerForKey(kGULLoggerWarningCountKey); } void GULResetNumberOfIssuesLogged(void) { - dispatch_async(getGULLoggerCounterQueue(), ^{ - [getGULLoggerUsetDefaults() setInteger:0 forKey:kGULLoggerErrorCountKey]; - [getGULLoggerUsetDefaults() setInteger:0 forKey:kGULLoggerWarningCountKey]; + dispatch_barrier_async(getGULLoggerCounterQueue(), ^{ + GULLoggerUserDefaultsSetIntegerForKey(0, kGULLoggerErrorCountKey); + GULLoggerUserDefaultsSetIntegerForKey(0, kGULLoggerWarningCountKey); }); } void GULIncrementUserDefaultsIntegerForKey(NSString *key) { - NSUserDefaults *defaults = getGULLoggerUsetDefaults(); - NSInteger errorCount = [defaults integerForKey:key]; - [defaults setInteger:errorCount + 1 forKey:key]; + NSInteger value = GULGetUserDefaultsIntegerForKey(key); + GULLoggerUserDefaultsSetIntegerForKey(value + 1, key); } void GULIncrementLogCountForLevel(GULLoggerLevel level) { - dispatch_async(getGULLoggerCounterQueue(), ^{ + dispatch_barrier_async(getGULLoggerCounterQueue(), ^{ if (level == GULLoggerLevelError) { GULIncrementUserDefaultsIntegerForKey(kGULLoggerErrorCountKey); } else if (level == GULLoggerLevelWarning) { diff --git a/GoogleUtilities/Logger/Private/GULLogger.h b/GoogleUtilities/Logger/Private/GULLogger.h index 453de4bd955..167f24c4caa 100644 --- a/GoogleUtilities/Logger/Private/GULLogger.h +++ b/GoogleUtilities/Logger/Private/GULLogger.h @@ -141,13 +141,15 @@ extern void GULLogDebug(GULLoggerService service, /** * Retrieve the number of errors that have been logged since the stat was last reset. + * Calling this method can be comparably expensive, so it should not be called from main thread. */ -extern void GULNumberOfErrorsLogged(void (^completion)(NSInteger)); +extern NSInteger GULNumberOfErrorsLogged(void); /** * Retrieve the number of warnings that have been logged since the stat was last reset. + * Calling this method can be comparably expensive, so it should not be called from main thread. */ -extern void GULNumberOfWarningsLogged(void (^completion)(NSInteger)); +extern NSInteger GULNumberOfWarningsLogged(void); /** * Reset number of errors and warnings that have been logged to 0. From b34a2780bd21bd85992a33952ae44f515fb2b904 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Fri, 22 Mar 2019 13:45:02 -0700 Subject: [PATCH 075/214] Ensure token refresh notification is triggered any time a new token is refreshed. b/77662386 (#2562) --- Example/InstanceID/Tests/FIRInstanceIDTest.m | 11 +- Firebase/InstanceID/FIRInstanceID+Testing.h | 6 -- Firebase/InstanceID/FIRInstanceID.m | 102 ++++++------------- 3 files changed, 36 insertions(+), 83 deletions(-) diff --git a/Example/InstanceID/Tests/FIRInstanceIDTest.m b/Example/InstanceID/Tests/FIRInstanceIDTest.m index c3a857f14a4..6895abf8401 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDTest.m @@ -52,6 +52,7 @@ - (void)didCompleteConfigure; - (NSString *)cachedTokenIfAvailable; - (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler; + (FIRInstanceID *)instanceIDForTests; +- (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler; @end @interface FIRInstanceIDTest : XCTestCase @@ -157,7 +158,7 @@ - (void)testTokenShouldBeRefreshedIfCacheTokenNeedsToBeRefreshed { handler:[OCMArg any]]; [self.mockInstanceID didCompleteConfigure]; - OCMVerify([self.mockInstanceID fetchDefaultToken]); + OCMVerify([self.mockInstanceID defaultTokenWithHandler:nil]); XCTAssertEqualObjects([self.mockInstanceID token], kToken); } @@ -172,7 +173,7 @@ - (void)testTokenShouldBeRefreshedIfNoCacheTokenButAutoInitAllowed { [self.mockInstanceID didCompleteConfigure]; - OCMVerify([self.mockInstanceID fetchDefaultToken]); + OCMVerify([self.mockInstanceID defaultTokenWithHandler:nil]); } - (void)testTokenIsDeletedAlongWithIdentity { @@ -519,11 +520,11 @@ - (void)testDefaultToken_noCachedToken { cachedTokenInfoWithAuthorizedEntity:kAuthorizedEntity scope:@"*"]; - OCMExpect([self.mockInstanceID fetchDefaultToken]); + OCMExpect([self.mockInstanceID defaultTokenWithHandler:nil]); NSString *token = [self.mockInstanceID token]; XCTAssertNil(token); [self.mockInstanceID stopMocking]; - OCMVerify([self.mockInstanceID fetchDefaultToken]); + OCMVerify([self.mockInstanceID defaultTokenWithHandler:nil]); } /** @@ -535,7 +536,7 @@ - (void)testDefaultToken_validCachedToken { cachedTokenInfoWithAuthorizedEntity:kAuthorizedEntity scope:@"*"]; - [[self.mockInstanceID reject] fetchDefaultToken]; + [[self.mockInstanceID reject] defaultTokenWithHandler:nil]; NSString *token = [self.mockInstanceID token]; XCTAssertEqualObjects(token, kToken); } diff --git a/Firebase/InstanceID/FIRInstanceID+Testing.h b/Firebase/InstanceID/FIRInstanceID+Testing.h index 88fabf216d6..ba24926fc4f 100644 --- a/Firebase/InstanceID/FIRInstanceID+Testing.h +++ b/Firebase/InstanceID/FIRInstanceID+Testing.h @@ -35,12 +35,6 @@ */ - (void)start; -/** - * Without checking any caches etc, always attempts to fetch the default token (unless a fetch - * is already in progress. - */ -- (void)fetchDefaultToken; - + (int64_t)maxRetryCountForDefaultToken; + (int64_t)minIntervalForDefaultTokenRetry; + (int64_t)maxRetryIntervalForDefaultTokenInSeconds; diff --git a/Firebase/InstanceID/FIRInstanceID.m b/Firebase/InstanceID/FIRInstanceID.m index c8efba974e6..c5ae18e373b 100644 --- a/Firebase/InstanceID/FIRInstanceID.m +++ b/Firebase/InstanceID/FIRInstanceID.m @@ -190,7 +190,7 @@ - (NSString *)token { // If we've never had a cached default token, we should fetch one because unrelatedly, // this request will help us determine whether the locally-generated Instance ID keypair is not // unique, and therefore generate a new one. - [self fetchDefaultToken]; + [self defaultTokenWithHandler:nil]; return nil; } } @@ -217,40 +217,16 @@ - (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler { // If no handler, simply return since client has generated iid and token. return; } - - // Now get token - FIRInstanceIDTokenHandler tokenHandler = ^void(NSString *token, NSError *error) { - if (error) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007, - @"Failed to retrieve the default FCM token after %ld retries", - (long)self.retryCountForDefaultToken); - if (handler) { - // If token fetching fails, result should be nil with error returned. + [self defaultTokenWithHandler:^(NSString *_Nullable token, NSError *_Nullable error) { + if (handler) { + if (error) { handler(nil, error); + return; } - return; - } - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@", - token); - NSString *previousFCMToken = self.defaultFCMToken; - self.defaultFCMToken = token; - - // Only notify of token refresh if we have a new valid token that's different than before - if (self.defaultFCMToken.length && ![self.defaultFCMToken isEqualToString:previousFCMToken]) { - NSNotification *tokenRefreshNotification = - [NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification - object:[self.defaultFCMToken copy]]; - [[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification - postingStyle:NSPostASAP]; - } - - if (handler) { result.token = token; handler(result, nil); } - }; - - [self defaultTokenWithHandler:tokenHandler]; + }]; }]; } @@ -627,7 +603,7 @@ - (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self fetchDefaultToken]; + [self defaultTokenWithHandler:nil]; }); } if (handler) { @@ -727,7 +703,7 @@ - (void)didCompleteConfigure { // Clean up expired tokens by checking the token refresh policy. if ([self.tokenManager checkForTokenRefreshPolicy]) { // Default token is expired, fetch default token from server. - [self fetchDefaultToken]; + [self defaultTokenWithHandler:nil]; } // Notify FCM with the default token. #pragma clang diagnostic push @@ -738,7 +714,7 @@ - (void)didCompleteConfigure { // When there is no cached token, must check auto init is enabled. // If it's disabled, don't initiate token generation/refresh. // If no cache token and auto init is enabled, fetch a token from server. - [self fetchDefaultToken]; + [self defaultTokenWithHandler:nil]; // Notify FCM with the default token. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -855,40 +831,6 @@ - (NSInteger)retryIntervalToFetchDefaultToken { kMaxRetryIntervalForDefaultTokenInSeconds); } -- (void)fetchDefaultToken { - if (self.isFetchingDefaultToken) { - return; - } - - FIRInstanceID_WEAKIFY(self); - FIRInstanceIDTokenHandler handler = ^void(NSString *token, NSError *error) { - FIRInstanceID_STRONGIFY(self); - - if (error) { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007, - @"Failed to retrieve the default FCM token after %ld retries", - (long)self.retryCountForDefaultToken); - } else { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@", - token); - NSString *previousFCMToken = self.defaultFCMToken; - self.defaultFCMToken = token; - - // Only notify of token refresh if we have a new valid token that's different than before - if (self.defaultFCMToken.length && ![self.defaultFCMToken isEqualToString:previousFCMToken]) { - NSNotification *tokenRefreshNotification = - [NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification - object:[self.defaultFCMToken copy]]; - [[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification - postingStyle:NSPostASAP]; - } - } - }; - - // Get a "*" token using this APNS token. - [self defaultTokenWithHandler:handler]; -} - - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { if (self.isFetchingDefaultToken || self.isDefaultTokenFetchScheduled) { return; @@ -938,6 +880,9 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { [self defaultTokenWithHandler:handler]; }); } else { + FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007, + @"Failed to retrieve the default FCM token after %ld retries", + (long)self.retryCountForDefaultToken); if (handler) { handler(nil, error); } @@ -945,7 +890,6 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { } else { // If somebody updated IID with APNS token while our initial request did not have it // set we need to update it on the server. - BOOL shouldNotifyHandler = YES; NSData *deviceTokenInRequest = instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSKey]; BOOL isSandboxInRequest = [instanceIDOptions[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey] boolValue]; @@ -961,8 +905,6 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { // APNs value did change mid-fetch, so the token should be re-fetched with the current APNs // value. self.isDefaultTokenFetchScheduled = YES; - // Wait to notify until we can modify this token with APNS (or receive a new token) - shouldNotifyHandler = NO; FIRInstanceID_WEAKIFY(self); dispatch_async(dispatch_get_main_queue(), ^{ FIRInstanceID_STRONGIFY(self); @@ -972,12 +914,28 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeRefetchingTokenForAPNS, @"Received APNS token while fetching default token. " @"Refetching default token."); + // Do not notify and handle completion handler since this is a retry. + // Simply return. + return; } else { FIRInstanceIDLoggerInfo(kFIRInstanceIDMessageCodeInstanceID010, @"Successfully fetched default token."); } // Post the required notifications if somebody is waiting. - if (shouldNotifyHandler && handler) { + FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeInstanceID008, @"Got default token %@", + token); + NSString *previousFCMToken = self.defaultFCMToken; + self.defaultFCMToken = token; + + // Only notify of token refresh if we have a new valid token that's different than before + if (self.defaultFCMToken.length && ![self.defaultFCMToken isEqualToString:previousFCMToken]) { + NSNotification *tokenRefreshNotification = + [NSNotification notificationWithName:kFIRInstanceIDTokenRefreshNotification + object:[self.defaultFCMToken copy]]; + [[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification + postingStyle:NSPostASAP]; + } + if (handler) { handler(token, nil); } } @@ -1040,7 +998,7 @@ - (void)notifyAPNSTokenIsSet:(NSNotification *)notification { if ([tokenInfo.token isEqualToString:self.defaultFCMToken]) { // We will perform a special fetch for the default FCM token, so that the delegate methods // are called. For all others, we will do an internal re-fetch. - [self fetchDefaultToken]; + [self defaultTokenWithHandler:nil]; } else { [self.tokenManager fetchNewTokenWithAuthorizedEntity:tokenInfo.authorizedEntity scope:tokenInfo.scope From 77215913c3d3fcd4f0fda1dcf7ad38a01b5c58a5 Mon Sep 17 00:00:00 2001 From: Gil Date: Fri, 22 Mar 2019 13:46:48 -0700 Subject: [PATCH 076/214] Port FSTLocalDocumentsView to C++ (#2489) * Port FSTLocalDocumentsView to C++. * Replace callers of FSTLocalDocumentsView * Remove FSTLocalDocumentsView --- .../Source/Local/FSTLocalDocumentsView.h | 74 ------ .../Source/Local/FSTLocalDocumentsView.mm | 245 ------------------ Firestore/Source/Local/FSTLocalStore.mm | 41 +-- .../firebase/firestore/local/CMakeLists.txt | 2 + .../firestore/local/local_documents_view.h | 117 +++++++++ .../firestore/local/local_documents_view.mm | 222 ++++++++++++++++ 6 files changed, 362 insertions(+), 339 deletions(-) delete mode 100644 Firestore/Source/Local/FSTLocalDocumentsView.h delete mode 100644 Firestore/Source/Local/FSTLocalDocumentsView.mm create mode 100644 Firestore/core/src/firebase/firestore/local/local_documents_view.h create mode 100644 Firestore/core/src/firebase/firestore/local/local_documents_view.mm diff --git a/Firestore/Source/Local/FSTLocalDocumentsView.h b/Firestore/Source/Local/FSTLocalDocumentsView.h deleted file mode 100644 index 1072b1b63b1..00000000000 --- a/Firestore/Source/Local/FSTLocalDocumentsView.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 - -#include "Firestore/core/src/firebase/firestore/local/index_manager.h" -#include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" -#include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h" -#include "Firestore/core/src/firebase/firestore/model/document_key.h" -#include "Firestore/core/src/firebase/firestore/model/document_key_set.h" -#include "Firestore/core/src/firebase/firestore/model/document_map.h" - -@class FSTMaybeDocument; -@class FSTQuery; - -NS_ASSUME_NONNULL_BEGIN - -/** - * A readonly view of the local state of all documents we're tracking (i.e. we have a cached - * version in remoteDocumentCache or local mutations for the document). The view is computed by - * applying the mutations in the FSTMutationQueue to the FSTRemoteDocumentCache. - */ -@interface FSTLocalDocumentsView : NSObject - -+ (instancetype) - viewWithRemoteDocumentCache: - (firebase::firestore::local::RemoteDocumentCache *)remoteDocumentCache - mutationQueue:(firebase::firestore::local::MutationQueue *)mutationQueue - indexManager:(firebase::firestore::local::IndexManager *)indexManager; - -- (instancetype)init __attribute__((unavailable("Use a static constructor"))); - -/** - * Get the local view of the document identified by `key`. - * - * @return Local view of the document or nil if we don't have any cached state for it. - */ -- (nullable FSTMaybeDocument *)documentForKey:(const firebase::firestore::model::DocumentKey &)key; - -/** - * Gets the local view of the documents identified by `keys`. - * - * If we don't have cached state for a document in `keys`, a FSTDeletedDocument will be stored - * for that key in the resulting set. - */ -- (firebase::firestore::model::MaybeDocumentMap)documentsForKeys: - (const firebase::firestore::model::DocumentKeySet &)keys; - -/** - * Similar to `documentsForKeys`, but creates the local view from the given - * `baseDocs` without retrieving documents from the local store. - */ -- (firebase::firestore::model::MaybeDocumentMap)localViewsForDocuments: - (const firebase::firestore::model::MaybeDocumentMap &)baseDocs; - -/** Performs a query against the local view of all documents. */ -- (firebase::firestore::model::DocumentMap)documentsMatchingQuery:(FSTQuery *)query; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTLocalDocumentsView.mm b/Firestore/Source/Local/FSTLocalDocumentsView.mm deleted file mode 100644 index 23a38276891..00000000000 --- a/Firestore/Source/Local/FSTLocalDocumentsView.mm +++ /dev/null @@ -1,245 +0,0 @@ -/* - * 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 "Firestore/Source/Local/FSTLocalDocumentsView.h" - -#include -#include - -#import "Firestore/Source/Core/FSTQuery.h" -#import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTMutation.h" -#import "Firestore/Source/Model/FSTMutationBatch.h" - -#include "Firestore/core/src/firebase/firestore/local/index_manager.h" -#include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" -#include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h" -#include "Firestore/core/src/firebase/firestore/model/document_key.h" -#include "Firestore/core/src/firebase/firestore/model/document_map.h" -#include "Firestore/core/src/firebase/firestore/model/resource_path.h" -#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" -#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" -#include "Firestore/core/src/firebase/firestore/util/string_apple.h" - -using firebase::firestore::local::IndexManager; -using firebase::firestore::local::MutationQueue; -using firebase::firestore::local::RemoteDocumentCache; -using firebase::firestore::model::DocumentKey; -using firebase::firestore::model::DocumentKeySet; -using firebase::firestore::model::DocumentMap; -using firebase::firestore::model::MaybeDocumentMap; -using firebase::firestore::model::ResourcePath; -using firebase::firestore::model::SnapshotVersion; -using firebase::firestore::util::MakeString; - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTLocalDocumentsView () -- (instancetype)initWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache - mutationQueue:(MutationQueue *)mutationQueue - indexManager:(IndexManager *)indexManager NS_DESIGNATED_INITIALIZER; - -@end - -@implementation FSTLocalDocumentsView { - RemoteDocumentCache *_remoteDocumentCache; - MutationQueue *_mutationQueue; - IndexManager *_indexManager; -} - -+ (instancetype)viewWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache - mutationQueue:(MutationQueue *)mutationQueue - indexManager:(IndexManager *)indexManager { - return [[FSTLocalDocumentsView alloc] initWithRemoteDocumentCache:remoteDocumentCache - mutationQueue:mutationQueue - indexManager:indexManager]; -} - -- (instancetype)initWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache - mutationQueue:(MutationQueue *)mutationQueue - indexManager:(IndexManager *)indexManager { - if (self = [super init]) { - _remoteDocumentCache = remoteDocumentCache; - _mutationQueue = mutationQueue; - _indexManager = indexManager; - } - return self; -} - -- (nullable FSTMaybeDocument *)documentForKey:(const DocumentKey &)key { - std::vector batches = - _mutationQueue->AllMutationBatchesAffectingDocumentKey(key); - return [self documentForKey:key inBatches:batches]; -} - -// Internal version of documentForKey: which allows reusing `batches`. -- (nullable FSTMaybeDocument *)documentForKey:(const DocumentKey &)key - inBatches:(const std::vector &)batches { - FSTMaybeDocument *_Nullable document = _remoteDocumentCache->Get(key); - for (FSTMutationBatch *batch : batches) { - document = [batch applyToLocalDocument:document documentKey:key]; - } - - return document; -} - -// Returns the view of the given `docs` as they would appear after applying all -// mutations in the given `batches`. -- (MaybeDocumentMap)applyLocalMutationsToDocuments:(const MaybeDocumentMap &)docs - fromBatches: - (const std::vector &)batches { - MaybeDocumentMap results; - - for (const auto &kv : docs) { - const DocumentKey &key = kv.first; - FSTMaybeDocument *localView = kv.second; - for (FSTMutationBatch *batch : batches) { - localView = [batch applyToLocalDocument:localView documentKey:key]; - } - results = results.insert(key, localView); - } - return results; -} - -- (MaybeDocumentMap)documentsForKeys:(const DocumentKeySet &)keys { - MaybeDocumentMap docs = _remoteDocumentCache->GetAll(keys); - return [self localViewsForDocuments:docs]; -} - -/** - * Similar to `documentsForKeys`, but creates the local view from the given - * `baseDocs` without retrieving documents from the local store. - */ -- (MaybeDocumentMap)localViewsForDocuments:(const MaybeDocumentMap &)baseDocs { - MaybeDocumentMap results; - - DocumentKeySet allKeys; - for (const auto &kv : baseDocs) { - allKeys = allKeys.insert(kv.first); - } - std::vector batches = - _mutationQueue->AllMutationBatchesAffectingDocumentKeys(allKeys); - - MaybeDocumentMap docs = [self applyLocalMutationsToDocuments:baseDocs fromBatches:batches]; - - for (const auto &kv : docs) { - const DocumentKey &key = kv.first; - FSTMaybeDocument *maybeDoc = kv.second; - - // TODO(http://b/32275378): Don't conflate missing / deleted. - if (!maybeDoc) { - maybeDoc = [FSTDeletedDocument documentWithKey:key - version:SnapshotVersion::None() - hasCommittedMutations:NO]; - } - results = results.insert(key, maybeDoc); - } - - return results; -} - -- (DocumentMap)documentsMatchingQuery:(FSTQuery *)query { - if ([query isDocumentQuery]) { - return [self documentsMatchingDocumentQuery:query.path]; - } else if ([query isCollectionGroupQuery]) { - return [self documentsMatchingCollectionGroupQuery:query]; - } else { - return [self documentsMatchingCollectionQuery:query]; - } -} - -- (DocumentMap)documentsMatchingDocumentQuery:(const ResourcePath &)docPath { - DocumentMap result; - // Just do a simple document lookup. - FSTMaybeDocument *doc = [self documentForKey:DocumentKey{docPath}]; - if ([doc isKindOfClass:[FSTDocument class]]) { - result = result.insert(doc.key, static_cast(doc)); - } - return result; -} - -- (DocumentMap)documentsMatchingCollectionGroupQuery:(FSTQuery *)query { - HARD_ASSERT(query.path.empty(), - "Currently we only support collection group queries at the root."); - - std::string collection_id = MakeString(query.collectionGroup); - std::vector parents = _indexManager->GetCollectionParents(collection_id); - DocumentMap results; - - // Perform a collection query against each parent that contains the collection_id and - // aggregate the results. - for (const ResourcePath &parent : parents) { - FSTQuery *collectionQuery = [query collectionQueryAtPath:parent.Append(collection_id)]; - DocumentMap collectionResults = [self documentsMatchingCollectionQuery:collectionQuery]; - for (const auto &kv : collectionResults.underlying_map()) { - const DocumentKey &key = kv.first; - FSTDocument *doc = static_cast(kv.second); - results = results.insert(key, doc); - } - } - return results; -} - -- (DocumentMap)documentsMatchingCollectionQuery:(FSTQuery *)query { - DocumentMap results = _remoteDocumentCache->GetMatching(query); - // Get locally persisted mutation batches. - std::vector matchingBatches = - _mutationQueue->AllMutationBatchesAffectingQuery(query); - - for (FSTMutationBatch *batch : matchingBatches) { - for (FSTMutation *mutation : [batch mutations]) { - // Only process documents belonging to the collection. - if (!query.path.IsImmediateParentOf(mutation.key.path())) { - continue; - } - - const DocumentKey &key = mutation.key; - // baseDoc may be nil for the documents that weren't yet written to the backend. - FSTMaybeDocument *baseDoc = nil; - auto found = results.underlying_map().find(key); - if (found != results.underlying_map().end()) { - baseDoc = found->second; - } - FSTMaybeDocument *mutatedDoc = [mutation applyToLocalDocument:baseDoc - baseDocument:baseDoc - localWriteTime:batch.localWriteTime]; - - if ([mutatedDoc isKindOfClass:[FSTDocument class]]) { - results = results.insert(key, static_cast(mutatedDoc)); - } else { - results = results.erase(key); - } - } - } - - // Finally, filter out any documents that don't actually match the query. Note that the extra - // reference here prevents ARC from deallocating the initial unfiltered results while we're - // enumerating them. - DocumentMap unfiltered = results; - for (const auto &kv : unfiltered.underlying_map()) { - const DocumentKey &key = kv.first; - FSTDocument *doc = static_cast(kv.second); - if (![query matchesDocument:doc]) { - results = results.erase(key); - } - } - - return results; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTLocalStore.mm b/Firestore/Source/Local/FSTLocalStore.mm index a5115d2e825..97d542bd37d 100644 --- a/Firestore/Source/Local/FSTLocalStore.mm +++ b/Firestore/Source/Local/FSTLocalStore.mm @@ -16,6 +16,7 @@ #import "Firestore/Source/Local/FSTLocalStore.h" +#include #include #include #include @@ -24,7 +25,6 @@ #import "FIRTimestamp.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTLRUGarbageCollector.h" -#import "Firestore/Source/Local/FSTLocalDocumentsView.h" #import "Firestore/Source/Local/FSTLocalViewChanges.h" #import "Firestore/Source/Local/FSTLocalWriteResult.h" #import "Firestore/Source/Local/FSTPersistence.h" @@ -36,17 +36,22 @@ #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/core/target_id_generator.h" #include "Firestore/core/src/firebase/firestore/immutable/sorted_set.h" +#include "Firestore/core/src/firebase/firestore/local/local_documents_view.h" #include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" #include "Firestore/core/src/firebase/firestore/local/query_cache.h" #include "Firestore/core/src/firebase/firestore/local/reference_set.h" #include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h" +#include "Firestore/core/src/firebase/firestore/model/document_key_set.h" +#include "Firestore/core/src/firebase/firestore/model/document_map.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/remote/remote_event.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" +#include "absl/memory/memory.h" using firebase::firestore::auth::User; using firebase::firestore::core::TargetIdGenerator; +using firebase::firestore::local::LocalDocumentsView; using firebase::firestore::local::LruResults; using firebase::firestore::local::MutationQueue; using firebase::firestore::local::QueryCache; @@ -82,9 +87,6 @@ @interface FSTLocalStore () /** Manages our in-memory or durable persistence. */ @property(nonatomic, strong, readonly) id persistence; -/** The "local" view of all documents (layering mutationQueue on top of remoteDocumentCache). */ -@property(nonatomic, nullable, strong) FSTLocalDocumentsView *localDocuments; - /** Maps a query to the data about that query. */ @property(nonatomic) QueryCache *queryCache; @@ -99,6 +101,9 @@ @implementation FSTLocalStore { /** The set of all mutations that have been sent but not yet been applied to the backend. */ MutationQueue *_mutationQueue; + /** The "local" view of all documents (layering mutationQueue on top of remoteDocumentCache). */ + std::unique_ptr _localDocuments; + /** The set of document references maintained by any local views. */ ReferenceSet _localViewReferences; @@ -113,10 +118,8 @@ - (instancetype)initWithPersistence:(id)persistence _mutationQueue = [persistence mutationQueueForUser:initialUser]; _remoteDocumentCache = [persistence remoteDocumentCache]; _queryCache = [persistence queryCache]; - _localDocuments = - [FSTLocalDocumentsView viewWithRemoteDocumentCache:_remoteDocumentCache - mutationQueue:_mutationQueue - indexManager:[persistence indexManager]]; + _localDocuments = absl::make_unique(_remoteDocumentCache, _mutationQueue, + [_persistence indexManager]); [_persistence.referenceDelegate addInMemoryPins:&_localViewReferences]; _targetIDGenerator = TargetIdGenerator::QueryCacheTargetIdGenerator(0); @@ -141,7 +144,7 @@ - (MaybeDocumentMap)userDidChange:(const User &)user { [&]() -> std::vector { return _mutationQueue->AllMutationBatches(); }); // The old one has a reference to the mutation queue, so nil it out first. - self.localDocuments = nil; + _localDocuments.reset(); _mutationQueue = [self.persistence mutationQueueForUser:user]; [self startMutationQueue]; @@ -150,10 +153,8 @@ - (MaybeDocumentMap)userDidChange:(const User &)user { std::vector newBatches = _mutationQueue->AllMutationBatches(); // Recreate our LocalDocumentsView using the new MutationQueue. - self.localDocuments = - [FSTLocalDocumentsView viewWithRemoteDocumentCache:_remoteDocumentCache - mutationQueue:_mutationQueue - indexManager:[_persistence indexManager]]; + _localDocuments = absl::make_unique(_remoteDocumentCache, _mutationQueue, + [_persistence indexManager]); // Union the old/new changed keys. DocumentKeySet changedKeys; @@ -166,7 +167,7 @@ - (MaybeDocumentMap)userDidChange:(const User &)user { } // Return the set of all (potentially) changed documents as the result of the user change. - return [self.localDocuments documentsForKeys:changedKeys]; + return _localDocuments->GetDocuments(changedKeys); }); } @@ -180,7 +181,7 @@ - (FSTLocalWriteResult *)locallyWriteMutations:(std::vector &&)mu return self.persistence.run("Locally write mutations", [&]() -> FSTLocalWriteResult * { // Load and apply all existing mutations. This lets us compute the current base state for // all non-idempotent transforms before applying any additional user-provided writes. - MaybeDocumentMap existingDocuments = [self.localDocuments documentsForKeys:keys]; + MaybeDocumentMap existingDocuments = _localDocuments->GetDocuments(keys); // For non-idempotent mutations (such as `FieldValue.increment()`), we record the base // state in a separate patch mutation. This is later used to guarantee consistent values @@ -230,7 +231,7 @@ - (MaybeDocumentMap)acknowledgeBatchWithResult:(FSTMutationBatchResult *)batchRe [self applyBatchResult:batchResult]; _mutationQueue->PerformConsistencyCheck(); - return [self.localDocuments documentsForKeys:batch.keys]; + return _localDocuments->GetDocuments(batch.keys); }); } @@ -242,7 +243,7 @@ - (MaybeDocumentMap)rejectBatchID:(BatchId)batchID { _mutationQueue->RemoveMutationBatch(toReject); _mutationQueue->PerformConsistencyCheck(); - return [self.localDocuments documentsForKeys:toReject.keys]; + return _localDocuments->GetDocuments(toReject.keys); }); } @@ -363,7 +364,7 @@ - (MaybeDocumentMap)applyRemoteEvent:(const RemoteEvent &)remoteEvent { _queryCache->SetLastRemoteSnapshotVersion(remoteVersion); } - return [self.localDocuments localViewsForDocuments:changedDocs]; + return _localDocuments->GetLocalViewOfDocuments(changedDocs); }); } @@ -426,7 +427,7 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(BatchId)batchID { - (nullable FSTMaybeDocument *)readDocument:(const DocumentKey &)key { return self.persistence.run("ReadDocument", [&]() -> FSTMaybeDocument *_Nullable { - return [self.localDocuments documentForKey:key]; + return _localDocuments->GetDocument(key); }); } @@ -483,7 +484,7 @@ - (void)releaseQuery:(FSTQuery *)query { - (DocumentMap)executeQuery:(FSTQuery *)query { return self.persistence.run("ExecuteQuery", [&]() -> DocumentMap { - return [self.localDocuments documentsMatchingQuery:query]; + return _localDocuments->GetDocumentsMatchingQuery(query); }); } diff --git a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt index c18f3d03dd2..82c6e9af325 100644 --- a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt @@ -58,6 +58,8 @@ cc_library( document_reference.cc index_manager.h listen_sequence.h + local_documents_view.h + local_documents_view.mm local_serializer.h local_serializer.cc memory_index_manager.cc diff --git a/Firestore/core/src/firebase/firestore/local/local_documents_view.h b/Firestore/core/src/firebase/firestore/local/local_documents_view.h new file mode 100644 index 00000000000..b4dd6501385 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/local_documents_view.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LOCAL_DOCUMENTS_VIEW_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LOCAL_DOCUMENTS_VIEW_H_ + +#import + +#include + +#include "Firestore/core/src/firebase/firestore/local/index_manager.h" +#include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" +#include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h" +#include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/document_key_set.h" +#include "Firestore/core/src/firebase/firestore/model/document_map.h" + +NS_ASSUME_NONNULL_BEGIN + +@class FSTMaybeDocument; +@class FSTMutationBatch; +@class FSTQuery; + +namespace firebase { +namespace firestore { +namespace local { + +/** + * A readonly view of the local state of all documents we're tracking (i.e. we + * have a cached version in remoteDocumentCache or local mutations for the + * document). The view is computed by applying the mutations in the + * FSTMutationQueue to the FSTRemoteDocumentCache. + */ +class LocalDocumentsView { + public: + LocalDocumentsView(RemoteDocumentCache* remote_document_cache, + MutationQueue* mutation_queue, + IndexManager* index_manager) + : remote_document_cache_{remote_document_cache}, + mutation_queue_{mutation_queue}, + index_manager_{index_manager} { + } + + /** + * Gets the local view of the document identified by `key`. + * + * @return Local view of the document or nil if we don't have any cached state + * for it. + */ + FSTMaybeDocument* _Nullable GetDocument(const model::DocumentKey& key); + + /** + * Gets the local view of the documents identified by `keys`. + * + * If we don't have cached state for a document in `keys`, a + * FSTDeletedDocument will be stored for that key in the resulting set. + */ + model::MaybeDocumentMap GetDocuments(const model::DocumentKeySet& keys); + + /** + * Similar to `documentsForKeys`, but creates the local view from the given + * `baseDocs` without retrieving documents from the local store. + */ + model::MaybeDocumentMap GetLocalViewOfDocuments( + const model::MaybeDocumentMap& base_docs); + + /** Performs a query against the local view of all documents. */ + model::DocumentMap GetDocumentsMatchingQuery(FSTQuery* query); + + private: + /** Internal version of GetDocument that allows re-using batches. */ + FSTMaybeDocument* _Nullable GetDocument( + const model::DocumentKey& key, + const std::vector& batches); + + /** + * Returns the view of the given `docs` as they would appear after applying + * all mutations in the given `batches`. + */ + model::MaybeDocumentMap ApplyLocalMutationsToDocuments( + const model::MaybeDocumentMap& docs, + const std::vector& batches); + + /** Performs a simple document lookup for the given path. */ + model::DocumentMap GetDocumentsMatchingDocumentQuery( + const model::ResourcePath& doc_path); + + model::DocumentMap GetDocumentsMatchingCollectionGroupQuery(FSTQuery* query); + + /** Queries the remote documents and overlays mutations. */ + model::DocumentMap GetDocumentsMatchingCollectionQuery(FSTQuery* query); + + RemoteDocumentCache* remote_document_cache_; + MutationQueue* mutation_queue_; + IndexManager* index_manager_; +}; + +} // namespace local +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LOCAL_DOCUMENTS_VIEW_H_ diff --git a/Firestore/core/src/firebase/firestore/local/local_documents_view.mm b/Firestore/core/src/firebase/firestore/local/local_documents_view.mm new file mode 100644 index 00000000000..c27692b231b --- /dev/null +++ b/Firestore/core/src/firebase/firestore/local/local_documents_view.mm @@ -0,0 +1,222 @@ +/* + * 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 "Firestore/core/src/firebase/firestore/local/local_documents_view.h" + +#include + +#import "Firestore/Source/Core/FSTQuery.h" +#import "Firestore/Source/Model/FSTDocument.h" +#import "Firestore/Source/Model/FSTMutation.h" +#import "Firestore/Source/Model/FSTMutationBatch.h" + +#include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" +#include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h" +#include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/document_map.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace local { + +using model::DocumentKey; +using model::DocumentKeySet; +using model::DocumentMap; +using model::MaybeDocumentMap; +using model::ResourcePath; +using model::SnapshotVersion; +using util::MakeString; + +FSTMaybeDocument* _Nullable LocalDocumentsView::GetDocument( + const DocumentKey& key) { + std::vector batches = + mutation_queue_->AllMutationBatchesAffectingDocumentKey(key); + return GetDocument(key, batches); +} + +FSTMaybeDocument* _Nullable LocalDocumentsView::GetDocument( + const DocumentKey& key, const std::vector& batches) { + FSTMaybeDocument* _Nullable document = remote_document_cache_->Get(key); + for (FSTMutationBatch* batch : batches) { + document = [batch applyToLocalDocument:document documentKey:key]; + } + + return document; +} + +MaybeDocumentMap LocalDocumentsView::ApplyLocalMutationsToDocuments( + const MaybeDocumentMap& docs, + const std::vector& batches) { + MaybeDocumentMap results; + + for (const auto& kv : docs) { + const DocumentKey& key = kv.first; + FSTMaybeDocument* local_view = kv.second; + for (FSTMutationBatch* batch : batches) { + local_view = [batch applyToLocalDocument:local_view documentKey:key]; + } + results = results.insert(key, local_view); + } + return results; +} + +MaybeDocumentMap LocalDocumentsView::GetDocuments(const DocumentKeySet& keys) { + MaybeDocumentMap docs = remote_document_cache_->GetAll(keys); + return GetLocalViewOfDocuments(docs); +} + +/** + * Similar to `documentsForKeys`, but creates the local view from the given + * `baseDocs` without retrieving documents from the local store. + */ +MaybeDocumentMap LocalDocumentsView::GetLocalViewOfDocuments( + const MaybeDocumentMap& base_docs) { + MaybeDocumentMap results; + + DocumentKeySet all_keys; + for (const auto& kv : base_docs) { + all_keys = all_keys.insert(kv.first); + } + std::vector batches = + mutation_queue_->AllMutationBatchesAffectingDocumentKeys(all_keys); + + MaybeDocumentMap docs = ApplyLocalMutationsToDocuments(base_docs, batches); + + for (const auto& kv : docs) { + const DocumentKey& key = kv.first; + FSTMaybeDocument* maybe_doc = kv.second; + + // TODO(http://b/32275378): Don't conflate missing / deleted. + if (!maybe_doc) { + maybe_doc = [FSTDeletedDocument documentWithKey:key + version:SnapshotVersion::None() + hasCommittedMutations:NO]; + } + results = results.insert(key, maybe_doc); + } + + return results; +} + +DocumentMap LocalDocumentsView::GetDocumentsMatchingQuery(FSTQuery* query) { + if ([query isDocumentQuery]) { + return GetDocumentsMatchingDocumentQuery(query.path); + } else if ([query isCollectionGroupQuery]) { + return GetDocumentsMatchingCollectionGroupQuery(query); + } else { + return GetDocumentsMatchingCollectionQuery(query); + } +} + +DocumentMap LocalDocumentsView::GetDocumentsMatchingDocumentQuery( + const ResourcePath& doc_path) { + DocumentMap result; + // Just do a simple document lookup. + FSTMaybeDocument* doc = GetDocument(DocumentKey{doc_path}); + if ([doc isKindOfClass:[FSTDocument class]]) { + result = result.insert(doc.key, static_cast(doc)); + } + return result; +} + +model::DocumentMap LocalDocumentsView::GetDocumentsMatchingCollectionGroupQuery( + FSTQuery* query) { + HARD_ASSERT( + query.path.empty(), + "Currently we only support collection group queries at the root."); + + std::string collection_id = MakeString(query.collectionGroup); + std::vector parents = + index_manager_->GetCollectionParents(collection_id); + DocumentMap results; + + // Perform a collection query against each parent that contains the + // collection_id and aggregate the results. + for (const ResourcePath& parent : parents) { + FSTQuery* collection_query = + [query collectionQueryAtPath:parent.Append(collection_id)]; + DocumentMap collection_results = + GetDocumentsMatchingCollectionQuery(collection_query); + for (const auto& kv : collection_results.underlying_map()) { + const DocumentKey& key = kv.first; + FSTDocument* doc = static_cast(kv.second); + results = results.insert(key, doc); + } + } + return results; +} + +DocumentMap LocalDocumentsView::GetDocumentsMatchingCollectionQuery( + FSTQuery* query) { + DocumentMap results = remote_document_cache_->GetMatching(query); + // Get locally persisted mutation batches. + std::vector matchingBatches = + mutation_queue_->AllMutationBatchesAffectingQuery(query); + + for (FSTMutationBatch* batch : matchingBatches) { + for (FSTMutation* mutation : [batch mutations]) { + // Only process documents belonging to the collection. + if (!query.path.IsImmediateParentOf(mutation.key.path())) { + continue; + } + + const DocumentKey& key = mutation.key; + // base_doc may be nil for the documents that weren't yet written to the + // backend. + FSTMaybeDocument* base_doc = nil; + auto found = results.underlying_map().find(key); + if (found != results.underlying_map().end()) { + base_doc = found->second; + } + FSTMaybeDocument* mutated_doc = + [mutation applyToLocalDocument:base_doc + baseDocument:base_doc + localWriteTime:batch.localWriteTime]; + + if ([mutated_doc isKindOfClass:[FSTDocument class]]) { + results = results.insert(key, static_cast(mutated_doc)); + } else { + results = results.erase(key); + } + } + } + + // Finally, filter out any documents that don't actually match the query. Note + // that the extra reference here prevents DocumentMap's destructor from + // deallocating the initial unfiltered results while we're iterating over + // them. + DocumentMap unfiltered = results; + for (const auto& kv : unfiltered.underlying_map()) { + const DocumentKey& key = kv.first; + auto* doc = static_cast(kv.second); + if (![query matchesDocument:doc]) { + results = results.erase(key); + } + } + + return results; +} + +} // namespace local +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END From b76c8436e0186f633ea846d31ef278999e2aca94 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 22 Mar 2019 16:33:20 -0700 Subject: [PATCH 077/214] Improve error message (#2614) --- Firebase/Core/FIRApp.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index f6f7b97e533..01324cea98e 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -161,8 +161,8 @@ + (void)configureWithName:(NSString *)name options:(FIROptions *)options { if (!((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9') || character == '_' || character == '-')) { [NSException raise:kFirebaseCoreErrorDomain - format:@"App name should only contain Letters, " - @"Numbers, Underscores, and Dashes."]; + format:@"App name can only contain alphanumeric (A-Z,a-z,0-9), " + @"hyphen (-), and underscore (_) characters"]; } } From 3c469b3853b8fc8a747c01ee0d49c227d81e87f2 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Fri, 22 Mar 2019 18:04:31 -0700 Subject: [PATCH 078/214] Fix all IID analyzer warnings (#2613) --- .../InstanceID/FIRInstanceIDAuthKeyChain.m | 20 ++++++++----------- .../InstanceID/FIRInstanceIDKeyPairStore.m | 6 ++++-- Firebase/InstanceID/FIRInstanceIDStore.h | 4 ++-- .../FIRInstanceIDTokenDeleteOperation.h | 2 +- .../InstanceID/FIRInstanceIDTokenOperation.h | 2 +- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m b/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m index ad758dacf7e..f75362fd33e 100644 --- a/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m +++ b/Firebase/InstanceID/FIRInstanceIDAuthKeyChain.m @@ -33,7 +33,7 @@ @interface FIRInstanceIDAuthKeychain () // cachedKeychainData is keyed by service and account, the value is an array of NSData. // It is used to cache the tokens per service, per account, as well as checkin data per service, // per account inside the keychain. -@property(nonatomic, copy) +@property(nonatomic) NSMutableDictionary *> *> *cachedKeychainData; @@ -89,7 +89,8 @@ - (NSMutableDictionary *)keychainQueryForService:(NSString *)service account:(NS keychainQuery[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; keychainQuery[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; // FIRInstanceIDKeychain should only take a query and return a result, will handle the query here. - CFArrayRef passwordInfos = [[FIRInstanceIDKeychain sharedInstance] itemWithQuery:keychainQuery]; + NSArray *passwordInfos = + CFBridgingRelease([[FIRInstanceIDKeychain sharedInstance] itemWithQuery:keychainQuery]); if (!passwordInfos) { // Nothing was found, simply return from this sync block. @@ -105,19 +106,14 @@ - (NSMutableDictionary *)keychainQueryForService:(NSString *)service account:(NS } return @[]; } - NSInteger numPasswords = CFArrayGetCount(passwordInfos); + NSInteger numPasswords = passwordInfos.count; results = [[NSMutableArray alloc] init]; - if (0 < numPasswords) { - for (NSUInteger i = 0; i < numPasswords; i++) { - NSDictionary *passwordInfo = [((__bridge NSArray *)passwordInfos) objectAtIndex:i]; - if (passwordInfo[(__bridge id)kSecValueData]) { - [results addObject:passwordInfo[(__bridge id)kSecValueData]]; - } + for (NSUInteger i = 0; i < numPasswords; i++) { + NSDictionary *passwordInfo = [passwordInfos objectAtIndex:i]; + if (passwordInfo[(__bridge id)kSecValueData]) { + [results addObject:passwordInfo[(__bridge id)kSecValueData]]; } } - if (passwordInfos != NULL) { - CFRelease(passwordInfos); - } // We query the keychain because it didn't exist in cache, now query is done, update the result in // the cache. diff --git a/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m b/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m index c913daafa14..7b715a7c556 100644 --- a/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m +++ b/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m @@ -60,7 +60,7 @@ // Query the key given a tag SecKeyRef FIRInstanceIDCachedKeyRefWithTag(NSString *tag) { _FIRInstanceIDDevAssert([tag length], @"Invalid tag for keychain specified"); - if (![tag length]) { + if (!tag.length) { return NULL; } NSDictionary *queryKey = FIRInstanceIDKeyPairQuery(tag, YES, NO); @@ -279,7 +279,9 @@ - (FIRInstanceIDKeyPair *)validCachedKeyPairWithSubtype:(NSString *)subtype // There is no need to reset keypair again here as FIRInstanceID init call is always // going to be ahead of this call, which already trigger keypair reset if it's new install FIRInstanceIDErrorCode code = kFIRInstanceIDErrorCodeInvalidKeyPairCreationTime; - *error = [NSError errorWithFIRInstanceIDErrorCode:code]; + if (error) { + *error = [NSError errorWithFIRInstanceIDErrorCode:code]; + } return nil; } } diff --git a/Firebase/InstanceID/FIRInstanceIDStore.h b/Firebase/InstanceID/FIRInstanceIDStore.h index 2b4a68a510a..d1f26348707 100644 --- a/Firebase/InstanceID/FIRInstanceIDStore.h +++ b/Firebase/InstanceID/FIRInstanceIDStore.h @@ -96,8 +96,8 @@ NS_ASSUME_NONNULL_BEGIN * @return The cached token info if any for the given authorizedEntity and scope else * returns nil. */ -- (FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity - scope:(NSString *)scope; +- (nullable FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity + scope:(NSString *)scope; /** * Return all cached token infos from the Keychain. * diff --git a/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h b/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h index 177ec2d9f31..58368d04172 100644 --- a/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h +++ b/Firebase/InstanceID/FIRInstanceIDTokenDeleteOperation.h @@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FIRInstanceIDTokenDeleteOperation : FIRInstanceIDTokenOperation - (instancetype)initWithAuthorizedEntity:(nullable NSString *)authorizedEntity - scope:(NSString *)scope + scope:(nullable NSString *)scope checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences keyPair:(nullable FIRInstanceIDKeyPair *)keyPair action:(FIRInstanceIDTokenAction)action; diff --git a/Firebase/InstanceID/FIRInstanceIDTokenOperation.h b/Firebase/InstanceID/FIRInstanceIDTokenOperation.h index 58e0019baa8..1a1842cf287 100644 --- a/Firebase/InstanceID/FIRInstanceIDTokenOperation.h +++ b/Firebase/InstanceID/FIRInstanceIDTokenOperation.h @@ -57,7 +57,7 @@ typedef void (^FIRInstanceIDTokenOperationCompletion)(FIRInstanceIDTokenOperatio @property(nonatomic, readonly) FIRInstanceIDTokenAction action; @property(nonatomic, readonly, nullable) NSString *authorizedEntity; -@property(nonatomic, readonly) NSString *scope; +@property(nonatomic, readonly, nullable) NSString *scope; @property(nonatomic, readonly, nullable) NSDictionary *options; @property(nonatomic, readonly, strong) FIRInstanceIDCheckinPreferences *checkinPreferences; @property(nonatomic, readonly, strong) FIRInstanceIDKeyPair *keyPair; From 69c8e5251eddfe39c30db70be461d8874cf9be79 Mon Sep 17 00:00:00 2001 From: Gil Date: Sun, 24 Mar 2019 12:21:20 -0700 Subject: [PATCH 079/214] Avoid retain cycles in parent pointers (#2621) All the various table drivers that need to navigate back must use weak pointers to get there because the parent owns the children. --- .../firebase/firestore/local/leveldb_remote_document_cache.h | 3 ++- .../core/src/firebase/firestore/local/memory_mutation_queue.h | 3 ++- .../core/src/firebase/firestore/local/memory_query_cache.h | 4 +++- .../firebase/firestore/local/memory_remote_document_cache.h | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.h b/Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.h index 536d74c1d33..99bf754b87a 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.h +++ b/Firestore/core/src/firebase/firestore/local/leveldb_remote_document_cache.h @@ -57,7 +57,8 @@ class LevelDbRemoteDocumentCache : public RemoteDocumentCache { FSTMaybeDocument* DecodeMaybeDocument(absl::string_view encoded, const model::DocumentKey& key); - FSTLevelDB* db_; + // This instance is owned by FSTLevelDB; avoid a retain cycle. + __weak FSTLevelDB* db_; FSTLocalSerializer* serializer_; }; diff --git a/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h b/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h index 463439e9331..b8113c70dda 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h +++ b/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h @@ -111,7 +111,8 @@ class MemoryMutationQueue : public MutationQueue { */ int IndexOfBatchId(model::BatchId batch_id); - FSTMemoryPersistence* persistence_; + // This instance is owned by FSTMemoryPersistence; avoid a retain cycle. + __weak FSTMemoryPersistence* persistence_; /** * A FIFO queue of all mutations to apply to the backend. Mutations are added * to the end of the queue as they're written, and removed from the front of diff --git a/Firestore/core/src/firebase/firestore/local/memory_query_cache.h b/Firestore/core/src/firebase/firestore/local/memory_query_cache.h index 706b8f40401..ac34fca078e 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_query_cache.h +++ b/Firestore/core/src/firebase/firestore/local/memory_query_cache.h @@ -95,7 +95,9 @@ class MemoryQueryCache : public QueryCache { void SetLastRemoteSnapshotVersion(model::SnapshotVersion version) override; private: - FSTMemoryPersistence* persistence_; + // This instance is owned by FSTMemoryPersistence; avoid a retain cycle. + __weak FSTMemoryPersistence* persistence_; + /** The highest sequence number encountered */ model::ListenSequenceNumber highest_listen_sequence_number_; /** The highest numbered target ID encountered. */ diff --git a/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h index 85bddc37472..e0f3cda628a 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h +++ b/Firestore/core/src/firebase/firestore/local/memory_remote_document_cache.h @@ -62,7 +62,8 @@ class MemoryRemoteDocumentCache : public RemoteDocumentCache { /** Underlying cache of documents. */ model::MaybeDocumentMap docs_; - FSTMemoryPersistence *persistence_; + // This instance is owned by FSTMemoryPersistence; avoid a retain cycle. + __weak FSTMemoryPersistence *persistence_; }; } // namespace local From 0738c0efd2d7bbbce80520f5faf51192470ff052 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Sun, 24 Mar 2019 17:17:10 -0400 Subject: [PATCH 080/214] Fix handler for multiple calls of -[FIRInstanceID instanceIDWithHandler:] (#2445) (#2559) --- Example/InstanceID/Tests/FIRInstanceIDTest.m | 176 ++++++++++++++++++ Example/default.profraw | Bin 0 -> 2056 bytes Firebase/InstanceID/FIRInstanceID.m | 80 +++++--- .../InstanceID/FIRInstanceIDCombinedHandler.h | 31 +++ .../InstanceID/FIRInstanceIDCombinedHandler.m | 64 +++++++ default.profraw | Bin 0 -> 2056 bytes 6 files changed, 324 insertions(+), 27 deletions(-) create mode 100644 Example/default.profraw create mode 100644 Firebase/InstanceID/FIRInstanceIDCombinedHandler.h create mode 100644 Firebase/InstanceID/FIRInstanceIDCombinedHandler.m create mode 100644 default.profraw diff --git a/Example/InstanceID/Tests/FIRInstanceIDTest.m b/Example/InstanceID/Tests/FIRInstanceIDTest.m index 6895abf8401..2bc67cf659d 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDTest.m @@ -811,6 +811,182 @@ - (void)testDefaultToken_maxRetries { XCTAssertEqual(newTokenFetchCount, [FIRInstanceID maxRetryCountForDefaultToken]); } +- (void)testInstanceIDWithHandler_WhileRequesting_Success { + [self stubKeyPairStoreToReturnValidKeypair]; + [self mockAuthServiceToAlwaysReturnValidCheckin]; + + // Expect `fetchNewTokenWithAuthorizedEntity` to be called once + XCTestExpectation *fetchNewTokenExpectation = + [self expectationWithDescription:@"fetchNewTokenExpectation"]; + __block FIRInstanceIDTokenHandler tokenHandler; + + [[[self.mockTokenManager stub] andDo:^(NSInvocation *invocation) { + [invocation getArgument:&tokenHandler atIndex:6]; + [fetchNewTokenExpectation fulfill]; + }] fetchNewTokenWithAuthorizedEntity:kAuthorizedEntity + scope:kFIRInstanceIDDefaultTokenScope + keyPair:[OCMArg any] + options:[OCMArg any] + handler:[OCMArg any]]; + + // Make 1st call + XCTestExpectation *handlerExpectation1 = [self expectationWithDescription:@"handlerExpectation1"]; + FIRInstanceIDResultHandler handler1 = + ^(FIRInstanceIDResult *_Nullable result, NSError *_Nullable error) { + [handlerExpectation1 fulfill]; + XCTAssertNotNil(result); + XCTAssertEqual(result.token, kToken); + XCTAssertNil(error); + }; + + [self.mockInstanceID instanceIDWithHandler:handler1]; + + // Make 2nd call + XCTestExpectation *handlerExpectation2 = [self expectationWithDescription:@"handlerExpectation1"]; + FIRInstanceIDResultHandler handler2 = + ^(FIRInstanceIDResult *_Nullable result, NSError *_Nullable error) { + [handlerExpectation2 fulfill]; + XCTAssertNotNil(result); + XCTAssertEqual(result.token, kToken); + XCTAssertNil(error); + }; + + [self.mockInstanceID instanceIDWithHandler:handler2]; + + // Wait for `fetchNewTokenWithAuthorizedEntity` to be performed + [self waitForExpectations:@[ fetchNewTokenExpectation ] timeout:1 enforceOrder:false]; + // Finish token fetch request + tokenHandler(kToken, nil); + + // Wait for completion handlers for both calls to be performed + [self waitForExpectationsWithTimeout:1 handler:NULL]; +} + +- (void)testInstanceIDWithHandler_WhileRequesting_RetrySuccess { + [self stubKeyPairStoreToReturnValidKeypair]; + [self mockAuthServiceToAlwaysReturnValidCheckin]; + + // Expect `fetchNewTokenWithAuthorizedEntity` to be called twice + XCTestExpectation *fetchNewTokenExpectation1 = + [self expectationWithDescription:@"fetchNewTokenExpectation1"]; + XCTestExpectation *fetchNewTokenExpectation2 = + [self expectationWithDescription:@"fetchNewTokenExpectation2"]; + NSArray *fetchNewTokenExpectations = @[ fetchNewTokenExpectation1, fetchNewTokenExpectation2 ]; + + __block NSInteger fetchNewTokenCallCount = 0; + __block FIRInstanceIDTokenHandler tokenHandler; + + [[[self.mockTokenManager stub] andDo:^(NSInvocation *invocation) { + [invocation getArgument:&tokenHandler atIndex:6]; + [fetchNewTokenExpectations[fetchNewTokenCallCount] fulfill]; + fetchNewTokenCallCount += 1; + }] fetchNewTokenWithAuthorizedEntity:kAuthorizedEntity + scope:kFIRInstanceIDDefaultTokenScope + keyPair:[OCMArg any] + options:[OCMArg any] + handler:[OCMArg any]]; + + // Mock Instance ID's retry interval to 0, to vastly speed up this test. + [[[self.mockInstanceID stub] andReturnValue:@(0)] retryIntervalToFetchDefaultToken]; + + // Make 1st call + XCTestExpectation *handlerExpectation1 = [self expectationWithDescription:@"handlerExpectation1"]; + FIRInstanceIDResultHandler handler1 = + ^(FIRInstanceIDResult *_Nullable result, NSError *_Nullable error) { + [handlerExpectation1 fulfill]; + XCTAssertNotNil(result); + XCTAssertEqual(result.token, kToken); + XCTAssertNil(error); + }; + + [self.mockInstanceID instanceIDWithHandler:handler1]; + + // Make 2nd call + XCTestExpectation *handlerExpectation2 = [self expectationWithDescription:@"handlerExpectation1"]; + FIRInstanceIDResultHandler handler2 = + ^(FIRInstanceIDResult *_Nullable result, NSError *_Nullable error) { + [handlerExpectation2 fulfill]; + XCTAssertNotNil(result); + XCTAssertEqual(result.token, kToken); + XCTAssertNil(error); + }; + + [self.mockInstanceID instanceIDWithHandler:handler2]; + + // Wait for the 1st `fetchNewTokenWithAuthorizedEntity` to be performed + [self waitForExpectations:@[ fetchNewTokenExpectation1 ] timeout:1 enforceOrder:false]; + // Fail for the 1st time + tokenHandler(nil, [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeUnknown]); + + // Wait for the 2nd token feth + [self waitForExpectations:@[ fetchNewTokenExpectation2 ] timeout:1 enforceOrder:false]; + // Finish with success + tokenHandler(kToken, nil); + + // Wait for completion handlers for both calls to be performed + [self waitForExpectationsWithTimeout:1 handler:NULL]; +} + +- (void)testInstanceIDWithHandler_WhileRequesting_RetryFailure { + [self stubKeyPairStoreToReturnValidKeypair]; + [self mockAuthServiceToAlwaysReturnValidCheckin]; + + // Expect `fetchNewTokenWithAuthorizedEntity` to be called once + NSMutableArray *fetchNewTokenExpectations = [NSMutableArray array]; + for (NSInteger i = 0; i < [[self.instanceID class] maxRetryCountForDefaultToken]; ++i) { + NSString *name = [NSString stringWithFormat:@"fetchNewTokenExpectation-%ld", (long)i]; + [fetchNewTokenExpectations addObject:[self expectationWithDescription:name]]; + } + + __block NSInteger fetchNewTokenCallCount = 0; + __block FIRInstanceIDTokenHandler tokenHandler; + + [[[self.mockTokenManager stub] andDo:^(NSInvocation *invocation) { + [invocation getArgument:&tokenHandler atIndex:6]; + [fetchNewTokenExpectations[fetchNewTokenCallCount] fulfill]; + fetchNewTokenCallCount += 1; + }] fetchNewTokenWithAuthorizedEntity:kAuthorizedEntity + scope:kFIRInstanceIDDefaultTokenScope + keyPair:[OCMArg any] + options:[OCMArg any] + handler:[OCMArg any]]; + + // Mock Instance ID's retry interval to 0, to vastly speed up this test. + [[[self.mockInstanceID stub] andReturnValue:@(0)] retryIntervalToFetchDefaultToken]; + + // Make 1st call + XCTestExpectation *handlerExpectation1 = [self expectationWithDescription:@"handlerExpectation1"]; + FIRInstanceIDResultHandler handler1 = + ^(FIRInstanceIDResult *_Nullable result, NSError *_Nullable error) { + [handlerExpectation1 fulfill]; + XCTAssertNil(result); + XCTAssertNotNil(error); + }; + + [self.mockInstanceID instanceIDWithHandler:handler1]; + + // Make 2nd call + XCTestExpectation *handlerExpectation2 = [self expectationWithDescription:@"handlerExpectation1"]; + FIRInstanceIDResultHandler handler2 = + ^(FIRInstanceIDResult *_Nullable result, NSError *_Nullable error) { + [handlerExpectation2 fulfill]; + XCTAssertNil(result); + XCTAssertNotNil(error); + }; + + [self.mockInstanceID instanceIDWithHandler:handler2]; + + for (NSInteger i = 0; i < [[self.instanceID class] maxRetryCountForDefaultToken]; ++i) { + // Wait for the i `fetchNewTokenWithAuthorizedEntity` to be performed + [self waitForExpectations:@[ fetchNewTokenExpectations[i] ] timeout:1 enforceOrder:false]; + // Fail for the i time + tokenHandler(nil, [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeUnknown]); + } + + // Wait for completion handlers for both calls to be performed + [self waitForExpectationsWithTimeout:1 handler:NULL]; +} + /** * Tests a Keychain read failure while we try to fetch a new InstanceID token. If the Keychain * read fails we won't be able to fetch the public key which is required while fetching a new diff --git a/Example/default.profraw b/Example/default.profraw new file mode 100644 index 0000000000000000000000000000000000000000..5caaf10542c59713f173beac6e81226aca03c097 GIT binary patch literal 2056 zcmZoHO3N=Q$obF000E*8oX!mff-#9il}-S~Az_DR1x zk}!1wA`o>DRSXQAP=q5h`* zjH9VG56w;_#Vm6_awXUSs$M`G)jhEA{3Di<*2a$+tj8g^`es-GZ*YCb#E0T&kR*-^>w ze6=ZE<7&lkk?Sv@>K$aTt6%^3sMy&huf%KGrZ#wQz1yh}22qzFi>jUvYQC42g7o75 zhh1R)od8v@po*#<=3k!eb`M;Ry`O&d?Z1}|^TRb~K-D*(sfUH*0(VV!%gNxw+RQ&>VRP`|P1qJS2^!-%eyf+~FQPVT0#s^UK z3>v8Fg`p0R@jZOd;Hr6`1e9M94pDbO8&y5bzuhyMx>}!io`$J!fU5t1L%oDc<>sx9 zlRv=JcR3h=VCpA8)f?bY-{SBx=7H%PGno1rQ1t;g)WgDklpYO% z;TQr;M0#o&^BKm9TT}Z^`!O5xxK5Q{B_JwYwpdT2yK4pWYmFt-XJ$;Y*mQT|kLqPh zSB6BCKCiC%v*GSJqo=FnnasA`-mAPcEc$#?AG>9%c*c5}(;rry5Z|!SX#0*6cFqUW zcel@zW?z#m4Z>^B+wSU#I$gtw*duF@*j_mvU*M8buT>tx> zao~&?9b?}e-f^~ecb~79KO@e3SLv|^XER9eef8IltlY#p{89nDY*?=r<6Kf Ww0c`zn(7N|)`rijW4Lp1o*DolLLuP* literal 0 HcmV?d00001 diff --git a/Firebase/InstanceID/FIRInstanceID.m b/Firebase/InstanceID/FIRInstanceID.m index c5ae18e373b..f5c4a6eb551 100644 --- a/Firebase/InstanceID/FIRInstanceID.m +++ b/Firebase/InstanceID/FIRInstanceID.m @@ -24,6 +24,7 @@ #import #import "FIRInstanceID+Private.h" #import "FIRInstanceIDAuthService.h" +#import "FIRInstanceIDCombinedHandler.h" #import "FIRInstanceIDConstants.h" #import "FIRInstanceIDDefines.h" #import "FIRInstanceIDKeyPairStore.h" @@ -114,9 +115,9 @@ @interface FIRInstanceID () @property(nonatomic, readwrite, strong) FIRInstanceIDKeyPairStore *keyPairStore; // backoff and retry for default token -@property(atomic, readwrite, assign) BOOL isFetchingDefaultToken; -@property(atomic, readwrite, assign) BOOL isDefaultTokenFetchScheduled; @property(nonatomic, readwrite, assign) NSInteger retryCountForDefaultToken; +@property(atomic, strong, nullable) + FIRInstanceIDCombinedHandler *defaultTokenFetchHandler; @end @@ -831,10 +832,30 @@ - (NSInteger)retryIntervalToFetchDefaultToken { kMaxRetryIntervalForDefaultTokenInSeconds); } -- (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { - if (self.isFetchingDefaultToken || self.isDefaultTokenFetchScheduled) { +- (void)defaultTokenWithHandler:(nullable FIRInstanceIDTokenHandler)aHandler { + [self defaultTokenWithRetry:NO handler:aHandler]; +} + +/** + * @param retry Indicates if the method is called to perform a retry after a failed attempt. + * If `YES`, then actual token request will be performed even if `self.defaultTokenFetchHandler != + * nil` + */ +- (void)defaultTokenWithRetry:(BOOL)retry handler:(nullable FIRInstanceIDTokenHandler)aHandler { + BOOL shouldPerformRequest = retry || self.defaultTokenFetchHandler == nil; + + if (!self.defaultTokenFetchHandler) { + self.defaultTokenFetchHandler = [[FIRInstanceIDCombinedHandler alloc] init]; + } + + if (aHandler) { + [self.defaultTokenFetchHandler addHandler:aHandler]; + } + + if (!shouldPerformRequest) { return; } + NSDictionary *instanceIDOptions = @{}; BOOL hasFirebaseMessaging = NSClassFromString(kFIRInstanceIDFCMSDKClassString) != nil; if (hasFirebaseMessaging && self.apnsTokenData) { @@ -851,7 +872,6 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { FIRInstanceID_WEAKIFY(self); FIRInstanceIDTokenHandler newHandler = ^void(NSString *token, NSError *error) { FIRInstanceID_STRONGIFY(self); - self.isFetchingDefaultToken = NO; if (error) { FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID009, @@ -871,21 +891,12 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { // Do not retry beyond the maximum limit. if (self.retryCountForDefaultToken < [[self class] maxRetryCountForDefaultToken]) { NSInteger retryInterval = [self retryIntervalToFetchDefaultToken]; - FIRInstanceID_WEAKIFY(self); - self.isDefaultTokenFetchScheduled = YES; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - FIRInstanceID_STRONGIFY(self); - self.isDefaultTokenFetchScheduled = NO; - [self defaultTokenWithHandler:handler]; - }); + [self retryGetDefaultTokenAfter:retryInterval]; } else { FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID007, @"Failed to retrieve the default FCM token after %ld retries", (long)self.retryCountForDefaultToken); - if (handler) { - handler(nil, error); - } + [self performDefaultTokenHandlerWithToken:nil error:error]; } } else { // If somebody updated IID with APNS token while our initial request did not have it @@ -904,13 +915,7 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { if (!APNSRemainedSameDuringFetch && hasFirebaseMessaging) { // APNs value did change mid-fetch, so the token should be re-fetched with the current APNs // value. - self.isDefaultTokenFetchScheduled = YES; - FIRInstanceID_WEAKIFY(self); - dispatch_async(dispatch_get_main_queue(), ^{ - FIRInstanceID_STRONGIFY(self); - self.isDefaultTokenFetchScheduled = NO; - [self defaultTokenWithHandler:handler]; - }); + [self retryGetDefaultTokenAfter:0]; FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeRefetchingTokenForAPNS, @"Received APNS token while fetching default token. " @"Refetching default token."); @@ -934,20 +939,41 @@ - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler { object:[self.defaultFCMToken copy]]; [[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification postingStyle:NSPostASAP]; - } - if (handler) { - handler(token, nil); + + [self performDefaultTokenHandlerWithToken:token error:nil]; } } }; - self.isFetchingDefaultToken = YES; [self tokenWithAuthorizedEntity:self.fcmSenderID scope:kFIRInstanceIDDefaultTokenScope options:instanceIDOptions handler:newHandler]; } +/** + * + */ +- (void)performDefaultTokenHandlerWithToken:(NSString *)token error:(NSError *)error { + if (!self.defaultTokenFetchHandler) { + return; + } + + [self.defaultTokenFetchHandler combinedHandler](token, error); + self.defaultTokenFetchHandler = nil; +} + +- (void)retryGetDefaultTokenAfter:(NSTimeInterval)retryInterval { + FIRInstanceID_WEAKIFY(self); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + FIRInstanceID_STRONGIFY(self); + // Pass nil: no new handlers to be added, currently existing handlers + // will be called + [self defaultTokenWithRetry:YES handler:nil]; + }); +} + #pragma mark - APNS Token // This should only be triggered from FCM. - (void)notifyAPNSTokenIsSet:(NSNotification *)notification { diff --git a/Firebase/InstanceID/FIRInstanceIDCombinedHandler.h b/Firebase/InstanceID/FIRInstanceIDCombinedHandler.h new file mode 100644 index 00000000000..dcb5429b953 --- /dev/null +++ b/Firebase/InstanceID/FIRInstanceIDCombinedHandler.h @@ -0,0 +1,31 @@ +/* + * Copyright 2019 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 + +NS_ASSUME_NONNULL_BEGIN + +/** + * A generic class to combine several handler blocks into a single block in a thread-safe manner + */ +@interface FIRInstanceIDCombinedHandler : NSObject + +- (void)addHandler:(void (^)(ResultType _Nullable result, NSError* _Nullable error))handler; +- (void (^)(ResultType _Nullable result, NSError* _Nullable error))combinedHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/InstanceID/FIRInstanceIDCombinedHandler.m b/Firebase/InstanceID/FIRInstanceIDCombinedHandler.m new file mode 100644 index 00000000000..bc6be6c1085 --- /dev/null +++ b/Firebase/InstanceID/FIRInstanceIDCombinedHandler.m @@ -0,0 +1,64 @@ +/* + * Copyright 2019 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 "FIRInstanceIDCombinedHandler.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^FIRInstanseIDHandler)(id _Nullable result, NSError *_Nullable error); + +@interface FIRInstanceIDCombinedHandler () +@property(atomic, readonly, strong) NSMutableArray *handlers; +@end + +NS_ASSUME_NONNULL_END + +@implementation FIRInstanceIDCombinedHandler + +- (instancetype)init { + self = [super init]; + if (self) { + _handlers = [NSMutableArray array]; + } + return self; +} + +- (void)addHandler:(FIRInstanseIDHandler)handler { + if (!handler) { + return; + } + + @synchronized(self) { + [self.handlers addObject:handler]; + } +} + +- (FIRInstanseIDHandler)combinedHandler { + FIRInstanseIDHandler combinedHandler = nil; + + @synchronized(self) { + NSArray *handlers = [self.handlers copy]; + combinedHandler = ^(id result, NSError *error) { + for (FIRInstanseIDHandler handler in handlers) { + handler(result, error); + } + }; + } + + return combinedHandler; +} + +@end diff --git a/default.profraw b/default.profraw new file mode 100644 index 0000000000000000000000000000000000000000..d102825dd2dd51aeb4258726ffe48cd40a5c1bae GIT binary patch literal 2056 zcmZoHO3N=Q$obF000E*%Jl2)hbS5qF(?cH`F>*(d$( zNW#T7==IGd;@eF&yr1FHT24)r(f zXB+Qt_fm z)4TR*MzM36EXjbXH$YPl3(t)lR;We{}QG$1;;+F3kKL zQ1uVWu$zCuPU=p5ll9&}70roDJh&JRK-DiOM^z6qUr^xgMc+>a&U*u*A2mI5YJ31y z&rpG?UKr{C8Q;SP4X&C8NRTLM#yl{cV+K<{1FAj%hk97JkJ6(d zFdRdGiAYZ^V?M)JacgSdX+LH|9@nYzs{}-)%NFZtba$;_eyy=&`pk?;7Mt!){87DZ z>B^9Z(&yDRe>U7bXY_QHJd@eB+k2IlhDD!m>SMQT70*~NbNa)o6XF{d8g1Wk!p`|% z`tJ65((KE!EYBtVh}h;Ky5|nJfMMgS@2wTGz4os<78zC?ea~!{-;sTP|JqNRi|c>C zGY*^)qhsv5!#mE_?(XyT@@K@E?<&1joZ59DWd7VIvkx9)k&y%Qb Xi&k%|OH+M;&D!u;bqsed&Qk*bVzxNx literal 0 HcmV?d00001 From 4cb3160f38d2beddbf6093c32d3468b0277be6c9 Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 25 Mar 2019 09:13:54 -0700 Subject: [PATCH 081/214] Clean up warnings in the Firestore project (#2622) * Add nanopb::CheckedSize Fix implicit narrowing warnings and reduce pb_size_t copypasta. * Rename local::DocumentReference to DocumentKeyReference This cleans up a build-time warning about a duplicate file in the Copy Headers phase and cleans up a massive duplicate id warning during pod install. --- .../firebase/firestore/local/CMakeLists.txt | 4 +- ...reference.cc => document_key_reference.cc} | 20 +++++---- ...t_reference.h => document_key_reference.h} | 28 ++++++------- .../firestore/local/local_serializer.cc | 14 +++---- .../firestore/local/memory_mutation_queue.h | 8 ++-- .../firestore/local/memory_mutation_queue.mm | 14 +++---- .../firebase/firestore/local/reference_set.cc | 21 +++++----- .../firebase/firestore/local/reference_set.h | 11 ++--- .../firestore/nanopb/nanopb_string.cc | 4 +- .../firebase/firestore/nanopb/nanopb_util.h | 42 +++++++++++++++++++ .../firebase/firestore/remote/serializer.cc | 31 +++++++------- .../firebase/firestore/remote/serializer.h | 2 +- 12 files changed, 122 insertions(+), 77 deletions(-) rename Firestore/core/src/firebase/firestore/local/{document_reference.cc => document_key_reference.cc} (74%) rename Firestore/core/src/firebase/firestore/local/{document_reference.h => document_key_reference.h} (74%) create mode 100644 Firestore/core/src/firebase/firestore/nanopb/nanopb_util.h diff --git a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt index 82c6e9af325..dbffa9e46c7 100644 --- a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt @@ -54,8 +54,8 @@ endif() cc_library( firebase_firestore_local SOURCES - document_reference.h - document_reference.cc + document_key_reference.h + document_key_reference.cc index_manager.h listen_sequence.h local_documents_view.h diff --git a/Firestore/core/src/firebase/firestore/local/document_reference.cc b/Firestore/core/src/firebase/firestore/local/document_key_reference.cc similarity index 74% rename from Firestore/core/src/firebase/firestore/local/document_reference.cc rename to Firestore/core/src/firebase/firestore/local/document_key_reference.cc index e3f1794ed18..61c1b718456 100644 --- a/Firestore/core/src/firebase/firestore/local/document_reference.cc +++ b/Firestore/core/src/firebase/firestore/local/document_key_reference.cc @@ -14,10 +14,11 @@ * limitations under the License. */ +#include "Firestore/core/src/firebase/firestore/local/document_key_reference.h" + #include #include -#include "Firestore/core/src/firebase/firestore/local/document_reference.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" @@ -29,22 +30,23 @@ namespace local { using model::DocumentKey; using util::ComparisonResult; -bool operator==(const DocumentReference& lhs, const DocumentReference& rhs) { +bool operator==(const DocumentKeyReference& lhs, + const DocumentKeyReference& rhs) { return lhs.key_ == rhs.key_ && lhs.ref_id_ == rhs.ref_id_; } -size_t DocumentReference::Hash() const { +size_t DocumentKeyReference::Hash() const { return util::Hash(key_.ToString(), ref_id_); } -std::string DocumentReference::ToString() const { - return util::StringFormat("", +std::string DocumentKeyReference::ToString() const { + return util::StringFormat("", key_.ToString(), ref_id_); } /** Sorts document references by key then ID. */ -bool DocumentReference::ByKey::operator()(const DocumentReference& lhs, - const DocumentReference& rhs) const { +bool DocumentKeyReference::ByKey::operator()( + const DocumentKeyReference& lhs, const DocumentKeyReference& rhs) const { util::Comparator key_less; if (key_less(lhs.key_, rhs.key_)) return true; if (key_less(rhs.key_, lhs.key_)) return false; @@ -54,8 +56,8 @@ bool DocumentReference::ByKey::operator()(const DocumentReference& lhs, } /** Sorts document references by ID then key. */ -bool DocumentReference::ById::operator()(const DocumentReference& lhs, - const DocumentReference& rhs) const { +bool DocumentKeyReference::ById::operator()( + const DocumentKeyReference& lhs, const DocumentKeyReference& rhs) const { util::Comparator id_less; if (id_less(lhs.ref_id_, rhs.ref_id_)) return true; if (id_less(rhs.ref_id_, lhs.ref_id_)) return false; diff --git a/Firestore/core/src/firebase/firestore/local/document_reference.h b/Firestore/core/src/firebase/firestore/local/document_key_reference.h similarity index 74% rename from Firestore/core/src/firebase/firestore/local/document_reference.h rename to Firestore/core/src/firebase/firestore/local/document_key_reference.h index 3160ca42788..257971b5b2d 100644 --- a/Firestore/core/src/firebase/firestore/local/document_reference.h +++ b/Firestore/core/src/firebase/firestore/local/document_key_reference.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_DOCUMENT_REFERENCE_H_ -#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_DOCUMENT_REFERENCE_H_ +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_DOCUMENT_KEY_REFERENCE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_DOCUMENT_KEY_REFERENCE_H_ #include #include @@ -39,13 +39,13 @@ namespace local { * * Not to be confused with FIRDocumentReference. */ -class DocumentReference { +class DocumentKeyReference { public: - DocumentReference() { + DocumentKeyReference() { } /** Initializes the document reference with the given key and Id. */ - DocumentReference(model::DocumentKey key, int32_t ref_id) + DocumentKeyReference(model::DocumentKey key, int32_t ref_id) : key_{std::move(key)}, ref_id_{ref_id} { } @@ -63,23 +63,23 @@ class DocumentReference { return ref_id_; } - friend bool operator==(const DocumentReference& lhs, - const DocumentReference& rhs); + friend bool operator==(const DocumentKeyReference& lhs, + const DocumentKeyReference& rhs); size_t Hash() const; std::string ToString() const; /** Sorts document references by key then Id. */ - struct ByKey : public util::Comparator { - bool operator()(const DocumentReference& lhs, - const DocumentReference& rhs) const; + struct ByKey : public util::Comparator { + bool operator()(const DocumentKeyReference& lhs, + const DocumentKeyReference& rhs) const; }; /** Sorts document references by Id then key. */ - struct ById : public util::Comparator { - bool operator()(const DocumentReference& lhs, - const DocumentReference& rhs) const; + struct ById : public util::Comparator { + bool operator()(const DocumentKeyReference& lhs, + const DocumentKeyReference& rhs) const; }; private: @@ -94,4 +94,4 @@ class DocumentReference { } // namespace firestore } // namespace firebase -#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_DOCUMENT_REFERENCE_H_ +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_DOCUMENT_KEY_REFERENCE_H_ diff --git a/Firestore/core/src/firebase/firestore/local/local_serializer.cc b/Firestore/core/src/firebase/firestore/local/local_serializer.cc index 7f252969186..2b7944f09b3 100644 --- a/Firestore/core/src/firebase/firestore/local/local_serializer.cc +++ b/Firestore/core/src/firebase/firestore/local/local_serializer.cc @@ -29,6 +29,7 @@ #include "Firestore/core/src/firebase/firestore/model/no_document.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/model/unknown_document.h" +#include "Firestore/core/src/firebase/firestore/nanopb/nanopb_util.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/string_format.h" @@ -44,6 +45,7 @@ using model::MutationBatch; using model::NoDocument; using model::SnapshotVersion; using model::UnknownDocument; +using nanopb::CheckedSize; using nanopb::Reader; using nanopb::Writer; using remote::MakeArray; @@ -123,10 +125,8 @@ google_firestore_v1_Document LocalSerializer::EncodeDocument( rpc_serializer_.EncodeString(rpc_serializer_.EncodeKey(doc.key())); // Encode Document.fields (unless it's empty) - size_t count = doc.data().GetInternalValue().size(); - HARD_ASSERT(count <= std::numeric_limits::max(), - "Unable to encode specified document. Too many fields."); - result.fields_count = static_cast(count); + pb_size_t count = CheckedSize(doc.data().GetInternalValue().size()); + result.fields_count = count; result.fields = MakeArray(count); int i = 0; for (const auto& kv : doc.data().GetInternalValue()) { @@ -257,10 +257,8 @@ firestore_client_WriteBatch LocalSerializer::EncodeMutationBatch( firestore_client_WriteBatch result{}; result.batch_id = mutation_batch.batch_id(); - size_t count = mutation_batch.mutations().size(); - HARD_ASSERT(count <= std::numeric_limits::max(), - "Unable to encode specified mutation batch. Too many mutations."); - result.writes_count = static_cast(count); + pb_size_t count = CheckedSize(mutation_batch.mutations().size()); + result.writes_count = count; result.writes = MakeArray(count); int i = 0; for (const std::unique_ptr& mutation : mutation_batch.mutations()) { diff --git a/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h b/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h index b8113c70dda..927317cc3df 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h +++ b/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.h @@ -29,7 +29,7 @@ #import "Firestore/Source/Public/FIRTimestamp.h" #include "Firestore/core/src/firebase/firestore/immutable/sorted_set.h" -#include "Firestore/core/src/firebase/firestore/local/document_reference.h" +#include "Firestore/core/src/firebase/firestore/local/document_key_reference.h" #include "Firestore/core/src/firebase/firestore/local/mutation_queue.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_key_set.h" @@ -94,8 +94,8 @@ class MemoryMutationQueue : public MutationQueue { void SetLastStreamToken(NSData* _Nullable token) override; private: - using DocumentReferenceSet = - immutable::SortedSet; + using DocumentKeyReferenceSet = + immutable::SortedSet; std::vector AllMutationBatchesWithIds( const std::set& batch_ids); @@ -147,7 +147,7 @@ class MemoryMutationQueue : public MutationQueue { NSData* _Nullable last_stream_token_; /** An ordered mapping between documents and the mutation batch IDs. */ - DocumentReferenceSet batches_by_document_key_; + DocumentKeyReferenceSet batches_by_document_key_; }; } // namespace local diff --git a/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.mm b/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.mm index 52ff632d808..aace5e5ce3c 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.mm +++ b/Firestore/core/src/firebase/firestore/local/memory_mutation_queue.mm @@ -25,7 +25,7 @@ #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Model/FSTMutationBatch.h" -#include "Firestore/core/src/firebase/firestore/local/document_reference.h" +#include "Firestore/core/src/firebase/firestore/local/document_key_reference.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" @@ -98,7 +98,7 @@ // Track references by document key and index collection parents. for (FSTMutation* mutation : [batch mutations]) { batches_by_document_key_ = batches_by_document_key_.insert( - DocumentReference{mutation.key, batch_id}); + DocumentKeyReference{mutation.key, batch_id}); persistence_.indexManager->AddToCollectionParentIndex( mutation.key.path().PopLast()); @@ -121,7 +121,7 @@ const DocumentKey& key = mutation.key; [persistence_.referenceDelegate removeMutationReference:key]; - DocumentReference reference{key, batch.batchID}; + DocumentKeyReference reference{key, batch.batchID}; batches_by_document_key_ = batches_by_document_key_.erase(reference); } } @@ -132,7 +132,7 @@ // First find the set of affected batch IDs. std::set batch_ids; for (const DocumentKey& key : document_keys) { - DocumentReference start{key, 0}; + DocumentKeyReference start{key, 0}; for (const auto& reference : batches_by_document_key_.values_from(start)) { if (key != reference.key()) break; @@ -149,7 +149,7 @@ const DocumentKey& key) { std::vector result; - DocumentReference start{key, 0}; + DocumentKeyReference start{key, 0}; for (const auto& reference : batches_by_document_key_.values_from(start)) { if (key != reference.key()) break; @@ -178,7 +178,7 @@ if (!DocumentKey::IsDocumentKey(start_path)) { start_path = start_path.Append(""); } - DocumentReference start{DocumentKey{start_path}, 0}; + DocumentKeyReference start{DocumentKey{start_path}, 0}; // Find unique batchIDs referenced by all documents potentially matching the // query. @@ -242,7 +242,7 @@ bool MemoryMutationQueue::ContainsKey(const model::DocumentKey& key) { // Create a reference with a zero ID as the start position to find any // document reference with this key. - DocumentReference reference{key, 0}; + DocumentKeyReference reference{key, 0}; auto range = batches_by_document_key_.values_from(reference); auto begin = range.begin(); return begin != range.end() && begin->key() == key; diff --git a/Firestore/core/src/firebase/firestore/local/reference_set.cc b/Firestore/core/src/firebase/firestore/local/reference_set.cc index 9529b1ffed7..57431d75c9f 100644 --- a/Firestore/core/src/firebase/firestore/local/reference_set.cc +++ b/Firestore/core/src/firebase/firestore/local/reference_set.cc @@ -15,8 +15,9 @@ */ #include "Firestore/core/src/firebase/firestore/local/reference_set.h" + #include "Firestore/core/src/firebase/firestore/immutable/sorted_set.h" -#include "Firestore/core/src/firebase/firestore/local/document_reference.h" +#include "Firestore/core/src/firebase/firestore/local/document_key_reference.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" namespace firebase { @@ -27,7 +28,7 @@ using model::DocumentKey; using model::DocumentKeySet; void ReferenceSet::AddReference(const DocumentKey& key, int id) { - DocumentReference reference{key, id}; + DocumentKeyReference reference{key, id}; by_key_ = by_key_.insert(reference); by_id_ = by_id_.insert(reference); } @@ -39,7 +40,7 @@ void ReferenceSet::AddReferences(const DocumentKeySet& keys, int id) { } void ReferenceSet::RemoveReference(const DocumentKey& key, int id) { - RemoveReference(DocumentReference{key, id}); + RemoveReference(DocumentKeyReference{key, id}); } void ReferenceSet::RemoveReferences( @@ -50,8 +51,8 @@ void ReferenceSet::RemoveReferences( } DocumentKeySet ReferenceSet::RemoveReferences(int id) { - DocumentReference start{DocumentKey::Empty(), id}; - DocumentReference end{DocumentKey::Empty(), id + 1}; + DocumentKeyReference start{DocumentKey::Empty(), id}; + DocumentKeyReference end{DocumentKey::Empty(), id + 1}; DocumentKeySet removed{}; @@ -65,19 +66,19 @@ DocumentKeySet ReferenceSet::RemoveReferences(int id) { void ReferenceSet::RemoveAllReferences() { auto initial = by_key_; - for (const DocumentReference& reference : initial) { + for (const DocumentKeyReference& reference : initial) { RemoveReference(reference); } } -void ReferenceSet::RemoveReference(const DocumentReference& reference) { +void ReferenceSet::RemoveReference(const DocumentKeyReference& reference) { by_key_ = by_key_.erase(reference); by_id_ = by_id_.erase(reference); } DocumentKeySet ReferenceSet::ReferencedKeys(int id) { - DocumentReference start{DocumentKey::Empty(), id}; - DocumentReference end{DocumentKey::Empty(), id + 1}; + DocumentKeyReference start{DocumentKey::Empty(), id}; + DocumentKeyReference end{DocumentKey::Empty(), id + 1}; DocumentKeySet keys; for (const auto& reference : by_id_.values_in(start, end)) { @@ -89,7 +90,7 @@ DocumentKeySet ReferenceSet::ReferencedKeys(int id) { bool ReferenceSet::ContainsKey(const DocumentKey& key) { // Create a reference with a zero ID as the start position to find any // document reference with this key. - DocumentReference start{key, 0}; + DocumentKeyReference start{key, 0}; auto range = by_key_.values_from(start); auto begin = range.begin(); diff --git a/Firestore/core/src/firebase/firestore/local/reference_set.h b/Firestore/core/src/firebase/firestore/local/reference_set.h index 58677e200ff..0e14c998bbd 100644 --- a/Firestore/core/src/firebase/firestore/local/reference_set.h +++ b/Firestore/core/src/firebase/firestore/local/reference_set.h @@ -18,7 +18,7 @@ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_REFERENCE_SET_H_ #include "Firestore/core/src/firebase/firestore/immutable/sorted_set.h" -#include "Firestore/core/src/firebase/firestore/local/document_reference.h" +#include "Firestore/core/src/firebase/firestore/local/document_key_reference.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_key_set.h" @@ -31,7 +31,7 @@ namespace local { * (either a TargetId or BatchId). As references are added to or removed from * the set corresponding events are emitted to a registered garbage collector. * - * Each reference is represented by a DocumentReference object. Each of them + * Each reference is represented by a DocumentKeyReference object. Each of them * contains enough information to uniquely identify the reference. They are all * stored primarily in a set sorted by key. A document is considered garbage if * there's no references in that set (this can be efficiently checked thanks to @@ -81,10 +81,11 @@ class ReferenceSet { bool ContainsKey(const model::DocumentKey& key); private: - void RemoveReference(const DocumentReference& reference); + void RemoveReference(const DocumentKeyReference& reference); - immutable::SortedSet by_key_; - immutable::SortedSet by_id_; + immutable::SortedSet + by_key_; + immutable::SortedSet by_id_; }; } // namespace local diff --git a/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc b/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc index 98151c1561b..90850021e76 100644 --- a/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc +++ b/Firestore/core/src/firebase/firestore/nanopb/nanopb_string.cc @@ -19,6 +19,8 @@ #include #include +#include "Firestore/core/src/firebase/firestore/nanopb/nanopb_util.h" + namespace firebase { namespace firestore { namespace nanopb { @@ -28,7 +30,7 @@ String::~String() { } /* static */ pb_bytes_array_t* String::MakeBytesArray(absl::string_view value) { - auto size = static_cast(value.size()); + pb_size_t size = CheckedSize(value.size()); // Allocate one extra byte for the null terminator that's not necessarily // there in a string_view. As long as we're making a copy, might as well diff --git a/Firestore/core/src/firebase/firestore/nanopb/nanopb_util.h b/Firestore/core/src/firebase/firestore/nanopb/nanopb_util.h new file mode 100644 index 00000000000..667c28a6b6d --- /dev/null +++ b/Firestore/core/src/firebase/firestore/nanopb/nanopb_util.h @@ -0,0 +1,42 @@ +/* + * Copyright 2019 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_NANOPB_NANOPB_UTIL_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_NANOPB_UTIL_H_ + +#include + +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + +namespace firebase { +namespace firestore { +namespace nanopb { + +/** + * Static casts the given size_t value down to a nanopb compatible size, after + * asserting that the value isn't out of range. + */ +inline pb_size_t CheckedSize(size_t size) { + HARD_ASSERT(size <= PB_SIZE_MAX, + "Size exceeds nanopb limits. Too many entries."); + return static_cast(size); +} + +} // namespace nanopb +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_NANOPB_UTIL_H_ diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc index 9c0c008caea..cd0b9a15c4f 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.cc +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -35,6 +35,7 @@ #include "Firestore/core/src/firebase/firestore/model/field_value.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/nanopb/nanopb_util.h" #include "Firestore/core/src/firebase/firestore/nanopb/reader.h" #include "Firestore/core/src/firebase/firestore/nanopb/writer.h" #include "Firestore/core/src/firebase/firestore/timestamp_internal.h" @@ -67,13 +68,14 @@ using firebase::firestore::model::Precondition; using firebase::firestore::model::ResourcePath; using firebase::firestore::model::SetMutation; using firebase::firestore::model::SnapshotVersion; +using firebase::firestore::nanopb::CheckedSize; using firebase::firestore::nanopb::Reader; using firebase::firestore::nanopb::Writer; using firebase::firestore::util::Status; using firebase::firestore::util::StringFormat; pb_bytes_array_t* Serializer::EncodeString(const std::string& str) { - auto size = static_cast(str.size()); + pb_size_t size = CheckedSize(str.size()); auto result = static_cast(malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(size))); result->size = size; @@ -83,11 +85,12 @@ pb_bytes_array_t* Serializer::EncodeString(const std::string& str) { std::string Serializer::DecodeString(const pb_bytes_array_t* str) { if (str == nullptr) return ""; - return std::string{reinterpret_cast(str->bytes), str->size}; + size_t size = static_cast(str->size); + return std::string{reinterpret_cast(str->bytes), size}; } pb_bytes_array_t* Serializer::EncodeBytes(const std::vector& bytes) { - auto size = static_cast(bytes.size()); + pb_size_t size = CheckedSize(bytes.size()); auto result = static_cast(malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(size))); result->size = size; @@ -150,9 +153,9 @@ FieldValue::Map DecodeFields( google_firestore_v1_MapValue EncodeMapValue(const ObjectValue& object_value) { google_firestore_v1_MapValue result{}; - size_t count = object_value.GetInternalValue().size(); + pb_size_t count = CheckedSize(object_value.GetInternalValue().size()); - result.fields_count = static_cast(count); + result.fields_count = count; result.fields = MakeArray(count); int i = 0; @@ -450,8 +453,8 @@ google_firestore_v1_Document Serializer::EncodeDocument( result.name = EncodeString(EncodeKey(key)); // Encode Document.fields (unless it's empty) - size_t count = object_value.GetInternalValue().size(); - result.fields_count = static_cast(count); + pb_size_t count = CheckedSize(object_value.GetInternalValue().size()); + result.fields_count = count; result.fields = MakeArray(count); int i = 0; for (const auto& kv : object_value.GetInternalValue()) { @@ -701,10 +704,8 @@ google_firestore_v1_DocumentMask Serializer::EncodeDocumentMask( const FieldMask& mask) { google_firestore_v1_DocumentMask result{}; - size_t count = mask.size(); - HARD_ASSERT(count <= PB_SIZE_MAX, - "Unable to encode specified document mask. Too many fields."); - result.field_paths_count = static_cast(count); + pb_size_t count = CheckedSize(mask.size()); + result.field_paths_count = count; result.field_paths = MakeArray(count); int i = 0; @@ -748,8 +749,8 @@ google_firestore_v1_Target_QueryTarget Serializer::EncodeQueryTarget( google_firestore_v1_Target_QueryTarget_structured_query_tag; if (!collection_id.empty()) { - size_t count = 1; - result.structured_query.from_count = static_cast(count); + pb_size_t count = 1; + result.structured_query.from_count = count; result.structured_query.from = MakeArray( count); @@ -894,9 +895,7 @@ google_firestore_v1_ArrayValue Serializer::EncodeArray( const std::vector& array_value) { google_firestore_v1_ArrayValue result{}; - size_t count = array_value.size(); - HARD_ASSERT(count <= PB_SIZE_MAX, - "Unable to encode specified array. Too many entries."); + pb_size_t count = CheckedSize(array_value.size()); result.values_count = count; result.values = MakeArray(count); diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h index df8c2a12ddc..ae011b3defa 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.h +++ b/Firestore/core/src/firebase/firestore/remote/serializer.h @@ -53,7 +53,7 @@ class LocalSerializer; namespace remote { template -T* MakeArray(size_t count) { +T* MakeArray(pb_size_t count) { return reinterpret_cast(calloc(count, sizeof(T))); } From c2a3d04e6da5b0b671d4b11553e832905403130b Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 25 Mar 2019 11:04:21 -0700 Subject: [PATCH 082/214] Port FSTDocumentSet to C++ (#2608) * Replace FSTDocumentSet's guts with immutable::SortedSet. * Port FSTDocumentSet to C++ DocumentSet. * Replace implementation of FSTDocumentSet with C++ DocumentSet * Update all usages of FSTDocumentSet * Remove FSTDocumentSet * Add Objective-C++ guard * Use absl::c_equal instead of hand-rolling it. * Split SortedContainer out of SortedMapBase ... and use it * Fix NSNotFound -> npos * Remove = default constructors that aren't true. These are implicitly deleted because DocumentSet is a member and isn't default constructible. * Fix usage of FSTQuery.limit --- .../Tests/API/FIRQuerySnapshotTests.mm | 7 +- Firestore/Example/Tests/API/FSTAPIHelpers.mm | 17 +- .../Tests/Core/FSTEventManagerTests.mm | 5 +- .../Tests/Core/FSTQueryListenerTests.mm | 88 ++++----- .../Example/Tests/Core/FSTViewSnapshotTest.mm | 10 +- Firestore/Example/Tests/Core/FSTViewTests.mm | 79 +++++--- .../Example/Tests/Local/FSTLocalStoreTests.mm | 2 +- .../Tests/Model/FSTDocumentSetTests.mm | 108 +++++----- Firestore/Example/Tests/Util/FSTHelpers.h | 7 +- Firestore/Example/Tests/Util/FSTHelpers.mm | 9 +- Firestore/Source/API/FIRDocumentChange.mm | 21 +- Firestore/Source/API/FIRDocumentReference.mm | 2 +- .../Source/API/FIRQuerySnapshot+Internal.h | 1 - Firestore/Source/API/FIRQuerySnapshot.mm | 30 +-- Firestore/Source/Core/FSTEventManager.mm | 4 +- Firestore/Source/Core/FSTFirestoreClient.mm | 2 +- Firestore/Source/Core/FSTSyncEngine.mm | 2 +- Firestore/Source/Core/FSTView.h | 3 +- Firestore/Source/Core/FSTView.mm | 60 +++--- Firestore/Source/Local/FSTLocalViewChanges.h | 1 - Firestore/Source/Model/FSTDocumentSet.h | 85 -------- Firestore/Source/Model/FSTDocumentSet.mm | 187 ------------------ .../firestore/api/document_reference.mm | 6 +- .../firebase/firestore/api/query_snapshot.h | 9 +- .../firebase/firestore/api/query_snapshot.mm | 7 +- .../firebase/firestore/core/view_snapshot.h | 18 +- .../firebase/firestore/core/view_snapshot.mm | 34 ++-- .../firestore/immutable/array_sorted_map.h | 2 +- .../firebase/firestore/immutable/llrb_node.h | 2 +- ...sorted_map_base.cc => sorted_container.cc} | 6 +- .../{sorted_map_base.h => sorted_container.h} | 39 ++-- .../firebase/firestore/immutable/sorted_map.h | 6 +- .../firebase/firestore/immutable/sorted_set.h | 6 +- .../firestore/immutable/tree_sorted_map.h | 2 +- .../firebase/firestore/model/document_set.h | 174 ++++++++++++++++ .../firebase/firestore/model/document_set.mm | 121 ++++++++++++ .../firestore/immutable/sorted_map_test.cc | 2 - .../firestore/immutable/sorted_set_test.cc | 5 +- 38 files changed, 613 insertions(+), 556 deletions(-) delete mode 100644 Firestore/Source/Model/FSTDocumentSet.h delete mode 100644 Firestore/Source/Model/FSTDocumentSet.mm rename Firestore/core/src/firebase/firestore/immutable/{sorted_map_base.cc => sorted_container.cc} (89%) rename Firestore/core/src/firebase/firestore/immutable/{sorted_map_base.h => sorted_container.h} (77%) create mode 100644 Firestore/core/src/firebase/firestore/model/document_set.h create mode 100644 Firestore/core/src/firebase/firestore/model/document_set.mm diff --git a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm index edec5367b56..9a45e8c388c 100644 --- a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm +++ b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm @@ -29,15 +29,16 @@ #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; using firebase::firestore::core::DocumentViewChange; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentSet; NS_ASSUME_NONNULL_BEGIN @@ -87,8 +88,8 @@ - (void)testIncludeMetadataChanges { FSTDocument *doc2Old = FSTTestDoc("foo/baz", 1, @{@"a" : @"b"}, FSTDocumentStateSynced); FSTDocument *doc2New = FSTTestDoc("foo/baz", 1, @{@"a" : @"c"}, FSTDocumentStateSynced); - FSTDocumentSet *oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[ doc1Old, doc2Old ]); - FSTDocumentSet *newDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[ doc2New, doc2New ]); + DocumentSet oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[ doc1Old, doc2Old ]); + DocumentSet newDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[ doc2New, doc2New ]); std::vector documentChanges{ DocumentViewChange{doc1New, DocumentViewChange::Type::kMetadata}, DocumentViewChange{doc2New, DocumentViewChange::Type::kModified}, diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.mm b/Firestore/Example/Tests/API/FSTAPIHelpers.mm index bcab27f7bc4..ee9f4d15cb0 100644 --- a/Firestore/Example/Tests/API/FSTAPIHelpers.mm +++ b/Firestore/Example/Tests/API/FSTAPIHelpers.mm @@ -32,9 +32,9 @@ #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" @@ -43,6 +43,7 @@ using firebase::firestore::core::DocumentViewChange; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentSet; NS_ASSUME_NONNULL_BEGIN @@ -98,26 +99,24 @@ bool hasPendingWrites, bool fromCache) { SnapshotMetadata metadata(hasPendingWrites, fromCache); - FSTDocumentSet *oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[]); + DocumentSet oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[]); DocumentKeySet mutatedKeys; for (NSString *key in oldDocs) { - oldDocuments = [oldDocuments - documentSetByAddingDocument:FSTTestDoc(util::StringFormat("%s/%s", path, key), 1, - oldDocs[key], - hasPendingWrites ? FSTDocumentStateLocalMutations - : FSTDocumentStateSynced)]; + oldDocuments = oldDocuments.insert( + FSTTestDoc(util::StringFormat("%s/%s", path, key), 1, oldDocs[key], + hasPendingWrites ? FSTDocumentStateLocalMutations : FSTDocumentStateSynced)); if (hasPendingWrites) { const std::string documentKey = util::StringFormat("%s/%s", path, key); mutatedKeys = mutatedKeys.insert(testutil::Key(documentKey)); } } - FSTDocumentSet *newDocuments = oldDocuments; + DocumentSet newDocuments = oldDocuments; std::vector documentChanges; for (NSString *key in docsToAdd) { FSTDocument *docToAdd = FSTTestDoc(util::StringFormat("%s/%s", path, key), 1, docsToAdd[key], hasPendingWrites ? FSTDocumentStateLocalMutations : FSTDocumentStateSynced); - newDocuments = [newDocuments documentSetByAddingDocument:docToAdd]; + newDocuments = newDocuments.insert(docToAdd); documentChanges.emplace_back(docToAdd, DocumentViewChange::Type::kAdded); if (hasPendingWrites) { const std::string documentKey = util::StringFormat("%s/%s", path, key); diff --git a/Firestore/Example/Tests/Core/FSTEventManagerTests.mm b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm index 11152bd5035..98cfe536bc4 100644 --- a/Firestore/Example/Tests/Core/FSTEventManagerTests.mm +++ b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm @@ -23,18 +23,19 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSyncEngine.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_key_set.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" using firebase::firestore::core::ViewSnapshot; using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentSet; using firebase::firestore::model::OnlineState; using firebase::firestore::util::StatusOr; @@ -106,7 +107,7 @@ - (FSTQueryListener *)queryListenerForQuery:(FSTQuery *)query } - (ViewSnapshot)makeEmptyViewSnapshotWithQuery:(FSTQuery *)query { - FSTDocumentSet *emptyDocs = [FSTDocumentSet documentSetWithComparator:query.comparator]; + DocumentSet emptyDocs{query.comparator}; // sync_state_changed has to be `true` to prevent an assertion about a meaningless view snapshot. return ViewSnapshot{ query, emptyDocs, emptyDocs, {}, DocumentKeySet{}, false, /*sync_state_changed=*/true, false}; diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index 15ebfcaf995..7516a8aaf05 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -24,13 +24,13 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTView.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Util/FSTAsyncQueryListener.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/include/firebase/firestore/firestore_errors.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/remote/remote_event.h" #include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h" @@ -44,6 +44,7 @@ using firebase::firestore::core::ViewSnapshot; using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentSet; using firebase::firestore::model::OnlineState; using firebase::firestore::remote::TargetChange; using firebase::firestore::util::DelayedConstructor; @@ -119,15 +120,14 @@ - (void)testRaisesCollectionEvents { XC_ASSERT_THAT(accum[0].document_changes(), ElementsAre(change1, change2)); XC_ASSERT_THAT(accum[1].document_changes(), ElementsAre(change3)); - ViewSnapshot expectedSnap2{ - snap2.query(), - snap2.documents(), - /*old_documents=*/[FSTDocumentSet documentSetWithComparator : snap2.query().comparator], - /*document_changes=*/{ change1, change4 }, - snap2.mutated_keys(), - snap2.from_cache(), - /*sync_state_changed=*/true, - /*excludes_metadata_changes=*/true}; + ViewSnapshot expectedSnap2{snap2.query(), + snap2.documents(), + /*old_documents=*/DocumentSet{snap2.query().comparator}, + /*document_changes=*/{change1, change4}, + snap2.mutated_keys(), + snap2.from_cache(), + /*sync_state_changed=*/true, + /*excludes_metadata_changes=*/true}; XC_ASSERT_THAT(otherAccum, ElementsAre(expectedSnap2)); } @@ -395,15 +395,14 @@ - (void)testWillWaitForSyncIfOnline { DocumentViewChange change1{doc1, DocumentViewChange::Type::kAdded}; DocumentViewChange change2{doc2, DocumentViewChange::Type::kAdded}; - ViewSnapshot expectedSnap{ - snap3.query(), - snap3.documents(), - /*old_documents=*/[FSTDocumentSet documentSetWithComparator : snap3.query().comparator], - /*document_changes=*/{ change1, change2 }, - snap3.mutated_keys(), - /*from_cache=*/false, - /*sync_state_changed=*/true, - /*excludes_metadata_changes=*/true}; + ViewSnapshot expectedSnap{snap3.query(), + snap3.documents(), + /*old_documents=*/DocumentSet{snap3.query().comparator}, + /*document_changes=*/{change1, change2}, + snap3.mutated_keys(), + /*from_cache=*/false, + /*sync_state_changed=*/true, + /*excludes_metadata_changes=*/true}; XC_ASSERT_THAT(events, ElementsAre(expectedSnap)); } @@ -433,15 +432,14 @@ - (void)testWillRaiseInitialEventWhenGoingOffline { DocumentViewChange change1{doc1, DocumentViewChange::Type::kAdded}; DocumentViewChange change2{doc2, DocumentViewChange::Type::kAdded}; - ViewSnapshot expectedSnap1{ - query, - /*documents=*/snap1.documents(), - /*old_documents=*/[FSTDocumentSet documentSetWithComparator : snap1.query().comparator], - /*document_changes=*/{ change1 }, - snap1.mutated_keys(), - /*from_cache=*/true, - /*sync_state_changed=*/true, - /*excludes_metadata_changes=*/true}; + ViewSnapshot expectedSnap1{query, + /*documents=*/snap1.documents(), + /*old_documents=*/DocumentSet{snap1.query().comparator}, + /*document_changes=*/{change1}, + snap1.mutated_keys(), + /*from_cache=*/true, + /*sync_state_changed=*/true, + /*excludes_metadata_changes=*/true}; ViewSnapshot expectedSnap2{query, /*documents=*/snap2.documents(), @@ -469,15 +467,14 @@ - (void)testWillRaiseInitialEventWhenGoingOfflineAndThereAreNoDocs { [listener queryDidChangeViewSnapshot:snap1]; // no event [listener applyChangedOnlineState:OnlineState::Offline]; // event - ViewSnapshot expectedSnap{ - query, - /*documents=*/snap1.documents(), - /*old_documents=*/[FSTDocumentSet documentSetWithComparator : snap1.query().comparator], - /*document_changes=*/{}, - snap1.mutated_keys(), - /*from_cache=*/true, - /*sync_state_changed=*/true, - /*excludes_metadata_changes=*/true}; + ViewSnapshot expectedSnap{query, + /*documents=*/snap1.documents(), + /*old_documents=*/DocumentSet{snap1.query().comparator}, + /*document_changes=*/{}, + snap1.mutated_keys(), + /*from_cache=*/true, + /*sync_state_changed=*/true, + /*excludes_metadata_changes=*/true}; XC_ASSERT_THAT(events, ElementsAre(expectedSnap)); } @@ -495,15 +492,14 @@ - (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { [listener applyChangedOnlineState:OnlineState::Offline]; // no event [listener queryDidChangeViewSnapshot:snap1]; // event - ViewSnapshot expectedSnap{ - query, - /*documents=*/snap1.documents(), - /*old_documents=*/[FSTDocumentSet documentSetWithComparator : snap1.query().comparator], - /*document_changes=*/{}, - snap1.mutated_keys(), - /*from_cache=*/true, - /*sync_state_changed=*/true, - /*excludes_metadata_changes=*/true}; + ViewSnapshot expectedSnap{query, + /*documents=*/snap1.documents(), + /*old_documents=*/DocumentSet{snap1.query().comparator}, + /*document_changes=*/{}, + snap1.mutated_keys(), + /*from_cache=*/true, + /*sync_state_changed=*/true, + /*excludes_metadata_changes=*/true}; XC_ASSERT_THAT(events, ElementsAre(expectedSnap)); } diff --git a/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm b/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm index 6dbdd805b4e..bd13b0ccccb 100644 --- a/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm +++ b/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm @@ -20,16 +20,17 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" using firebase::firestore::core::DocumentViewChange; using firebase::firestore::core::DocumentViewChangeSet; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentSet; NS_ASSUME_NONNULL_BEGIN @@ -100,10 +101,9 @@ - (void)testTrack { - (void)testViewSnapshotConstructor { FSTQuery *query = FSTTestQuery("a"); - FSTDocumentSet *documents = [FSTDocumentSet documentSetWithComparator:FSTDocumentComparatorByKey]; - FSTDocumentSet *oldDocuments = documents; - documents = - [documents documentSetByAddingDocument:FSTTestDoc("c/a", 1, @{}, FSTDocumentStateSynced)]; + DocumentSet documents = DocumentSet{FSTDocumentComparatorByKey}; + DocumentSet oldDocuments = documents; + documents = documents.insert(FSTTestDoc("c/a", 1, @{}, FSTDocumentStateSynced)); std::vector documentChanges{DocumentViewChange{ FSTTestDoc("c/a", 1, @{}, FSTDocumentStateSynced), DocumentViewChange::Type::kAdded}}; diff --git a/Firestore/Example/Tests/Core/FSTViewTests.mm b/Firestore/Example/Tests/Core/FSTViewTests.mm index 2bd80aab592..4245d072e6f 100644 --- a/Firestore/Example/Tests/Core/FSTViewTests.mm +++ b/Firestore/Example/Tests/Core/FSTViewTests.mm @@ -18,18 +18,19 @@ #import +#include #include #include #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" #include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h" @@ -40,10 +41,33 @@ using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::ResourcePath; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentSet; using testing::ElementsAre; NS_ASSUME_NONNULL_BEGIN +/** + * A custom matcher that verifies that the subject has the same keys as the given documents without + * verifying that the contents are the same. + */ +MATCHER_P(ContainsDocs, expected, "") { + if (expected.size() != arg.size()) { + return false; + } + for (FSTDocument *doc : expected) { + if (!arg.ContainsKey(doc.key)) { + return false; + } + } + return true; +} + +/** Constructs `ContainsDocs` instances with an initializer list. */ +inline ContainsDocsMatcherP> ContainsDocs( + std::vector docs) { + return ContainsDocsMatcherP>(docs); +} + @interface FSTViewTests : XCTestCase @end @@ -72,7 +96,7 @@ - (void)testAddsDocumentsBasedOnQuery { XCTAssertEqual(snapshot.query(), query); - XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ doc1, doc2 ])); + XC_ASSERT_THAT(snapshot.documents(), ElementsAre(doc1, doc2)); XCTAssertTrue(( snapshot.document_changes() == @@ -107,7 +131,7 @@ - (void)testRemovesDocuments { XCTAssertEqual(snapshot.query(), query); - XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ doc1, doc3 ])); + XC_ASSERT_THAT(snapshot.documents(), ElementsAre(doc1, doc3)); XCTAssertTrue(( snapshot.document_changes() == @@ -170,7 +194,7 @@ - (void)testFiltersDocumentsBasedOnQueryWithFilter { XCTAssertEqual(snapshot.query(), query); - XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ doc1, doc5, doc2 ])); + XC_ASSERT_THAT(snapshot.documents(), ElementsAre(doc1, doc5, doc2)); XCTAssertTrue(( snapshot.document_changes() == @@ -204,7 +228,7 @@ - (void)testUpdatesDocumentsBasedOnQueryWithFilter { XCTAssertEqual(snapshot.query(), query); - XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ doc1, doc3 ])); + XC_ASSERT_THAT(snapshot.documents(), ElementsAre(doc1, doc3)); FSTDocument *newDoc2 = FSTTestDoc("rooms/eros/messages/2", 1, @{@"sort" : @2}, FSTDocumentStateSynced); @@ -217,7 +241,7 @@ - (void)testUpdatesDocumentsBasedOnQueryWithFilter { XCTAssertEqual(snapshot.query(), query); - XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ newDoc4, doc1, newDoc2 ])); + XC_ASSERT_THAT(snapshot.documents(), ElementsAre(newDoc4, doc1, newDoc2)); XC_ASSERT_THAT(snapshot.document_changes(), ElementsAre(DocumentViewChange{doc3, DocumentViewChange::Type::kRemoved}, @@ -251,7 +275,7 @@ - (void)testRemovesDocumentsForQueryWithLimit { XCTAssertEqual(snapshot.query(), query); - XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ doc1, doc2 ])); + XC_ASSERT_THAT(snapshot.documents(), ElementsAre(doc1, doc2)); XCTAssertTrue(( snapshot.document_changes() == @@ -302,7 +326,7 @@ - (void)testDoesntReportChangesForDocumentBeyondLimitOfQuery { XCTAssertEqual(snapshot.query(), query); - XCTAssertEqualObjects(snapshot.documents().arrayValue, (@[ doc1, doc3 ])); + XC_ASSERT_THAT(snapshot.documents(), ElementsAre(doc1, doc3)); XC_ASSERT_THAT(snapshot.document_changes(), ElementsAre(DocumentViewChange{doc2, DocumentViewChange::Type::kRemoved}, @@ -372,13 +396,6 @@ - (void)testResumingQueryCreatesNoLimbos { XCTAssertEqualObjects(change.limboChanges, @[]); } -- (void)assertDocSet:(FSTDocumentSet *)docSet containsDocs:(NSArray *)docs { - XCTAssertEqual(docs.count, docSet.count); - for (FSTDocument *doc in docs) { - XCTAssertTrue([docSet containsKey:doc.key]); - } -} - - (void)testReturnsNeedsRefillOnDeleteInLimitQuery { FSTQuery *query = [[self queryForMessages] queryBySettingLimit:2]; FSTDocument *doc1 = FSTTestDoc("rooms/eros/messages/0", 0, @{}, FSTDocumentStateSynced); @@ -388,7 +405,7 @@ - (void)testReturnsNeedsRefillOnDeleteInLimitQuery { // Start with a full view. FSTViewDocumentChanges *changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(2, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -396,12 +413,12 @@ - (void)testReturnsNeedsRefillOnDeleteInLimitQuery { // Remove one of the docs. changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ FSTTestDeletedDoc( "rooms/eros/messages/0", 0, NO) ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc2})); XCTAssertTrue(changes.needsRefill); XCTAssertEqual(1, changes.changeSet.GetChanges().size()); // Refill it with just the one doc remaining. changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc2 ]) previousChanges:changes]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc2})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(1, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -424,7 +441,7 @@ - (void)testReturnsNeedsRefillOnReorderInLimitQuery { // Start with a full view. FSTViewDocumentChanges *changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2, doc3 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(2, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -432,13 +449,13 @@ - (void)testReturnsNeedsRefillOnReorderInLimitQuery { // Move one of the docs. doc2 = FSTTestDoc("rooms/eros/messages/1", 1, @{@"order" : @2000}, FSTDocumentStateSynced); changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc2 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2})); XCTAssertTrue(changes.needsRefill); XCTAssertEqual(1, changes.changeSet.GetChanges().size()); // Refill it with all three current docs. changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2, doc3 ]) previousChanges:changes]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc3 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc3})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(2, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -465,7 +482,7 @@ - (void)testDoesntNeedRefillOnReorderWithinLimit { // Start with a full view. FSTViewDocumentChanges *changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2, doc3, doc4, doc5 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2, doc3 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2, doc3})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(3, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -473,7 +490,7 @@ - (void)testDoesntNeedRefillOnReorderWithinLimit { // Move one of the docs. doc1 = FSTTestDoc("rooms/eros/messages/0", 1, @{@"order" : @3}, FSTDocumentStateSynced); changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc2, doc3, doc1 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc2, doc3, doc1})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(1, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -500,7 +517,7 @@ - (void)testDoesntNeedRefillOnReorderAfterLimitQuery { // Start with a full view. FSTViewDocumentChanges *changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2, doc3, doc4, doc5 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2, doc3 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2, doc3})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(3, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -508,7 +525,7 @@ - (void)testDoesntNeedRefillOnReorderAfterLimitQuery { // Move one of the docs. doc4 = FSTTestDoc("rooms/eros/messages/3", 1, @{@"order" : @6}, FSTDocumentStateSynced); changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc4 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2, doc3 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2, doc3})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(0, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -523,7 +540,7 @@ - (void)testDoesntNeedRefillForAdditionAfterTheLimit { // Start with a full view. FSTViewDocumentChanges *changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(2, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -531,7 +548,7 @@ - (void)testDoesntNeedRefillForAdditionAfterTheLimit { // Add a doc that is past the limit. FSTDocument *doc3 = FSTTestDoc("rooms/eros/messages/2", 1, @{}, FSTDocumentStateSynced); changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc3 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(0, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -545,7 +562,7 @@ - (void)testDoesntNeedRefillForDeletionsWhenNotNearTheLimit { FSTViewDocumentChanges *changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(2, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -553,7 +570,7 @@ - (void)testDoesntNeedRefillForDeletionsWhenNotNearTheLimit { // Remove one of the docs. changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ FSTTestDeletedDoc( "rooms/eros/messages/1", 0, NO) ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(1, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -568,7 +585,7 @@ - (void)testHandlesApplyingIrrelevantDocs { // Start with a full view. FSTViewDocumentChanges *changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ doc1, doc2 ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(2, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; @@ -576,7 +593,7 @@ - (void)testHandlesApplyingIrrelevantDocs { // Remove a doc that isn't even in the results. changes = [view computeChangesWithDocuments:FSTTestDocUpdates(@[ FSTTestDeletedDoc( "rooms/eros/messages/2", 0, NO) ])]; - [self assertDocSet:changes.documentSet containsDocs:@[ doc1, doc2 ]]; + XC_ASSERT_THAT(changes.documentSet, ContainsDocs({doc1, doc2})); XCTAssertFalse(changes.needsRefill); XCTAssertEqual(0, changes.changeSet.GetChanges().size()); [view applyChangesToDocuments:changes]; diff --git a/Firestore/Example/Tests/Local/FSTLocalStoreTests.mm b/Firestore/Example/Tests/Local/FSTLocalStoreTests.mm index 15d30c7d6eb..79d906a5d18 100644 --- a/Firestore/Example/Tests/Local/FSTLocalStoreTests.mm +++ b/Firestore/Example/Tests/Local/FSTLocalStoreTests.mm @@ -28,7 +28,6 @@ #import "Firestore/Source/Local/FSTPersistence.h" #import "Firestore/Source/Local/FSTQueryData.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Model/FSTMutationBatch.h" #import "Firestore/Source/Util/FSTClasses.h" @@ -40,6 +39,7 @@ #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/model/document_map.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/remote/remote_event.h" #include "Firestore/core/src/firebase/firestore/remote/watch_change.h" #include "Firestore/core/src/firebase/firestore/util/status.h" diff --git a/Firestore/Example/Tests/Model/FSTDocumentSetTests.mm b/Firestore/Example/Tests/Model/FSTDocumentSetTests.mm index 05be1b7eeab..edb2334f9b4 100644 --- a/Firestore/Example/Tests/Model/FSTDocumentSetTests.mm +++ b/Firestore/Example/Tests/Model/FSTDocumentSetTests.mm @@ -14,13 +14,19 @@ * limitations under the License. */ -#import "Firestore/Source/Model/FSTDocumentSet.h" - #import -#import "Firestore/Source/Model/FSTDocument.h" +#include #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#import "Firestore/Source/Model/FSTDocument.h" + +// TODO(wilhuff) move to first include once this test filename matches +#include "Firestore/core/src/firebase/firestore/model/document_set.h" +#include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h" + +using firebase::firestore::model::DocumentSet; +using testing::ElementsAre; NS_ASSUME_NONNULL_BEGIN @@ -44,91 +50,93 @@ - (void)setUp { } - (void)testCount { - XCTAssertEqual([FSTTestDocSet(_comp, @[]) count], 0); - XCTAssertEqual([FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]) count], 3); + XCTAssertEqual(FSTTestDocSet(_comp, @[]).size(), 0); + XCTAssertEqual(FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]).size(), 3); } - (void)testHasKey { - FSTDocumentSet *set = FSTTestDocSet(_comp, @[ _doc1, _doc2 ]); + DocumentSet set = FSTTestDocSet(_comp, @[ _doc1, _doc2 ]); - XCTAssertTrue([set containsKey:_doc1.key]); - XCTAssertTrue([set containsKey:_doc2.key]); - XCTAssertFalse([set containsKey:_doc3.key]); + XCTAssertTrue(set.ContainsKey(_doc1.key)); + XCTAssertTrue(set.ContainsKey(_doc2.key)); + XCTAssertFalse(set.ContainsKey(_doc3.key)); } - (void)testDocumentForKey { - FSTDocumentSet *set = FSTTestDocSet(_comp, @[ _doc1, _doc2 ]); + DocumentSet set = FSTTestDocSet(_comp, @[ _doc1, _doc2 ]); - XCTAssertEqualObjects([set documentForKey:_doc1.key], _doc1); - XCTAssertEqualObjects([set documentForKey:_doc2.key], _doc2); - XCTAssertNil([set documentForKey:_doc3.key]); + XCTAssertEqualObjects(set.GetDocument(_doc1.key), _doc1); + XCTAssertEqualObjects(set.GetDocument(_doc2.key), _doc2); + XCTAssertNil(set.GetDocument(_doc3.key)); } - (void)testFirstAndLastDocument { - FSTDocumentSet *set = FSTTestDocSet(_comp, @[]); - XCTAssertNil([set firstDocument]); - XCTAssertNil([set lastDocument]); + DocumentSet set = FSTTestDocSet(_comp, @[]); + XCTAssertNil(set.GetFirstDocument()); + XCTAssertNil(set.GetLastDocument()); set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); - XCTAssertEqualObjects([set firstDocument], _doc3); - XCTAssertEqualObjects([set lastDocument], _doc2); + XCTAssertEqualObjects(set.GetFirstDocument(), _doc3); + XCTAssertEqualObjects(set.GetLastDocument(), _doc2); } - (void)testKeepsDocumentsInTheRightOrder { - FSTDocumentSet *set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); - XCTAssertEqualObjects([[set documentEnumerator] allObjects], (@[ _doc3, _doc1, _doc2 ])); + DocumentSet set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); + XC_ASSERT_THAT(set, ElementsAre(_doc3, _doc1, _doc2)); } - (void)testDeletes { - FSTDocumentSet *set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); + DocumentSet set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); - FSTDocumentSet *setWithoutDoc1 = [set documentSetByRemovingKey:_doc1.key]; - XCTAssertEqualObjects([[setWithoutDoc1 documentEnumerator] allObjects], (@[ _doc3, _doc2 ])); - XCTAssertEqual([setWithoutDoc1 count], 2); + DocumentSet setWithoutDoc1 = set.erase(_doc1.key); + XC_ASSERT_THAT(setWithoutDoc1, ElementsAre(_doc3, _doc2)); + XCTAssertEqual(setWithoutDoc1.size(), 2); // Original remains unchanged - XCTAssertEqualObjects([[set documentEnumerator] allObjects], (@[ _doc3, _doc1, _doc2 ])); + XC_ASSERT_THAT(set, ElementsAre(_doc3, _doc1, _doc2)); - FSTDocumentSet *setWithoutDoc3 = [setWithoutDoc1 documentSetByRemovingKey:_doc3.key]; - XCTAssertEqualObjects([[setWithoutDoc3 documentEnumerator] allObjects], (@[ _doc2 ])); - XCTAssertEqual([setWithoutDoc3 count], 1); + DocumentSet setWithoutDoc3 = setWithoutDoc1.erase(_doc3.key); + XC_ASSERT_THAT(setWithoutDoc3, ElementsAre(_doc2)); + XCTAssertEqual(setWithoutDoc3.size(), 1); } - (void)testUpdates { - FSTDocumentSet *set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); + DocumentSet set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); FSTDocument *doc2Prime = FSTTestDoc("docs/2", 0, @{@"sort" : @9}, FSTDocumentStateSynced); - set = [set documentSetByAddingDocument:doc2Prime]; - XCTAssertEqual([set count], 3); - XCTAssertEqualObjects([set documentForKey:doc2Prime.key], doc2Prime); - XCTAssertEqualObjects([[set documentEnumerator] allObjects], (@[ _doc3, _doc1, doc2Prime ])); + set = set.insert(doc2Prime); + XCTAssertEqual(set.size(), 3); + XCTAssertEqualObjects(set.GetDocument(doc2Prime.key), doc2Prime); + XC_ASSERT_THAT(set, ElementsAre(_doc3, _doc1, doc2Prime)); } - (void)testAddsDocsWithEqualComparisonValues { FSTDocument *doc4 = FSTTestDoc("docs/4", 0, @{@"sort" : @2}, FSTDocumentStateSynced); - FSTDocumentSet *set = FSTTestDocSet(_comp, @[ _doc1, doc4 ]); - XCTAssertEqualObjects([[set documentEnumerator] allObjects], (@[ _doc1, doc4 ])); + DocumentSet set = FSTTestDocSet(_comp, @[ _doc1, doc4 ]); + XC_ASSERT_THAT(set, ElementsAre(_doc1, doc4)); } - (void)testIsEqual { - FSTDocumentSet *set1 = FSTTestDocSet(FSTDocumentComparatorByKey, @[ _doc1, _doc2, _doc3 ]); - FSTDocumentSet *set2 = FSTTestDocSet(FSTDocumentComparatorByKey, @[ _doc1, _doc2, _doc3 ]); - XCTAssertEqualObjects(set1, set1); - XCTAssertEqualObjects(set1, set2); - XCTAssertNotEqualObjects(set1, nil); - - FSTDocumentSet *sortedSet1 = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); - FSTDocumentSet *sortedSet2 = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); - XCTAssertEqualObjects(sortedSet1, sortedSet1); - XCTAssertEqualObjects(sortedSet1, sortedSet2); - XCTAssertNotEqualObjects(sortedSet1, nil); - - FSTDocumentSet *shortSet = FSTTestDocSet(FSTDocumentComparatorByKey, @[ _doc1, _doc2 ]); - XCTAssertNotEqualObjects(set1, shortSet); - XCTAssertNotEqualObjects(set1, sortedSet1); + DocumentSet empty{FSTDocumentComparatorByKey}; + DocumentSet set1 = FSTTestDocSet(FSTDocumentComparatorByKey, @[ _doc1, _doc2, _doc3 ]); + DocumentSet set2 = FSTTestDocSet(FSTDocumentComparatorByKey, @[ _doc1, _doc2, _doc3 ]); + XCTAssertEqual(set1, set1); + XCTAssertEqual(set1, set2); + XCTAssertNotEqual(set1, empty); + + DocumentSet sortedSet1 = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); + DocumentSet sortedSet2 = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); + XCTAssertEqual(sortedSet1, sortedSet1); + XCTAssertEqual(sortedSet1, sortedSet2); + XCTAssertNotEqual(sortedSet1, empty); + + DocumentSet shortSet = FSTTestDocSet(FSTDocumentComparatorByKey, @[ _doc1, _doc2 ]); + XCTAssertNotEqual(set1, shortSet); + XCTAssertNotEqual(set1, sortedSet1); } + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Util/FSTHelpers.h b/Firestore/Example/Tests/Util/FSTHelpers.h index 8a485f2dca8..a56b9b7eac1 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.h +++ b/Firestore/Example/Tests/Util/FSTHelpers.h @@ -24,6 +24,7 @@ #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_map.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" @@ -37,7 +38,6 @@ @class FSTDeletedDocument; @class FSTDocument; @class FSTDocumentKeyReference; -@class FSTDocumentSet; @class FSTFieldValue; @class FSTFilter; @class FSTLocalViewChanges; @@ -275,10 +275,11 @@ FSTSortOrder *FSTTestOrderBy(const absl::string_view field, NSString *direction) NSComparator FSTTestDocComparator(const absl::string_view fieldPath); /** - * Creates a FSTDocumentSet based on the given comparator, initially containing the given + * Creates a DocumentSet based on the given comparator, initially containing the given * documents. */ -FSTDocumentSet *FSTTestDocSet(NSComparator comp, NSArray *docs); +firebase::firestore::model::DocumentSet FSTTestDocSet(NSComparator comp, + NSArray *docs); /** Computes changes to the view with the docs and then applies them and returns the snapshot. */ absl::optional FSTTestApplyChanges( diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm index 12facfbfcc2..db77d439c9e 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.mm +++ b/Firestore/Example/Tests/Util/FSTHelpers.mm @@ -32,7 +32,6 @@ #import "Firestore/Source/Local/FSTLocalViewChanges.h" #import "Firestore/Source/Local/FSTQueryData.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Model/FSTMutation.h" @@ -40,6 +39,7 @@ #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/document_key_set.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.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" @@ -59,6 +59,7 @@ using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentSet; using firebase::firestore::model::FieldMask; using firebase::firestore::model::FieldPath; using firebase::firestore::model::FieldTransform; @@ -236,10 +237,10 @@ NSComparator FSTTestDocComparator(const absl::string_view fieldPath) { return [query comparator]; } -FSTDocumentSet *FSTTestDocSet(NSComparator comp, NSArray *docs) { - FSTDocumentSet *docSet = [FSTDocumentSet documentSetWithComparator:comp]; +DocumentSet FSTTestDocSet(NSComparator comp, NSArray *docs) { + DocumentSet docSet{comp}; for (FSTDocument *doc in docs) { - docSet = [docSet documentSetByAddingDocument:doc]; + docSet = docSet.insert(doc); } return docSet; } diff --git a/Firestore/Source/API/FIRDocumentChange.mm b/Firestore/Source/API/FIRDocumentChange.mm index ffcf645cfb8..1a5bccd20bb 100644 --- a/Firestore/Source/API/FIRDocumentChange.mm +++ b/Firestore/Source/API/FIRDocumentChange.mm @@ -20,14 +20,15 @@ #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" using firebase::firestore::api::Firestore; using firebase::firestore::core::DocumentViewChange; using firebase::firestore::core::ViewSnapshot; +using firebase::firestore::model::DocumentSet; NS_ASSUME_NONNULL_BEGIN @@ -59,7 +60,7 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(const DocumentViewChange & + (NSArray *)documentChangesForSnapshot:(const ViewSnapshot &)snapshot includeMetadataChanges:(bool)includeMetadataChanges firestore:(Firestore *)firestore { - if (snapshot.old_documents().isEmpty) { + if (snapshot.old_documents().empty()) { // Special case the first snapshot because index calculation is easy and fast. Also all changes // on the first snapshot are adds so there are also no metadata-only changes to filter out. FSTDocument *_Nullable lastDocument = nil; @@ -86,7 +87,7 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(const DocumentViewChange & } else { // A DocumentSet that is updated incrementally as changes are applied to use to lookup the index // of a document. - FSTDocumentSet *indexTracker = snapshot.old_documents(); + DocumentSet indexTracker = snapshot.old_documents(); NSMutableArray *changes = [NSMutableArray array]; for (const DocumentViewChange &change : snapshot.document_changes()) { if (!includeMetadataChanges && change.type() == DocumentViewChange::Type::kMetadata) { @@ -100,16 +101,16 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(const DocumentViewChange & fromCache:snapshot.from_cache() hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)]; - NSUInteger oldIndex = NSNotFound; - NSUInteger newIndex = NSNotFound; + size_t oldIndex = DocumentSet::npos; + size_t newIndex = DocumentSet::npos; if (change.type() != DocumentViewChange::Type::kAdded) { - oldIndex = [indexTracker indexOfKey:change.document().key]; - HARD_ASSERT(oldIndex != NSNotFound, "Index for document not found"); - indexTracker = [indexTracker documentSetByRemovingKey:change.document().key]; + oldIndex = indexTracker.IndexOf(change.document().key); + HARD_ASSERT(oldIndex != DocumentSet::npos, "Index for document not found"); + indexTracker = indexTracker.erase(change.document().key); } if (change.type() != DocumentViewChange::Type::kRemoved) { - indexTracker = [indexTracker documentSetByAddingDocument:change.document()]; - newIndex = [indexTracker indexOfKey:change.document().key]; + indexTracker = indexTracker.insert(change.document()); + newIndex = indexTracker.IndexOf(change.document().key); } [FIRDocumentChange documentChangeTypeForChange:change]; FIRDocumentChangeType type = [FIRDocumentChange documentChangeTypeForChange:change]; diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index ef5cce388bf..ac494db2e1d 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -29,13 +29,13 @@ #import "Firestore/Source/API/FSTUserDataConverter.h" #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/api/document_reference.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.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/error_apple.h" diff --git a/Firestore/Source/API/FIRQuerySnapshot+Internal.h b/Firestore/Source/API/FIRQuerySnapshot+Internal.h index 37c953d7d33..96589d1a3dd 100644 --- a/Firestore/Source/API/FIRQuerySnapshot+Internal.h +++ b/Firestore/Source/API/FIRQuerySnapshot+Internal.h @@ -23,7 +23,6 @@ @class FIRFirestore; @class FIRSnapshotMetadata; -@class FSTDocumentSet; @class FSTQuery; using firebase::firestore::api::Firestore; diff --git a/Firestore/Source/API/FIRQuerySnapshot.mm b/Firestore/Source/API/FIRQuerySnapshot.mm index 837055612d3..52a4810575c 100644 --- a/Firestore/Source/API/FIRQuerySnapshot.mm +++ b/Firestore/Source/API/FIRQuerySnapshot.mm @@ -26,19 +26,21 @@ #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" +#include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h" using firebase::firestore::api::Firestore; using firebase::firestore::api::QuerySnapshot; using firebase::firestore::core::ViewSnapshot; +using firebase::firestore::util::DelayedConstructor; NS_ASSUME_NONNULL_BEGIN @implementation FIRQuerySnapshot { - QuerySnapshot _snapshot; + DelayedConstructor _snapshot; FIRSnapshotMetadata *_cached_metadata; @@ -52,7 +54,7 @@ @implementation FIRQuerySnapshot { - (instancetype)initWithSnapshot:(QuerySnapshot &&)snapshot { if (self = [super init]) { - _snapshot = std::move(snapshot); + _snapshot.Init(std::move(snapshot)); } return self; } @@ -70,21 +72,21 @@ - (BOOL)isEqual:(nullable id)other { if (![other isKindOfClass:[FIRQuerySnapshot class]]) return NO; FIRQuerySnapshot *otherSnapshot = other; - return _snapshot == otherSnapshot->_snapshot; + return *_snapshot == *(otherSnapshot->_snapshot); } - (NSUInteger)hash { - return _snapshot.Hash(); + return _snapshot->Hash(); } - (FIRQuery *)query { - FIRFirestore *firestore = [FIRFirestore recoverFromFirestore:_snapshot.firestore()]; - return [FIRQuery referenceWithQuery:_snapshot.internal_query() firestore:firestore]; + FIRFirestore *firestore = [FIRFirestore recoverFromFirestore:_snapshot->firestore()]; + return [FIRQuery referenceWithQuery:_snapshot->internal_query() firestore:firestore]; } - (FIRSnapshotMetadata *)metadata { if (!_cached_metadata) { - _cached_metadata = [[FIRSnapshotMetadata alloc] initWithMetadata:_snapshot.metadata()]; + _cached_metadata = [[FIRSnapshotMetadata alloc] initWithMetadata:_snapshot->metadata()]; } return _cached_metadata; } @@ -92,20 +94,20 @@ - (FIRSnapshotMetadata *)metadata { @dynamic empty; - (BOOL)isEmpty { - return _snapshot.empty(); + return _snapshot->empty(); } // This property is exposed as an NSInteger instead of an NSUInteger since (as of Xcode 8.1) // Swift bridges NSUInteger as UInt, and we want to avoid forcing Swift users to cast their ints // where we can. See cr/146959032 for additional context. - (NSInteger)count { - return static_cast(_snapshot.size()); + return static_cast(_snapshot->size()); } - (NSArray *)documents { if (!_documents) { NSMutableArray *result = [NSMutableArray array]; - _snapshot.ForEachDocument([&result](DocumentSnapshot snapshot) { + _snapshot->ForEachDocument([&result](DocumentSnapshot snapshot) { [result addObject:[[FIRQueryDocumentSnapshot alloc] initWithSnapshot:std::move(snapshot)]]; }); @@ -120,16 +122,16 @@ - (NSInteger)count { - (NSArray *)documentChangesWithIncludeMetadataChanges: (BOOL)includeMetadataChanges { - if (includeMetadataChanges && _snapshot.view_snapshot().excludes_metadata_changes()) { + if (includeMetadataChanges && _snapshot->view_snapshot().excludes_metadata_changes()) { FSTThrowInvalidArgument( @"To include metadata changes with your document changes, you must call " @"addSnapshotListener(includeMetadataChanges: true)."); } if (!_documentChanges || _documentChangesIncludeMetadataChanges != includeMetadataChanges) { - _documentChanges = [FIRDocumentChange documentChangesForSnapshot:_snapshot.view_snapshot() + _documentChanges = [FIRDocumentChange documentChangesForSnapshot:_snapshot->view_snapshot() includeMetadataChanges:includeMetadataChanges - firestore:_snapshot.firestore()]; + firestore:_snapshot->firestore()]; _documentChangesIncludeMetadataChanges = includeMetadataChanges; } return _documentChanges; diff --git a/Firestore/Source/Core/FSTEventManager.mm b/Firestore/Source/Core/FSTEventManager.mm index 384dbb2910b..df69f05b243 100644 --- a/Firestore/Source/Core/FSTEventManager.mm +++ b/Firestore/Source/Core/FSTEventManager.mm @@ -21,8 +21,8 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSyncEngine.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/error_apple.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/status.h" @@ -216,7 +216,7 @@ - (BOOL)shouldRaiseInitialEventForSnapshot:(const ViewSnapshot &)snapshot } // Raise data from cache if we have any documents or we are offline - return !snapshot.documents().isEmpty || onlineState == OnlineState::Offline; + return !snapshot.documents().empty() || onlineState == OnlineState::Offline; } - (BOOL)shouldRaiseEventForSnapshot:(const ViewSnapshot &)snapshot { diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index 35d593bf318..76af1cd5d17 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -39,13 +39,13 @@ #import "Firestore/Source/Local/FSTLocalStore.h" #import "Firestore/Source/Local/FSTMemoryPersistence.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import "Firestore/Source/Util/FSTClasses.h" #include "Firestore/core/src/firebase/firestore/auth/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/document_set.h" #include "Firestore/core/src/firebase/firestore/remote/datastore.h" #include "Firestore/core/src/firebase/firestore/remote/remote_store.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" diff --git a/Firestore/Source/Core/FSTSyncEngine.mm b/Firestore/Source/Core/FSTSyncEngine.mm index 728933b425d..b00982fea36 100644 --- a/Firestore/Source/Core/FSTSyncEngine.mm +++ b/Firestore/Source/Core/FSTSyncEngine.mm @@ -31,7 +31,6 @@ #import "Firestore/Source/Local/FSTLocalWriteResult.h" #import "Firestore/Source/Local/FSTQueryData.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTMutationBatch.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" @@ -41,6 +40,7 @@ #include "Firestore/core/src/firebase/firestore/local/reference_set.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_map.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/remote/remote_event.h" #include "Firestore/core/src/firebase/firestore/util/error_apple.h" diff --git a/Firestore/Source/Core/FSTView.h b/Firestore/Source/Core/FSTView.h index f6048f9d7ca..aed87321503 100644 --- a/Firestore/Source/Core/FSTView.h +++ b/Firestore/Source/Core/FSTView.h @@ -34,7 +34,6 @@ class TargetChange; } // namespace firestore } // namespace firebase -@class FSTDocumentSet; @class FSTQuery; NS_ASSUME_NONNULL_BEGIN @@ -49,7 +48,7 @@ NS_ASSUME_NONNULL_BEGIN - (const firebase::firestore::model::DocumentKeySet &)mutatedKeys; /** The new set of docs that should be in the view. */ -@property(nonatomic, strong, readonly) FSTDocumentSet *documentSet; +- (const firebase::firestore::model::DocumentSet &)documentSet; /** The diff of this these docs with the previous set of docs. */ - (const firebase::firestore::core::DocumentViewChangeSet &)changeSet; diff --git a/Firestore/Source/Core/FSTView.mm b/Firestore/Source/Core/FSTView.mm index f839a5682b8..079c547ad27 100644 --- a/Firestore/Source/Core/FSTView.mm +++ b/Firestore/Source/Core/FSTView.mm @@ -22,12 +22,14 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTFieldValue.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/document_key_set.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/remote/remote_event.h" +#include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" using firebase::firestore::core::DocumentViewChange; @@ -36,9 +38,11 @@ using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::model::DocumentSet; using firebase::firestore::model::MaybeDocumentMap; using firebase::firestore::model::OnlineState; using firebase::firestore::remote::TargetChange; +using firebase::firestore::util::DelayedConstructor; NS_ASSUME_NONNULL_BEGIN @@ -68,7 +72,7 @@ int GetDocumentViewChangeTypePosition(DocumentViewChange::Type changeType) { /** The result of applying a set of doc changes to a view. */ @interface FSTViewDocumentChanges () -- (instancetype)initWithDocumentSet:(FSTDocumentSet *)documentSet +- (instancetype)initWithDocumentSet:(DocumentSet)documentSet changeSet:(DocumentViewChangeSet &&)changeSet needsRefill:(BOOL)needsRefill mutatedKeys:(DocumentKeySet)mutatedKeys NS_DESIGNATED_INITIALIZER; @@ -76,17 +80,18 @@ - (instancetype)initWithDocumentSet:(FSTDocumentSet *)documentSet @end @implementation FSTViewDocumentChanges { + DelayedConstructor _documentSet; DocumentKeySet _mutatedKeys; DocumentViewChangeSet _changeSet; } -- (instancetype)initWithDocumentSet:(FSTDocumentSet *)documentSet +- (instancetype)initWithDocumentSet:(DocumentSet)documentSet changeSet:(DocumentViewChangeSet &&)changeSet needsRefill:(BOOL)needsRefill mutatedKeys:(DocumentKeySet)mutatedKeys { self = [super init]; if (self) { - _documentSet = documentSet; + _documentSet.Init(std::move(documentSet)); _changeSet = std::move(changeSet); _needsRefill = needsRefill; _mutatedKeys = std::move(mutatedKeys); @@ -98,6 +103,10 @@ - (instancetype)initWithDocumentSet:(FSTDocumentSet *)documentSet return _mutatedKeys; } +- (const firebase::firestore::model::DocumentSet &)documentSet { + return *_documentSet; +} + - (const firebase::firestore::core::DocumentViewChangeSet &)changeSet { return _changeSet; } @@ -208,11 +217,11 @@ @interface FSTView () */ @property(nonatomic, assign, getter=isCurrent) BOOL current; -@property(nonatomic, strong) FSTDocumentSet *documentSet; - @end @implementation FSTView { + DelayedConstructor _documentSet; + /** Documents included in the remote target. */ DocumentKeySet _syncedDocuments; @@ -227,7 +236,7 @@ - (instancetype)initWithQuery:(FSTQuery *)query remoteDocuments:(DocumentKeySet) self = [super init]; if (self) { _query = query; - _documentSet = [FSTDocumentSet documentSetWithComparator:query.comparator]; + _documentSet.Init(query.comparator); _syncedDocuments = std::move(remoteDocuments); } return self; @@ -248,11 +257,11 @@ - (FSTViewDocumentChanges *)computeChangesWithDocuments:(const MaybeDocumentMap if (previousChanges) { changeSet = previousChanges.changeSet; } - FSTDocumentSet *oldDocumentSet = previousChanges ? previousChanges.documentSet : self.documentSet; + DocumentSet oldDocumentSet = previousChanges ? previousChanges.documentSet : *_documentSet; DocumentKeySet newMutatedKeys = previousChanges ? previousChanges.mutatedKeys : _mutatedKeys; DocumentKeySet oldMutatedKeys = _mutatedKeys; - FSTDocumentSet *newDocumentSet = oldDocumentSet; + DocumentSet newDocumentSet = oldDocumentSet; BOOL needsRefill = NO; // Track the last doc in a (full) limit. This is necessary, because some update (a delete, or an @@ -264,14 +273,15 @@ - (FSTViewDocumentChanges *)computeChangesWithDocuments:(const MaybeDocumentMap // Note that this should never get used in a refill (when previousChanges is set), because there // will only be adds -- no deletes or updates. FSTDocument *_Nullable lastDocInLimit = - (self.query.limit && oldDocumentSet.count == self.query.limit) ? oldDocumentSet.lastDocument - : nil; + (self.query.limit != NSNotFound && oldDocumentSet.size() == self.query.limit) + ? oldDocumentSet.GetLastDocument() + : nil; for (const auto &kv : docChanges) { const DocumentKey &key = kv.first; FSTMaybeDocument *maybeNewDoc = kv.second; - FSTDocument *_Nullable oldDoc = [oldDocumentSet documentForKey:key]; + FSTDocument *_Nullable oldDoc = oldDocumentSet.GetDocument(key); FSTDocument *_Nullable newDoc = nil; if ([maybeNewDoc isKindOfClass:[FSTDocument class]]) { newDoc = (FSTDocument *)maybeNewDoc; @@ -328,23 +338,23 @@ - (FSTViewDocumentChanges *)computeChangesWithDocuments:(const MaybeDocumentMap if (changeApplied) { if (newDoc) { - newDocumentSet = [newDocumentSet documentSetByAddingDocument:newDoc]; + newDocumentSet = newDocumentSet.insert(newDoc); if (newDoc.hasLocalMutations) { newMutatedKeys = newMutatedKeys.insert(key); } else { newMutatedKeys = newMutatedKeys.erase(key); } } else { - newDocumentSet = [newDocumentSet documentSetByRemovingKey:key]; + newDocumentSet = newDocumentSet.erase(key); newMutatedKeys = newMutatedKeys.erase(key); } } } - if (self.query.limit) { - for (long i = newDocumentSet.count - self.query.limit; i > 0; --i) { - FSTDocument *oldDoc = [newDocumentSet lastDocument]; - newDocumentSet = [newDocumentSet documentSetByRemovingKey:oldDoc.key]; + if (self.query.limit != NSNotFound && newDocumentSet.size() > self.query.limit) { + for (size_t i = newDocumentSet.size() - self.query.limit; i > 0; --i) { + FSTDocument *oldDoc = newDocumentSet.GetLastDocument(); + newDocumentSet = newDocumentSet.erase(oldDoc.key); newMutatedKeys = newMutatedKeys.erase(oldDoc.key); changeSet.AddChange(DocumentViewChange{oldDoc, DocumentViewChange::Type::kRemoved}); } @@ -353,7 +363,7 @@ - (FSTViewDocumentChanges *)computeChangesWithDocuments:(const MaybeDocumentMap HARD_ASSERT(!needsRefill || !previousChanges, "View was refilled using docs that themselves needed refilling."); - return [[FSTViewDocumentChanges alloc] initWithDocumentSet:newDocumentSet + return [[FSTViewDocumentChanges alloc] initWithDocumentSet:std::move(newDocumentSet) changeSet:std::move(changeSet) needsRefill:needsRefill mutatedKeys:newMutatedKeys]; @@ -377,8 +387,8 @@ - (FSTViewChange *)applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges targetChange:(const absl::optional &)targetChange { HARD_ASSERT(!docChanges.needsRefill, "Cannot apply changes that need a refill"); - FSTDocumentSet *oldDocuments = self.documentSet; - self.documentSet = docChanges.documentSet; + DocumentSet oldDocuments = *_documentSet; + *_documentSet = docChanges.documentSet; _mutatedKeys = docChanges.mutatedKeys; // Sort changes based on type and query comparator. @@ -424,7 +434,7 @@ - (FSTViewChange *)applyChangedOnlineState:(OnlineState)onlineState { // that sets `current` back to YES once the client is back online. self.current = NO; return [self applyChangesToDocuments:[[FSTViewDocumentChanges alloc] - initWithDocumentSet:self.documentSet + initWithDocumentSet:*_documentSet changeSet:DocumentViewChangeSet {} needsRefill:NO mutatedKeys:_mutatedKeys]]; @@ -443,14 +453,14 @@ - (BOOL)shouldBeLimboDocumentKey:(const DocumentKey &)key { return NO; } // The local store doesn't think it's a result, so it shouldn't be in limbo. - if (![self.documentSet containsKey:key]) { + if (!_documentSet->ContainsKey(key)) { return NO; } // If there are local changes to the doc, they might explain why the server doesn't know that it's // part of the query. So don't put it in limbo. // TODO(klimt): Ideally, we would only consider changes that might actually affect this specific // query. - if ([self.documentSet documentForKey:key].hasLocalMutations) { + if (_documentSet->GetDocument(key).hasLocalMutations) { return NO; } // Everything else is in limbo. @@ -489,7 +499,7 @@ - (void)applyTargetChange:(const absl::optional &)maybeTargetChang // TODO(klimt): Do this incrementally so that it's not quadratic when updating many documents. DocumentKeySet oldLimboDocuments = std::move(_limboDocuments); _limboDocuments = DocumentKeySet{}; - for (FSTDocument *doc in self.documentSet.documentEnumerator) { + for (FSTDocument *doc : *_documentSet) { if ([self shouldBeLimboDocumentKey:doc.key]) { _limboDocuments = _limboDocuments.insert(doc.key); } diff --git a/Firestore/Source/Local/FSTLocalViewChanges.h b/Firestore/Source/Local/FSTLocalViewChanges.h index 47651a0a272..66ec8631194 100644 --- a/Firestore/Source/Local/FSTLocalViewChanges.h +++ b/Firestore/Source/Local/FSTLocalViewChanges.h @@ -20,7 +20,6 @@ #include "Firestore/core/src/firebase/firestore/model/document_key_set.h" #include "Firestore/core/src/firebase/firestore/model/types.h" -@class FSTDocumentSet; @class FSTMutation; @class FSTQuery; diff --git a/Firestore/Source/Model/FSTDocumentSet.h b/Firestore/Source/Model/FSTDocumentSet.h deleted file mode 100644 index 2087a3da59a..00000000000 --- a/Firestore/Source/Model/FSTDocumentSet.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 - -#include "Firestore/core/src/firebase/firestore/model/document_key.h" -#include "Firestore/core/src/firebase/firestore/model/document_map.h" - -@class FSTDocument; - -NS_ASSUME_NONNULL_BEGIN - -/** - * DocumentSet is an immutable (copy-on-write) collection that holds documents in order specified - * by the provided comparator. We always add a document key comparator on top of what is provided - * to guarantee document equality based on the key. - */ -@interface FSTDocumentSet : NSObject - -/** Creates a new, empty FSTDocumentSet sorted by the given comparator, then by keys. */ -+ (instancetype)documentSetWithComparator:(NSComparator)comparator; - -- (instancetype)init __attribute__((unavailable("Use a static constructor instead"))); - -- (NSUInteger)count; - -/** Returns true if the dictionary contains no elements. */ -- (BOOL)isEmpty; - -/** Returns YES if this set contains a document with the given key. */ -- (BOOL)containsKey:(const firebase::firestore::model::DocumentKey &)key; - -/** Returns the document from this set with the given key if it exists or nil if it doesn't. */ -- (FSTDocument *_Nullable)documentForKey:(const firebase::firestore::model::DocumentKey &)key; - -/** - * Returns the first document in the set according to its built in ordering, or nil if the set - * is empty. - */ -- (FSTDocument *_Nullable)firstDocument; - -/** - * Returns the last document in the set according to its built in ordering, or nil if the set - * is empty. - */ -- (FSTDocument *_Nullable)lastDocument; - -/** - * Returns the index of the document with the provided key in the document set. Returns NSNotFound - * if the key is not present. - */ -- (NSUInteger)indexOfKey:(const firebase::firestore::model::DocumentKey &)key; - -- (NSEnumerator *)documentEnumerator; - -/** Returns a copy of the documents in this set as an array. This is O(n) on the size of the set. */ -- (NSArray *)arrayValue; - -/** - * Returns the documents as a `DocumentMap`. This is O(1) as this leverages - * our internal representation. - */ -- (const firebase::firestore::model::DocumentMap &)mapValue; - -/** Returns a new FSTDocumentSet that contains the given document. */ -- (instancetype)documentSetByAddingDocument:(FSTDocument *_Nullable)document; - -/** Returns a new FSTDocumentSet that excludes any document associated with the given key. */ -- (instancetype)documentSetByRemovingKey:(const firebase::firestore::model::DocumentKey &)key; -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Model/FSTDocumentSet.mm b/Firestore/Source/Model/FSTDocumentSet.mm deleted file mode 100644 index c87e2039e54..00000000000 --- a/Firestore/Source/Model/FSTDocumentSet.mm +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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. - */ - -#include - -#import "Firestore/Source/Model/FSTDocumentSet.h" - -#import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/third_party/Immutable/FSTImmutableSortedSet.h" - -#include "Firestore/core/src/firebase/firestore/model/document_key.h" - -using firebase::firestore::model::DocumentMap; -using firebase::firestore::model::DocumentKey; - -NS_ASSUME_NONNULL_BEGIN - -/** - * The type of the main collection of documents in an FSTDocumentSet. - * @see FSTDocumentSet#sortedSet - */ -typedef FSTImmutableSortedSet SetType; - -@interface FSTDocumentSet () - -- (instancetype)initWithIndex:(DocumentMap &&)index - set:(SetType *)sortedSet NS_DESIGNATED_INITIALIZER; - -/** - * The main collection of documents in the FSTDocumentSet. The documents are ordered by a - * comparator supplied from a query. The SetType collection exists in addition to the index to - * allow ordered traversal of the FSTDocumentSet. - */ -@property(nonatomic, strong, readonly) SetType *sortedSet; -@end - -@implementation FSTDocumentSet { - /** - * An index of the documents in the FSTDocumentSet, indexed by document key. The index - * exists to guarantee the uniqueness of document keys in the set and to allow lookup and removal - * of documents by key. - */ - DocumentMap _index; -} - -+ (instancetype)documentSetWithComparator:(NSComparator)comparator { - SetType *set = [FSTImmutableSortedSet setWithComparator:comparator]; - return [[FSTDocumentSet alloc] initWithIndex:DocumentMap {} set:set]; -} - -- (instancetype)initWithIndex:(DocumentMap &&)index set:(SetType *)sortedSet { - self = [super init]; - if (self) { - _index = std::move(index); - _sortedSet = sortedSet; - } - return self; -} - -- (BOOL)isEqual:(id)other { - if (other == self) { - return YES; - } - if (![other isMemberOfClass:[FSTDocumentSet class]]) { - return NO; - } - - FSTDocumentSet *otherSet = (FSTDocumentSet *)other; - if ([self count] != [otherSet count]) { - return NO; - } - - NSEnumerator *selfIter = [self.sortedSet objectEnumerator]; - NSEnumerator *otherIter = [otherSet.sortedSet objectEnumerator]; - - FSTDocument *selfDoc = [selfIter nextObject]; - FSTDocument *otherDoc = [otherIter nextObject]; - while (selfDoc) { - if (![selfDoc isEqual:otherDoc]) { - return NO; - } - selfDoc = [selfIter nextObject]; - otherDoc = [otherIter nextObject]; - } - return YES; -} - -- (NSUInteger)hash { - NSUInteger hash = 0; - for (FSTDocument *doc in self.sortedSet.objectEnumerator) { - hash = 31 * hash + [doc hash]; - } - return hash; -} - -- (NSString *)description { - return [self.sortedSet description]; -} - -- (NSUInteger)count { - return _index.size(); -} - -- (BOOL)isEmpty { - return _index.empty(); -} - -- (BOOL)containsKey:(const DocumentKey &)key { - return _index.underlying_map().find(key) != _index.underlying_map().end(); -} - -- (FSTDocument *_Nullable)documentForKey:(const DocumentKey &)key { - auto found = _index.underlying_map().find(key); - return found != _index.underlying_map().end() ? static_cast(found->second) : nil; -} - -- (FSTDocument *_Nullable)firstDocument { - return [self.sortedSet firstObject]; -} - -- (FSTDocument *_Nullable)lastDocument { - return [self.sortedSet lastObject]; -} - -- (NSUInteger)indexOfKey:(const DocumentKey &)key { - FSTDocument *doc = [self documentForKey:key]; - return doc ? [self.sortedSet indexOfObject:doc] : NSNotFound; -} - -- (NSEnumerator *)documentEnumerator { - return [self.sortedSet objectEnumerator]; -} - -- (NSArray *)arrayValue { - NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count]; - for (FSTDocument *doc in self.documentEnumerator) { - [result addObject:doc]; - } - return result; -} - -- (const DocumentMap &)mapValue { - return _index; -} - -- (instancetype)documentSetByAddingDocument:(FSTDocument *_Nullable)document { - // TODO(mcg): look into making document nonnull. - if (!document) { - return self; - } - - // Remove any prior mapping of the document's key before adding, preventing sortedSet from - // accumulating values that aren't in the index. - FSTDocumentSet *removed = [self documentSetByRemovingKey:document.key]; - - DocumentMap index = removed->_index.insert(document.key, document); - SetType *set = [removed.sortedSet setByAddingObject:document]; - return [[FSTDocumentSet alloc] initWithIndex:std::move(index) set:set]; -} - -- (instancetype)documentSetByRemovingKey:(const DocumentKey &)key { - FSTDocument *doc = [self documentForKey:key]; - if (!doc) { - return self; - } - - DocumentMap index = _index.erase(key); - SetType *set = [self.sortedSet setByRemovingObject:doc]; - return [[FSTDocumentSet alloc] initWithIndex:std::move(index) set:set]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index dc883d05cf0..75f568e79d7 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -24,13 +24,13 @@ #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Core/FSTQuery.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Util/FSTAsyncQueryListener.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.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/error_apple.h" @@ -191,9 +191,9 @@ } const ViewSnapshot& snapshot = maybe_snapshot.ValueOrDie(); - HARD_ASSERT(snapshot.documents().count <= 1, + HARD_ASSERT(snapshot.documents().size() <= 1, "Too many documents returned on a document query"); - FSTDocument* document = [snapshot.documents() documentForKey:key]; + FSTDocument* document = snapshot.documents().GetDocument(key); bool has_pending_writes = document diff --git a/Firestore/core/src/firebase/firestore/api/query_snapshot.h b/Firestore/core/src/firebase/firestore/api/query_snapshot.h index 006e8421a91..7c5f49dde79 100644 --- a/Firestore/core/src/firebase/firestore/api/query_snapshot.h +++ b/Firestore/core/src/firebase/firestore/api/query_snapshot.h @@ -26,11 +26,10 @@ #include #include -#import "Firestore/Source/Model/FSTDocumentSet.h" - #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" NS_ASSUME_NONNULL_BEGIN @@ -45,8 +44,6 @@ namespace api { */ class QuerySnapshot { public: - QuerySnapshot() = default; - QuerySnapshot(Firestore* firestore, FSTQuery* query, core::ViewSnapshot&& snapshot, @@ -63,12 +60,12 @@ class QuerySnapshot { * Indicates whether this `QuerySnapshot` is empty (contains no documents). */ bool empty() const { - return static_cast(snapshot_.documents().isEmpty); + return snapshot_.documents().empty(); } /** The count of documents in this `QuerySnapshot`. */ size_t size() const { - return static_cast(snapshot_.documents().count); + return snapshot_.documents().size(); } Firestore* firestore() const { diff --git a/Firestore/core/src/firebase/firestore/api/query_snapshot.mm b/Firestore/core/src/firebase/firestore/api/query_snapshot.mm index f3f9302d89f..7992cc15fba 100644 --- a/Firestore/core/src/firebase/firestore/api/query_snapshot.mm +++ b/Firestore/core/src/firebase/firestore/api/query_snapshot.mm @@ -24,10 +24,10 @@ #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" NS_ASSUME_NONNULL_BEGIN @@ -39,6 +39,7 @@ namespace objc = util::objc; using api::Firestore; using core::ViewSnapshot; +using model::DocumentSet; bool operator==(const QuerySnapshot& lhs, const QuerySnapshot& rhs) { return lhs.firestore_ == rhs.firestore_ && @@ -52,10 +53,10 @@ void QuerySnapshot::ForEachDocument( const std::function& callback) const { - FSTDocumentSet* documentSet = snapshot_.documents(); + DocumentSet documentSet = snapshot_.documents(); bool from_cache = metadata_.from_cache(); - for (FSTDocument* document in documentSet.documentEnumerator) { + for (FSTDocument* document : documentSet) { bool has_pending_writes = snapshot_.mutated_keys().contains(document.key); DocumentSnapshot snap(firestore_, document.key, document, from_cache, has_pending_writes); diff --git a/Firestore/core/src/firebase/firestore/core/view_snapshot.h b/Firestore/core/src/firebase/firestore/core/view_snapshot.h index 502f4afb4e7..279ea706952 100644 --- a/Firestore/core/src/firebase/firestore/core/view_snapshot.h +++ b/Firestore/core/src/firebase/firestore/core/view_snapshot.h @@ -30,13 +30,13 @@ #include "Firestore/core/src/firebase/firestore/immutable/sorted_map.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_key_set.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" NS_ASSUME_NONNULL_BEGIN @class FSTDocument; @class FSTQuery; -@class FSTDocumentSet; namespace firebase { namespace firestore { @@ -111,11 +111,9 @@ using ViewSnapshotHandler = */ class ViewSnapshot { public: - ViewSnapshot() = default; - ViewSnapshot(FSTQuery* query, - FSTDocumentSet* documents, - FSTDocumentSet* old_documents, + model::DocumentSet documents, + model::DocumentSet old_documents, std::vector document_changes, model::DocumentKeySet mutated_keys, bool from_cache, @@ -127,7 +125,7 @@ class ViewSnapshot { * added. */ static ViewSnapshot FromInitialDocuments(FSTQuery* query, - FSTDocumentSet* documents, + model::DocumentSet documents, model::DocumentKeySet mutated_keys, bool from_cache, bool excludes_metadata_changes); @@ -138,12 +136,12 @@ class ViewSnapshot { } /** The documents currently known to be results of the query. */ - FSTDocumentSet* documents() const { + const model::DocumentSet& documents() const { return documents_; } /** The documents of the last snapshot. */ - FSTDocumentSet* old_documents() const { + const model::DocumentSet& old_documents() const { return old_documents_; } @@ -184,8 +182,8 @@ class ViewSnapshot { private: FSTQuery* query_ = nil; - FSTDocumentSet* documents_ = nil; - FSTDocumentSet* old_documents_ = nil; + model::DocumentSet documents_; + model::DocumentSet old_documents_; std::vector document_changes_; model::DocumentKeySet mutated_keys_; diff --git a/Firestore/core/src/firebase/firestore/core/view_snapshot.mm b/Firestore/core/src/firebase/firestore/core/view_snapshot.mm index 66e58fddd94..6663063298c 100644 --- a/Firestore/core/src/firebase/firestore/core/view_snapshot.mm +++ b/Firestore/core/src/firebase/firestore/core/view_snapshot.mm @@ -20,8 +20,8 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Model/FSTDocumentSet.h" +#include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" #include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" #include "Firestore/core/src/firebase/firestore/util/string_format.h" @@ -34,6 +34,7 @@ namespace objc = util::objc; using model::DocumentKey; using model::DocumentKeySet; +using model::DocumentSet; using util::StringFormat; // DocumentViewChange @@ -134,16 +135,16 @@ // ViewSnapshot ViewSnapshot::ViewSnapshot(FSTQuery* query, - FSTDocumentSet* documents, - FSTDocumentSet* old_documents, + DocumentSet documents, + DocumentSet old_documents, std::vector document_changes, model::DocumentKeySet mutated_keys, bool from_cache, bool sync_state_changed, bool excludes_metadata_changes) : query_{query}, - documents_{documents}, - old_documents_{old_documents}, + documents_{std::move(documents)}, + old_documents_{std::move(old_documents)}, document_changes_{std::move(document_changes)}, mutated_keys_{std::move(mutated_keys)}, from_cache_{from_cache}, @@ -153,21 +154,20 @@ ViewSnapshot ViewSnapshot::FromInitialDocuments( FSTQuery* query, - FSTDocumentSet* documents, + DocumentSet documents, DocumentKeySet mutated_keys, bool from_cache, bool excludes_metadata_changes) { std::vector view_changes; - for (FSTDocument* doc in documents.documentEnumerator) { + for (FSTDocument* doc : documents) { view_changes.emplace_back(doc, DocumentViewChange::Type::kAdded); } - return ViewSnapshot{ - query, documents, - /*old_documents=*/ - [FSTDocumentSet documentSetWithComparator:query.comparator], - std::move(view_changes), std::move(mutated_keys), from_cache, - /*sync_state_changed=*/true, excludes_metadata_changes}; + return ViewSnapshot{query, documents, + /*old_documents=*/ + DocumentSet{query.comparator}, std::move(view_changes), + std::move(mutated_keys), from_cache, + /*sync_state_changed=*/true, excludes_metadata_changes}; } std::string ViewSnapshot::ToString() const { @@ -175,7 +175,7 @@ "", - query(), documents(), old_documents(), + query(), documents_.ToString(), old_documents_.ToString(), objc::Description(document_changes()), from_cache(), mutated_keys().size(), sync_state_changed(), excludes_metadata_changes()); } @@ -189,15 +189,15 @@ // straightforward way to compute its hash value. Since `ViewSnapshot` is // currently not stored in any dictionaries, this has no side effects. - return util::Hash([query() hash], [documents() hash], [old_documents() hash], + return util::Hash([query() hash], documents(), old_documents(), document_changes(), from_cache(), sync_state_changed(), excludes_metadata_changes()); } bool operator==(const ViewSnapshot& lhs, const ViewSnapshot& rhs) { return objc::Equals(lhs.query(), rhs.query()) && - objc::Equals(lhs.documents(), rhs.documents()) && - objc::Equals(lhs.old_documents(), rhs.old_documents()) && + lhs.documents() == rhs.documents() && + lhs.old_documents() == rhs.old_documents() && lhs.document_changes() == rhs.document_changes() && lhs.from_cache() == rhs.from_cache() && lhs.mutated_keys() == rhs.mutated_keys() && 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 a88c1bdf7ff..1a7076d0e8d 100644 --- a/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h +++ b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h @@ -27,7 +27,7 @@ #include "Firestore/core/src/firebase/firestore/immutable/keys_view.h" #include "Firestore/core/src/firebase/firestore/immutable/map_entry.h" -#include "Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h" +#include "Firestore/core/src/firebase/firestore/immutable/sorted_container.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/range.h" diff --git a/Firestore/core/src/firebase/firestore/immutable/llrb_node.h b/Firestore/core/src/firebase/firestore/immutable/llrb_node.h index 80c2d865455..0046580364e 100644 --- a/Firestore/core/src/firebase/firestore/immutable/llrb_node.h +++ b/Firestore/core/src/firebase/firestore/immutable/llrb_node.h @@ -21,7 +21,7 @@ #include #include "Firestore/core/src/firebase/firestore/immutable/llrb_node_iterator.h" -#include "Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h" +#include "Firestore/core/src/firebase/firestore/immutable/sorted_container.h" namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_map_base.cc b/Firestore/core/src/firebase/firestore/immutable/sorted_container.cc similarity index 89% rename from Firestore/core/src/firebase/firestore/immutable/sorted_map_base.cc rename to Firestore/core/src/firebase/firestore/immutable/sorted_container.cc index 954bdb9cd73..637270d3f71 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_map_base.cc +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_container.cc @@ -14,18 +14,16 @@ * limitations under the License. */ -#include "Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h" +#include "Firestore/core/src/firebase/firestore/immutable/sorted_container.h" namespace firebase { namespace firestore { namespace immutable { -namespace impl { // Define external storage for constants: +constexpr SortedContainer::size_type SortedContainer::npos; constexpr SortedMapBase::size_type SortedMapBase::kFixedSize; -constexpr SortedMapBase::size_type SortedMapBase::npos; -} // namespace impl } // namespace immutable } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h b/Firestore/core/src/firebase/firestore/immutable/sorted_container.h similarity index 77% rename from Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h rename to Firestore/core/src/firebase/firestore/immutable/sorted_container.h index a19bd778830..1cf5eb79c5c 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_container.h @@ -14,25 +14,25 @@ * limitations under the License. */ -#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_SORTED_MAP_BASE_H_ -#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_SORTED_MAP_BASE_H_ +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_SORTED_CONTAINER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_SORTED_CONTAINER_H_ #include namespace firebase { namespace firestore { namespace immutable { -namespace impl { /** - * A base class for implementing sorted maps, containing types and constants - * that don't depend upon the template parameters to the main class. + * A base class for implementing immutable sorted containers, containing types + * and constants that don't depend upon the template parameters to the main + * class. * * Note that this exists as a base class rather than as just a namespace in * order to make it possible for users of the SortedMap classes to avoid needing * to declare storage for each instantiation of the template. */ -class SortedMapBase { +class SortedContainer { public: /** * The type of size() methods on immutable collections. Note: @@ -42,6 +42,23 @@ class SortedMapBase { */ using size_type = uint32_t; + /** + * A sentinel return value that indicates not found. Functionally similar to + * std::string::npos. + */ + static constexpr size_type npos = static_cast(-1); +}; + +/** + * A base class for implementing sorted maps, containing types and constants + * that don't depend upon the template parameters to the main class. + * + * Note that this exists as a base class rather than as just a namespace in + * order to make it possible for users of the SortedMap classes to avoid needing + * to declare storage for each instantiation of the template. + */ +class SortedMapBase : public SortedContainer { + public: /** * The maximum size of an ArraySortedMap. * @@ -52,19 +69,11 @@ class SortedMapBase { * inserting and lookups. Feel free to empirically determine this constant, * but don't expect much gain in real world performance. */ - // TODO(wilhuff): actually use this for switching implementations. static constexpr size_type kFixedSize = 25; - - /** - * A sentinel return value that indicates not found. Functionally similar to - * std::string::npos. - */ - static constexpr size_type npos = static_cast(-1); }; -} // namespace impl } // namespace immutable } // namespace firestore } // namespace firebase -#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_SORTED_MAP_BASE_H_ +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_SORTED_CONTAINER_H_ diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_map.h b/Firestore/core/src/firebase/firestore/immutable/sorted_map.h index 7960f6588d3..f3c998bd80e 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_map.h +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_map.h @@ -21,7 +21,7 @@ #include "Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h" #include "Firestore/core/src/firebase/firestore/immutable/keys_view.h" -#include "Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h" +#include "Firestore/core/src/firebase/firestore/immutable/sorted_container.h" #include "Firestore/core/src/firebase/firestore/immutable/sorted_map_iterator.h" #include "Firestore/core/src/firebase/firestore/immutable/tree_sorted_map.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" @@ -36,7 +36,7 @@ namespace immutable { * has methods to efficiently create new maps that are mutations of it. */ template > -class SortedMap : public impl::SortedMapBase { +class SortedMap : public SortedMapBase { public: using key_type = K; using mapped_type = V; @@ -204,7 +204,7 @@ class SortedMap : public impl::SortedMapBase { tree_type result = tree_.erase(key); if (result.empty()) { // Flip back to the array representation for empty arrays. - return SortedMap{}; + return SortedMap{comparator()}; } return SortedMap{std::move(result)}; } diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_set.h b/Firestore/core/src/firebase/firestore/immutable/sorted_set.h index 0b64a167836..36b6e16ef03 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_set.h +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_set.h @@ -20,8 +20,8 @@ #include #include +#include "Firestore/core/src/firebase/firestore/immutable/sorted_container.h" #include "Firestore/core/src/firebase/firestore/immutable/sorted_map.h" -#include "Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" @@ -46,7 +46,7 @@ template , typename V = impl::Empty, typename M = SortedMap> -class SortedSet { +class SortedSet : public SortedContainer { public: using size_type = typename M::size_type; using value_type = K; @@ -101,7 +101,7 @@ class SortedSet { return const_iterator{map_.min()}; } - const K& max() const { + const_iterator max() const { return const_iterator{map_.max()}; } 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 9fd51c33643..9a1635c2902 100644 --- a/Firestore/core/src/firebase/firestore/immutable/tree_sorted_map.h +++ b/Firestore/core/src/firebase/firestore/immutable/tree_sorted_map.h @@ -26,7 +26,7 @@ #include "Firestore/core/src/firebase/firestore/immutable/keys_view.h" #include "Firestore/core/src/firebase/firestore/immutable/llrb_node.h" #include "Firestore/core/src/firebase/firestore/immutable/map_entry.h" -#include "Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h" +#include "Firestore/core/src/firebase/firestore/immutable/sorted_container.h" #include "Firestore/core/src/firebase/firestore/util/comparator_holder.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" diff --git a/Firestore/core/src/firebase/firestore/model/document_set.h b/Firestore/core/src/firebase/firestore/model/document_set.h new file mode 100644 index 00000000000..003f1050a3c --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/document_set.h @@ -0,0 +1,174 @@ +/* + * 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_SET_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_SET_H_ + +#if !defined(__OBJC__) +#error "This header only supports Objective-C++" +#endif // !defined(__OBJC__) + +#import + +#include +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/immutable/sorted_container.h" +#include "Firestore/core/src/firebase/firestore/immutable/sorted_set.h" +#include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/document_map.h" +#include "Firestore/core/src/firebase/firestore/util/comparison.h" + +@class FSTDocument; + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace model { + +/** + * A C++ comparator that returns less-than, implemented by delegating to + * an NSComparator. + */ +class DocumentSetComparator { + public: + explicit DocumentSetComparator(NSComparator delegate = nil) + : delegate_(delegate) { + } + + bool operator()(FSTDocument* lhs, FSTDocument* rhs) const { + return delegate_(lhs, rhs) == NSOrderedAscending; + } + + private: + NSComparator delegate_; +}; + +/** + * DocumentSet is an immutable (copy-on-write) collection that holds documents + * in order specified by the provided comparator. We always add a document key + * comparator on top of what is provided to guarantee document equality based on + * the key. + */ +class DocumentSet : public immutable::SortedContainer, + public util::Equatable { + public: + /** + * The type of the main collection of documents in an DocumentSet. + * @see sorted_set_. + */ + using SetType = immutable::SortedSet; + + // STL container types + using value_type = FSTDocument*; + using const_iterator = SetType::const_iterator; + + /** + * Creates a new, empty DocumentSet sorted by the given comparator, then by + * keys. + */ + explicit DocumentSet(NSComparator comparator); + + size_t size() const { + return index_.size(); + } + + /** Returns true if the dictionary contains no elements. */ + bool empty() const { + return index_.empty(); + } + + /** Returns true if this set contains a document with the given key. */ + bool ContainsKey(const DocumentKey& key) const; + + SetType::const_iterator begin() const { + return sorted_set_.begin(); + } + SetType::const_iterator end() const { + return sorted_set_.end(); + } + + /** + * Returns the document from this set with the given key if it exists or nil + * if it doesn't. + */ + FSTDocument* _Nullable GetDocument(const DocumentKey& key) const; + + /** + * Returns the first document in the set according to its built in ordering, + * or nil if the set is empty. + */ + FSTDocument* _Nullable GetFirstDocument() const; + + /** + * Returns the last document in the set according to its built in ordering, or + * nil if the set is empty. + */ + FSTDocument* _Nullable GetLastDocument() const; + + /** + * Returns the index of the document with the provided key in the document + * set. Returns `npos` if the key is not present. + */ + size_t IndexOf(const DocumentKey& key) const; + + /** Returns a new DocumentSet that contains the given document. */ + DocumentSet insert(FSTDocument* _Nullable document) const; + + /** + * Returns a new DocumentSet that excludes any document associated with + * the given key. + */ + DocumentSet erase(const DocumentKey& key) const; + + friend bool operator==(const DocumentSet& lhs, const DocumentSet& rhs); + + std::string ToString() const; + friend std::ostream& operator<<(std::ostream& os, const DocumentSet& set); + + size_t Hash() const; + + private: + DocumentSet(DocumentMap&& index, SetType&& sorted_set) + : index_(std::move(index)), sorted_set_(std::move(sorted_set)) { + } + + /** + * An index of the documents in the DocumentSet, indexed by document key. + * The index exists to guarantee the uniqueness of document keys in the set + * and to allow lookup and removal of documents by key. + */ + DocumentMap index_; + + /** + * The main collection of documents in the DocumentSet. The documents are + * ordered by a comparator supplied from a query. The SetType collection + * exists in addition to the index to allow ordered traversal of the + * DocumentSet. + */ + SetType sorted_set_; +}; + +} // namespace model +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_SET_H_ diff --git a/Firestore/core/src/firebase/firestore/model/document_set.mm b/Firestore/core/src/firebase/firestore/model/document_set.mm new file mode 100644 index 00000000000..47cb3d263dd --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/document_set.mm @@ -0,0 +1,121 @@ +/* + * 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/document_set.h" + +#include +#include + +#import "Firestore/Source/Model/FSTDocument.h" + +#include "Firestore/core/src/firebase/firestore/immutable/sorted_set.h" +#include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" +#include "absl/algorithm/container.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace model { + +namespace objc = util::objc; +using immutable::SortedSet; + +DocumentSet::DocumentSet(NSComparator comparator) + : index_{}, sorted_set_{DocumentSetComparator{comparator}} { +} + +bool operator==(const DocumentSet& lhs, const DocumentSet& rhs) { + return absl::c_equal(lhs.sorted_set_, rhs.sorted_set_, + [](FSTDocument* left_doc, FSTDocument* right_doc) { + return [left_doc isEqual:right_doc]; + }); +} + +std::string DocumentSet::ToString() const { + return util::ToString(sorted_set_); +} + +std::ostream& operator<<(std::ostream& os, const DocumentSet& set) { + return os << set.ToString(); +} + +size_t DocumentSet::Hash() const { + size_t hash = 0; + for (FSTDocument* doc : sorted_set_) { + hash = 31 * hash + [doc hash]; + } + return hash; +} + +bool DocumentSet::ContainsKey(const DocumentKey& key) const { + return index_.underlying_map().find(key) != index_.underlying_map().end(); +} + +FSTDocument* _Nullable DocumentSet::GetDocument(const DocumentKey& key) const { + auto found = index_.underlying_map().find(key); + return found != index_.underlying_map().end() + ? static_cast(found->second) + : nil; +} + +FSTDocument* _Nullable DocumentSet::GetFirstDocument() const { + auto result = sorted_set_.min(); + return result != sorted_set_.end() ? *result : nil; +} + +FSTDocument* _Nullable DocumentSet::GetLastDocument() const { + auto result = sorted_set_.max(); + return result != sorted_set_.end() ? *result : nil; +} + +size_t DocumentSet::IndexOf(const DocumentKey& key) const { + FSTDocument* doc = GetDocument(key); + return doc ? sorted_set_.find_index(doc) : npos; +} + +DocumentSet DocumentSet::insert(FSTDocument* _Nullable document) const { + // TODO(mcg): look into making document nonnull. + if (!document) { + return *this; + } + + // Remove any prior mapping of the document's key before adding, preventing + // sortedSet from accumulating values that aren't in the index. + DocumentSet removed = erase(document.key); + + DocumentMap index = removed.index_.insert(document.key, document); + SetType set = removed.sorted_set_.insert(document); + return {std::move(index), std::move(set)}; +} + +DocumentSet DocumentSet::erase(const DocumentKey& key) const { + FSTDocument* doc = GetDocument(key); + if (!doc) { + return *this; + } + + DocumentMap index = index_.erase(key); + SetType set = sorted_set_.erase(doc); + return {std::move(index), std::move(set)}; +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END 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 acd06429a47..2c26c45b403 100644 --- a/Firestore/core/test/firebase/firestore/immutable/sorted_map_test.cc +++ b/Firestore/core/test/firebase/firestore/immutable/sorted_map_test.cc @@ -36,8 +36,6 @@ #include "Firestore/core/test/firebase/firestore/immutable/testing.h" #include "gtest/gtest.h" -using firebase::firestore::immutable::impl::SortedMapBase; - namespace firebase { namespace firestore { namespace immutable { diff --git a/Firestore/core/test/firebase/firestore/immutable/sorted_set_test.cc b/Firestore/core/test/firebase/firestore/immutable/sorted_set_test.cc index a4b337cb166..3239fdafe8b 100644 --- a/Firestore/core/test/firebase/firestore/immutable/sorted_set_test.cc +++ b/Firestore/core/test/firebase/firestore/immutable/sorted_set_test.cc @@ -21,13 +21,12 @@ #include "Firestore/core/test/firebase/firestore/immutable/testing.h" -using firebase::firestore::immutable::impl::SortedMapBase; -using SizeType = SortedMapBase::size_type; - namespace firebase { namespace firestore { namespace immutable { +using SizeType = SortedContainer::size_type; + template SortedSet ToSet(const std::vector& container) { SortedSet result; From 675e74a53561fd13bb1b0ffc4a593c95e9353623 Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 25 Mar 2019 11:12:21 -0700 Subject: [PATCH 083/214] Add missing #include (#2616) * Add a lint check for ostream * Actaully #include to undefined stream insertion operators at link time --- .../core/src/firebase/firestore/timestamp.cc | 2 ++ .../src/firebase/firestore/util/status.cc | 2 ++ scripts/cpplint.py | 26 +++++++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/timestamp.cc b/Firestore/core/src/firebase/firestore/timestamp.cc index 1b67e9ea247..50ddec65ad3 100644 --- a/Firestore/core/src/firebase/firestore/timestamp.cc +++ b/Firestore/core/src/firebase/firestore/timestamp.cc @@ -16,6 +16,8 @@ #include "Firestore/core/include/firebase/firestore/timestamp.h" +#include + #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/strings/str_cat.h" diff --git a/Firestore/core/src/firebase/firestore/util/status.cc b/Firestore/core/src/firebase/firestore/util/status.cc index a8ff913a028..06a643daa7a 100644 --- a/Firestore/core/src/firebase/firestore/util/status.cc +++ b/Firestore/core/src/firebase/firestore/util/status.cc @@ -16,6 +16,8 @@ #include "Firestore/core/src/firebase/firestore/util/status.h" +#include + #include "Firestore/core/src/firebase/firestore/util/string_format.h" #include "absl/memory/memory.h" diff --git a/scripts/cpplint.py b/scripts/cpplint.py index 51fbe246ae0..06929c96fd7 100644 --- a/scripts/cpplint.py +++ b/scripts/cpplint.py @@ -553,6 +553,9 @@ # This is set by --headers flag. _hpp_headers = set(['h']) +# Source filename extensions +_cpp_extensions = set(['cc', 'mm']) + # {str, bool}: a map from error categories to booleans which indicate if the # category should be suppressed for every line. _global_error_suppressions = {} @@ -569,6 +572,15 @@ def ProcessHppHeadersOption(val): def IsHeaderExtension(file_extension): return file_extension in _hpp_headers +def IsSourceExtension(file_extension): + return file_extension in _cpp_extensions + +def IsSourceFilename(filename): + global _cpp_extensions + ext = os.path.splitext(filename)[-1].lower() + ext = ext[1:] # leading dot + return IsSourceExtension(ext) + def ParseNolintSuppressions(filename, raw_line, linenum, error): """Updates the global list of line error-suppressions. @@ -4579,7 +4591,7 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): error(filename, linenum, 'build/include', 4, '"%s" already included at %s:%s' % (include, filename, duplicate_line)) - elif (include.endswith('.cc') and + elif (IsSourceFilename(include) and os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): error(filename, linenum, 'build/include', 4, 'Do not include .cc files from other packages') @@ -5390,6 +5402,7 @@ def ExpectingFunctionArgs(clean_lines, linenum): ('', ('map', 'multimap',)), ('', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', 'unique_ptr', 'weak_ptr')), + ('', ('ostream',)), ('', ('queue', 'priority_queue',)), ('', ('set', 'multiset',)), ('', ('stack',)), @@ -5415,6 +5428,7 @@ def ExpectingFunctionArgs(clean_lines, linenum): ) _RE_PATTERN_STRING = re.compile(r'\bstring\b') +_RE_PATTERN_OSTREAM = re.compile(r'\bostream\b') _re_pattern_headers_maybe_templates = [] for _header, _templates in _HEADERS_MAYBE_TEMPLATES: @@ -5553,6 +5567,14 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, if prefix.endswith('std::') or not prefix.endswith('::'): required[''] = (linenum, 'string') + # Ostream is special too -- also non-templatized + matched = _RE_PATTERN_OSTREAM.search(line) + if matched: + if IsSourceFilename(filename): + required[''] = (linenum, 'ostream') + else: + required[''] = (linenum, 'ostream') + for pattern, template, header in _re_pattern_headers_maybe_templates: if pattern.search(line): required[header] = (linenum, template) @@ -5605,7 +5627,7 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, # didn't include it in the .h file. # TODO(unknown): Do a better job of finding .h files so we are confident that # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: + if IsSourceFilename(filename) and not header_found: return # All the lines have been processed, report the errors found. From 8e37cfe3f94aa11531da5a055e65da525107fbae Mon Sep 17 00:00:00 2001 From: rsgowman Date: Mon, 25 Mar 2019 16:41:19 -0400 Subject: [PATCH 084/214] Alter testutil::*Doc to return shared_ptrs. (#2546) --- .../firebase/firestore/core/query_test.cc | 40 +++---- .../firestore/local/local_serializer_test.cc | 8 +- .../firebase/firestore/model/mutation_test.cc | 108 +++++++++--------- .../firestore/model/precondition_test.cc | 22 ++-- .../firebase/firestore/testutil/testutil.h | 20 ++-- 5 files changed, 100 insertions(+), 98 deletions(-) diff --git a/Firestore/core/test/firebase/firestore/core/query_test.cc b/Firestore/core/test/firebase/firestore/core/query_test.cc index a6c810ce74f..ed58236f9d1 100644 --- a/Firestore/core/test/firebase/firestore/core/query_test.cc +++ b/Firestore/core/test/firebase/firestore/core/query_test.cc @@ -36,9 +36,9 @@ using testutil::Doc; using testutil::Filter; TEST(QueryTest, MatchesBasedOnDocumentKey) { - Document doc1 = Doc("rooms/eros/messages/1"); - Document doc2 = Doc("rooms/eros/messages/2"); - Document doc3 = Doc("rooms/other/messages/1"); + Document doc1 = *Doc("rooms/eros/messages/1"); + Document doc2 = *Doc("rooms/eros/messages/2"); + Document doc3 = *Doc("rooms/other/messages/1"); Query query = Query::AtPath({"rooms", "eros", "messages", "1"}); EXPECT_TRUE(query.Matches(doc1)); @@ -47,10 +47,10 @@ TEST(QueryTest, MatchesBasedOnDocumentKey) { } TEST(QueryTest, MatchesShallowAncestorQuery) { - Document doc1 = Doc("rooms/eros/messages/1"); - Document doc1_meta = Doc("rooms/eros/messages/1/meta/1"); - Document doc2 = Doc("rooms/eros/messages/2"); - Document doc3 = Doc("rooms/other/messages/1"); + Document doc1 = *Doc("rooms/eros/messages/1"); + Document doc1_meta = *Doc("rooms/eros/messages/1/meta/1"); + Document doc2 = *Doc("rooms/eros/messages/2"); + Document doc3 = *Doc("rooms/other/messages/1"); Query query = Query::AtPath({"rooms", "eros", "messages"}); EXPECT_TRUE(query.Matches(doc1)); @@ -60,9 +60,9 @@ TEST(QueryTest, MatchesShallowAncestorQuery) { } TEST(QueryTest, EmptyFieldsAreAllowedForQueries) { - Document doc1 = Doc("rooms/eros/messages/1", 0, - {{"text", FieldValue::FromString("msg1")}}); - Document doc2 = Doc("rooms/eros/messages/2"); + Document doc1 = *Doc("rooms/eros/messages/1", 0, + {{"text", FieldValue::FromString("msg1")}}); + Document doc2 = *Doc("rooms/eros/messages/2"); Query query = Query::AtPath({"rooms", "eros", "messages"}) .Filter(Filter("text", "==", "msg1")); @@ -77,14 +77,14 @@ TEST(QueryTest, PrimitiveValueFilter) { .Filter(Filter("sort", "<=", 2)); Document doc1 = - Doc("collection/1", 0, {{"sort", FieldValue::FromInteger(1)}}); + *Doc("collection/1", 0, {{"sort", FieldValue::FromInteger(1)}}); Document doc2 = - Doc("collection/2", 0, {{"sort", FieldValue::FromInteger(2)}}); + *Doc("collection/2", 0, {{"sort", FieldValue::FromInteger(2)}}); Document doc3 = - Doc("collection/3", 0, {{"sort", FieldValue::FromInteger(3)}}); - Document doc4 = Doc("collection/4", 0, {{"sort", FieldValue::False()}}); + *Doc("collection/3", 0, {{"sort", FieldValue::FromInteger(3)}}); + Document doc4 = *Doc("collection/4", 0, {{"sort", FieldValue::False()}}); Document doc5 = - Doc("collection/5", 0, {{"sort", FieldValue::FromString("string")}}); + *Doc("collection/5", 0, {{"sort", FieldValue::FromString("string")}}); EXPECT_FALSE(query1.Matches(doc1)); EXPECT_TRUE(query1.Matches(doc2)); @@ -103,14 +103,14 @@ TEST(QueryTest, NanFilter) { Query query = Query::AtPath(ResourcePath::FromString("collection")) .Filter(Filter("sort", "==", NAN)); - Document doc1 = Doc("collection/1", 0, {{"sort", FieldValue::Nan()}}); + Document doc1 = *Doc("collection/1", 0, {{"sort", FieldValue::Nan()}}); Document doc2 = - Doc("collection/2", 0, {{"sort", FieldValue::FromInteger(2)}}); + *Doc("collection/2", 0, {{"sort", FieldValue::FromInteger(2)}}); Document doc3 = - Doc("collection/3", 0, {{"sort", FieldValue::FromDouble(3.1)}}); - Document doc4 = Doc("collection/4", 0, {{"sort", FieldValue::False()}}); + *Doc("collection/3", 0, {{"sort", FieldValue::FromDouble(3.1)}}); + Document doc4 = *Doc("collection/4", 0, {{"sort", FieldValue::False()}}); Document doc5 = - Doc("collection/5", 0, {{"sort", FieldValue::FromString("string")}}); + *Doc("collection/5", 0, {{"sort", FieldValue::FromString("string")}}); EXPECT_TRUE(query.Matches(doc1)); EXPECT_FALSE(query.Matches(doc2)); diff --git a/Firestore/core/test/firebase/firestore/local/local_serializer_test.cc b/Firestore/core/test/firebase/firestore/local/local_serializer_test.cc index dac54256407..fa045134696 100644 --- a/Firestore/core/test/firebase/firestore/local/local_serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/local/local_serializer_test.cc @@ -310,8 +310,8 @@ TEST_F(LocalSerializerTest, EncodesMutationBatch) { } TEST_F(LocalSerializerTest, EncodesDocumentAsMaybeDocument) { - Document doc = Doc("some/path", /*version=*/42, - {{"foo", FieldValue::FromString("bar")}}); + Document doc = *Doc("some/path", /*version=*/42, + {{"foo", FieldValue::FromString("bar")}}); ::firestore::client::MaybeDocument maybe_doc_proto; maybe_doc_proto.mutable_document()->set_name( @@ -327,7 +327,7 @@ TEST_F(LocalSerializerTest, EncodesDocumentAsMaybeDocument) { } TEST_F(LocalSerializerTest, EncodesNoDocumentAsMaybeDocument) { - NoDocument no_doc = DeletedDoc("some/path", /*version=*/42); + NoDocument no_doc = *DeletedDoc("some/path", /*version=*/42); ::firestore::client::MaybeDocument maybe_doc_proto; maybe_doc_proto.mutable_no_document()->set_name( @@ -339,7 +339,7 @@ TEST_F(LocalSerializerTest, EncodesNoDocumentAsMaybeDocument) { } TEST_F(LocalSerializerTest, EncodesUnknownDocumentAsMaybeDocument) { - UnknownDocument unknown_doc = UnknownDoc("some/path", /*version=*/42); + UnknownDocument unknown_doc = *UnknownDoc("some/path", /*version=*/42); ::firestore::client::MaybeDocument maybe_doc_proto; maybe_doc_proto.mutable_unknown_document()->set_name( diff --git a/Firestore/core/test/firebase/firestore/model/mutation_test.cc b/Firestore/core/test/firebase/firestore/model/mutation_test.cc index 849ab616b93..8e3ad04ac1a 100644 --- a/Firestore/core/test/firebase/firestore/model/mutation_test.cc +++ b/Firestore/core/test/firebase/firestore/model/mutation_test.cc @@ -20,6 +20,7 @@ #include "Firestore/core/src/firebase/firestore/model/document.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" #include "gtest/gtest.h" @@ -35,10 +36,10 @@ using testutil::PatchMutation; using testutil::SetMutation; TEST(Mutation, AppliesSetsToDocuments) { - auto base_doc = std::make_shared( + MaybeDocumentPtr base_doc = Doc("collection/key", 0, {{"foo", FieldValue::FromString("foo-value")}, - {"baz", FieldValue::FromString("baz-value")}})); + {"baz", FieldValue::FromString("baz-value")}}); std::unique_ptr set = SetMutation( "collection/key", {{"bar", FieldValue::FromString("bar-value")}}); @@ -46,17 +47,17 @@ TEST(Mutation, AppliesSetsToDocuments) { set->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); ASSERT_NE(set_doc, nullptr); ASSERT_EQ(set_doc->type(), MaybeDocument::Type::Document); - EXPECT_EQ(*set_doc.get(), Doc("collection/key", 0, - {{"bar", FieldValue::FromString("bar-value")}}, - DocumentState::kLocalMutations)); + EXPECT_EQ(*set_doc, *Doc("collection/key", 0, + {{"bar", FieldValue::FromString("bar-value")}}, + DocumentState::kLocalMutations)); } TEST(Mutation, AppliesPatchToDocuments) { - auto base_doc = std::make_shared(Doc( + MaybeDocumentPtr base_doc = Doc( "collection/key", 0, {{"foo", FieldValue::FromMap({{"bar", FieldValue::FromString("bar-value")}})}, - {"baz", FieldValue::FromString("baz-value")}})); + {"baz", FieldValue::FromString("baz-value")}}); std::unique_ptr patch = PatchMutation( "collection/key", {{"foo.bar", FieldValue::FromString("new-bar-value")}}); @@ -64,16 +65,16 @@ TEST(Mutation, AppliesPatchToDocuments) { patch->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); ASSERT_NE(local, nullptr); EXPECT_EQ( - *local.get(), - Doc("collection/key", 0, - {{"foo", FieldValue::FromMap( - {{"bar", FieldValue::FromString("new-bar-value")}})}, - {"baz", FieldValue::FromString("baz-value")}}, - DocumentState::kLocalMutations)); + *local, + *Doc("collection/key", 0, + {{"foo", FieldValue::FromMap( + {{"bar", FieldValue::FromString("new-bar-value")}})}, + {"baz", FieldValue::FromString("baz-value")}}, + DocumentState::kLocalMutations)); } TEST(Mutation, AppliesPatchWithMergeToDocuments) { - auto base_doc = std::make_shared(DeletedDoc("collection/key", 0)); + MaybeDocumentPtr base_doc = DeletedDoc("collection/key", 0); std::unique_ptr upsert = PatchMutation( "collection/key", {{"foo.bar", FieldValue::FromString("new-bar-value")}}, @@ -82,11 +83,11 @@ TEST(Mutation, AppliesPatchWithMergeToDocuments) { upsert->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); ASSERT_NE(new_doc, nullptr); EXPECT_EQ( - *new_doc.get(), - Doc("collection/key", 0, - {{"foo", FieldValue::FromMap( - {{"bar", FieldValue::FromString("new-bar-value")}})}}, - DocumentState::kLocalMutations)); + *new_doc, + *Doc("collection/key", 0, + {{"foo", FieldValue::FromMap( + {{"bar", FieldValue::FromString("new-bar-value")}})}}, + DocumentState::kLocalMutations)); } TEST(Mutation, AppliesPatchToNullDocWithMergeToDocuments) { @@ -99,19 +100,19 @@ TEST(Mutation, AppliesPatchToNullDocWithMergeToDocuments) { upsert->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); ASSERT_NE(new_doc, nullptr); EXPECT_EQ( - *new_doc.get(), - Doc("collection/key", 0, - {{"foo", FieldValue::FromMap( - {{"bar", FieldValue::FromString("new-bar-value")}})}}, - DocumentState::kLocalMutations)); + *new_doc, + *Doc("collection/key", 0, + {{"foo", FieldValue::FromMap( + {{"bar", FieldValue::FromString("new-bar-value")}})}}, + DocumentState::kLocalMutations)); } TEST(Mutation, DeletesValuesFromTheFieldMask) { - auto base_doc = std::make_shared(Doc( + MaybeDocumentPtr base_doc = Doc( "collection/key", 0, {{"foo", FieldValue::FromMap({{"bar", FieldValue::FromString("bar-value")}, - {"baz", FieldValue::FromString("baz-value")}})}})); + {"baz", FieldValue::FromString("baz-value")}})}}); std::unique_ptr patch = PatchMutation("collection/key", FieldValue::Map(), {Field("foo.bar")}); @@ -119,18 +120,18 @@ TEST(Mutation, DeletesValuesFromTheFieldMask) { MaybeDocumentPtr patch_doc = patch->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); ASSERT_NE(patch_doc, nullptr); - EXPECT_EQ(*patch_doc.get(), - Doc("collection/key", 0, - {{"foo", FieldValue::FromMap( - {{"baz", FieldValue::FromString("baz-value")}})}}, - DocumentState::kLocalMutations)); + EXPECT_EQ(*patch_doc, + *Doc("collection/key", 0, + {{"foo", FieldValue::FromMap( + {{"baz", FieldValue::FromString("baz-value")}})}}, + DocumentState::kLocalMutations)); } TEST(Mutation, PatchesPrimitiveValue) { - auto base_doc = std::make_shared( + MaybeDocumentPtr base_doc = Doc("collection/key", 0, {{"foo", FieldValue::FromString("foo-value")}, - {"baz", FieldValue::FromString("baz-value")}})); + {"baz", FieldValue::FromString("baz-value")}}); std::unique_ptr patch = PatchMutation( "collection/key", {{"foo.bar", FieldValue::FromString("new-bar-value")}}); @@ -139,17 +140,16 @@ TEST(Mutation, PatchesPrimitiveValue) { patch->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); ASSERT_NE(patched_doc, nullptr); EXPECT_EQ( - *patched_doc.get(), - Doc("collection/key", 0, - {{"foo", FieldValue::FromMap( - {{"bar", FieldValue::FromString("new-bar-value")}})}, - {"baz", FieldValue::FromString("baz-value")}}, - DocumentState::kLocalMutations)); + *patched_doc, + *Doc("collection/key", 0, + {{"foo", FieldValue::FromMap( + {{"bar", FieldValue::FromString("new-bar-value")}})}, + {"baz", FieldValue::FromString("baz-value")}}, + DocumentState::kLocalMutations)); } TEST(Mutation, PatchingDeletedDocumentsDoesNothing) { - auto base_doc = - std::make_shared(testutil::DeletedDoc("collection/key", 0)); + MaybeDocumentPtr base_doc = testutil::DeletedDoc("collection/key", 0); std::unique_ptr patch = PatchMutation("collection/key", {{"foo", FieldValue::FromString("bar")}}); MaybeDocumentPtr patched_doc = @@ -251,20 +251,20 @@ TEST(Mutation, AppliesServerAckedArrayTransformsToDocuments) { } TEST(Mutation, DeleteDeletes) { - auto base_doc = std::make_shared( - Doc("collection/key", 0, {{"foo", FieldValue::FromString("bar")}})); + MaybeDocumentPtr base_doc = + Doc("collection/key", 0, {{"foo", FieldValue::FromString("bar")}}); std::unique_ptr del = testutil::DeleteMutation("collection/key"); MaybeDocumentPtr deleted_doc = del->ApplyToLocalView(base_doc, base_doc.get(), Timestamp::Now()); ASSERT_NE(deleted_doc, nullptr); - EXPECT_EQ(*deleted_doc.get(), testutil::DeletedDoc("collection/key", 0)); + EXPECT_EQ(*deleted_doc, *testutil::DeletedDoc("collection/key", 0)); } TEST(Mutation, SetWithMutationResult) { - auto base_doc = std::make_shared( - Doc("collection/key", 0, {{"foo", FieldValue::FromString("bar")}})); + MaybeDocumentPtr base_doc = + Doc("collection/key", 0, {{"foo", FieldValue::FromString("bar")}}); std::unique_ptr set = SetMutation( "collection/key", {{"foo", FieldValue::FromString("new-bar")}}); @@ -272,14 +272,14 @@ TEST(Mutation, SetWithMutationResult) { set->ApplyToRemoteDocument(base_doc, MutationResult(4)); ASSERT_NE(set_doc, nullptr); - EXPECT_EQ(*set_doc.get(), Doc("collection/key", 4, - {{"foo", FieldValue::FromString("new-bar")}}, - DocumentState::kCommittedMutations)); + EXPECT_EQ(*set_doc, *Doc("collection/key", 4, + {{"foo", FieldValue::FromString("new-bar")}}, + DocumentState::kCommittedMutations)); } TEST(Mutation, PatchWithMutationResult) { - auto base_doc = std::make_shared( - Doc("collection/key", 0, {{"foo", FieldValue::FromString("bar")}})); + MaybeDocumentPtr base_doc = + Doc("collection/key", 0, {{"foo", FieldValue::FromString("bar")}}); std::unique_ptr patch = PatchMutation( "collection/key", {{"foo", FieldValue::FromString("new-bar")}}); @@ -287,9 +287,9 @@ TEST(Mutation, PatchWithMutationResult) { patch->ApplyToRemoteDocument(base_doc, MutationResult(4)); ASSERT_NE(patch_doc, nullptr); - EXPECT_EQ(*patch_doc.get(), Doc("collection/key", 4, - {{"foo", FieldValue::FromString("new-bar")}}, - DocumentState::kCommittedMutations)); + EXPECT_EQ(*patch_doc, *Doc("collection/key", 4, + {{"foo", FieldValue::FromString("new-bar")}}, + DocumentState::kCommittedMutations)); } TEST(Mutation, Transitions) { diff --git a/Firestore/core/test/firebase/firestore/model/precondition_test.cc b/Firestore/core/test/firebase/firestore/model/precondition_test.cc index a755e29c14f..a610461da34 100644 --- a/Firestore/core/test/firebase/firestore/model/precondition_test.cc +++ b/Firestore/core/test/firebase/firestore/model/precondition_test.cc @@ -28,21 +28,21 @@ namespace firestore { namespace model { TEST(Precondition, None) { - const Precondition none = Precondition::None(); + 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); + NoDocument deleted_doc = *testutil::DeletedDoc("foo/doc", 1234567); + Document doc = *testutil::Doc("bar/doc", 7654321); EXPECT_TRUE(none.IsValidFor(&deleted_doc)); EXPECT_TRUE(none.IsValidFor(&doc)); EXPECT_TRUE(none.IsValidFor(nullptr)); } TEST(Precondition, Exists) { - const Precondition exists = Precondition::Exists(true); - const Precondition no_exists = Precondition::Exists(false); + Precondition exists = Precondition::Exists(true); + 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()); @@ -50,8 +50,8 @@ TEST(Precondition, Exists) { 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); + NoDocument deleted_doc = *testutil::DeletedDoc("foo/doc", 1234567); + Document doc = *testutil::Doc("bar/doc", 7654321); EXPECT_FALSE(exists.IsValidFor(&deleted_doc)); EXPECT_TRUE(exists.IsValidFor(&doc)); EXPECT_FALSE(exists.IsValidFor(nullptr)); @@ -61,15 +61,15 @@ TEST(Precondition, Exists) { } TEST(Precondition, UpdateTime) { - const Precondition update_time = + 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); + NoDocument deleted_doc = *testutil::DeletedDoc("foo/doc", 1234567); + Document not_match = *testutil::Doc("bar/doc", 7654321); + Document match = *testutil::Doc("baz/doc", 1234567); EXPECT_FALSE(update_time.IsValidFor(&deleted_doc)); EXPECT_FALSE(update_time.IsValidFor(¬_match)); EXPECT_TRUE(update_time.IsValidFor(&match)); diff --git a/Firestore/core/test/firebase/firestore/testutil/testutil.h b/Firestore/core/test/firebase/firestore/testutil/testutil.h index 07a1c8b8e81..6d7375b41b1 100644 --- a/Firestore/core/test/firebase/firestore/testutil/testutil.h +++ b/Firestore/core/test/firebase/firestore/testutil/testutil.h @@ -77,23 +77,25 @@ inline model::SnapshotVersion Version(int64_t version) { return model::SnapshotVersion{Timestamp::FromTimePoint(timepoint)}; } -inline model::Document Doc( +inline std::shared_ptr Doc( absl::string_view key, int64_t version = 0, const model::FieldValue::Map& data = model::FieldValue::Map(), model::DocumentState document_state = model::DocumentState::kSynced) { - return model::Document{model::ObjectValue::FromMap(data), Key(key), - Version(version), document_state}; + return std::make_shared(model::ObjectValue::FromMap(data), + Key(key), Version(version), + document_state); } -inline model::NoDocument DeletedDoc(absl::string_view key, int64_t version) { - return model::NoDocument{Key(key), Version(version), - /*has_committed_mutations=*/false}; +inline std::shared_ptr DeletedDoc(absl::string_view key, + int64_t version) { + return std::make_shared(Key(key), Version(version), + /*has_committed_mutations=*/false); } -inline model::UnknownDocument UnknownDoc(absl::string_view key, - int64_t version) { - return model::UnknownDocument{Key(key), Version(version)}; +inline std::shared_ptr UnknownDoc(absl::string_view key, + int64_t version) { + return std::make_shared(Key(key), Version(version)); } inline core::RelationFilter::Operator OperatorFromString(absl::string_view s) { From 3057672a9b501d22d4ad6c6e5fad7b86c668f9d5 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Mon, 25 Mar 2019 14:22:03 -0700 Subject: [PATCH 085/214] Support ERROR_INVALID_PROVIDER_ID in Firebase Auth. (#2629) --- Firebase/Auth/Source/FIRAuthErrorUtils.h | 7 +++++++ Firebase/Auth/Source/FIRAuthErrorUtils.m | 14 ++++++++++++++ Firebase/Auth/Source/FIRAuthInternalErrors.h | 5 +++++ Firebase/Auth/Source/Public/FIRAuthErrors.h | 5 +++++ Firebase/Auth/Source/RPCs/FIRAuthBackend.m | 14 ++++++++++++-- 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/Firebase/Auth/Source/FIRAuthErrorUtils.h b/Firebase/Auth/Source/FIRAuthErrorUtils.h index c09fda27897..bbf726f20c4 100644 --- a/Firebase/Auth/Source/FIRAuthErrorUtils.h +++ b/Firebase/Auth/Source/FIRAuthErrorUtils.h @@ -522,6 +522,13 @@ NS_ASSUME_NONNULL_BEGIN */ + (NSError *)nullUserErrorWithMessage:(nullable NSString *)message; +/** @fn invalidProviderIDErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidProviderID code. + @param message Error message from the backend, if any. + @remarks This error indicates that the provider id given for the web operation is invalid. + */ ++ (NSError *)invalidProviderIDErrorWithMessage:(nullable NSString *)message; + /** @fn invalidDynamicLinkDomainErrorWithMessage: @brief Constructs an @c NSError with the code and message provided. @param message Error message from the backend, if any. diff --git a/Firebase/Auth/Source/FIRAuthErrorUtils.m b/Firebase/Auth/Source/FIRAuthErrorUtils.m index ef6a5141840..0300e4a5f9e 100644 --- a/Firebase/Auth/Source/FIRAuthErrorUtils.m +++ b/Firebase/Auth/Source/FIRAuthErrorUtils.m @@ -424,6 +424,12 @@ static NSString *const kFIRAuthErrorMessageNullUser = @"A null user object was provided as the " "argument for an operation which requires a non-null user object."; +/** @var kFIRAuthErrorMessageInvalidProviderID + @brief Message for @c FIRAuthErrorCodeInvalidProviderID error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidProviderID = @"The provider ID provided for the " + "attempted web operation is invalid."; + /** @var kFIRAuthErrorMessageInvalidDynamicLinkDomain @brief Message for @c kFIRAuthErrorMessageInvalidDynamicLinkDomain error code. */ @@ -559,6 +565,8 @@ return kFIRAuthErrorMessageWebRequestFailed; case FIRAuthErrorCodeNullUser: return kFIRAuthErrorMessageNullUser; + case FIRAuthErrorCodeInvalidProviderID: + return kFIRAuthErrorMessageInvalidProviderID; case FIRAuthErrorCodeInvalidDynamicLinkDomain: return kFIRAuthErrorMessageInvalidDynamicLinkDomain; case FIRAuthErrorCodeWebInternalError: @@ -690,6 +698,8 @@ return @"ERROR_WEB_NETWORK_REQUEST_FAILED"; case FIRAuthErrorCodeNullUser: return @"ERROR_NULL_USER"; + case FIRAuthErrorCodeInvalidProviderID: + return @"ERROR_INVALID_PROVIDER_ID"; case FIRAuthErrorCodeInvalidDynamicLinkDomain: return @"ERROR_INVALID_DYNAMIC_LINK_DOMAIN"; case FIRAuthErrorCodeWebInternalError: @@ -1136,6 +1146,10 @@ + (NSError *)nullUserErrorWithMessage:(nullable NSString *)message { return [self errorWithCode:FIRAuthInternalErrorCodeNullUser message:message]; } ++ (NSError *)invalidProviderIDErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidProviderID message:message]; +} + + (NSError *)invalidDynamicLinkDomainErrorWithMessage:(nullable NSString *)message { return [self errorWithCode:FIRAuthInternalErrorCodeInvalidDynamicLinkDomain message:message]; } diff --git a/Firebase/Auth/Source/FIRAuthInternalErrors.h b/Firebase/Auth/Source/FIRAuthInternalErrors.h index 92f3b55ec9d..205dc0f3ab0 100644 --- a/Firebase/Auth/Source/FIRAuthInternalErrors.h +++ b/Firebase/Auth/Source/FIRAuthInternalErrors.h @@ -393,6 +393,11 @@ typedef NS_ENUM(NSInteger, FIRAuthInternalErrorCode) { FIRAuthInternalErrorCodeNullUser = FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeNullUser, + /** Indicates that the provider id given for the web operation is invalid. + */ + FIRAuthInternalErrorCodeInvalidProviderID = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidProviderID, + /** Indicates that the Firebase Dynamic Link domain used is either not configured or is unauthorized for the current project. */ diff --git a/Firebase/Auth/Source/Public/FIRAuthErrors.h b/Firebase/Auth/Source/Public/FIRAuthErrors.h index 656043e5fd0..2c90274e5b8 100644 --- a/Firebase/Auth/Source/Public/FIRAuthErrors.h +++ b/Firebase/Auth/Source/Public/FIRAuthErrors.h @@ -334,6 +334,11 @@ typedef NS_ENUM(NSInteger, FIRAuthErrorCode) { */ FIRAuthErrorCodeNullUser = 17067, + /** + * Represents the error code for when the given provider id for a web operation is invalid. + */ + FIRAuthErrorCodeInvalidProviderID = 17071, + /** Indicates that the Firebase Dynamic Link domain used is either not configured or is unauthorized for the current project. */ diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m index 188cc9fc04f..e862b5cd9f7 100644 --- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m +++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m @@ -294,9 +294,15 @@ */ static NSString *const kUnauthorizedDomainErrorMessage = @"UNAUTHORIZED_DOMAIN"; +/** @var kInvalidProviderIDErrorMessage + @brief This is the error message the server will respond with if the provider id given for the + web operation is invalid. + */ +static NSString *const kInvalidProviderIDErrorMessage = @"INVALID_PROVIDER_ID"; + /** @var kInvalidDynamicLinkDomainErrorMessage - @brief This is the error message the server will respond with if the dynamic link domain provided - in the request is invalid. + @brief This is the error message the server will respond with if the dynamic link domain + provided in the request is invalid. */ static NSString *const kInvalidDynamicLinkDomainErrorMessage = @"INVALID_DYNAMIC_LINK_DOMAIN"; @@ -1119,6 +1125,10 @@ + (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorM return [FIRAuthErrorUtils invalidContinueURIErrorWithMessage:serverDetailErrorMessage]; } + if ([shortErrorMessage isEqualToString:kInvalidProviderIDErrorMessage]) { + return [FIRAuthErrorUtils invalidProviderIDErrorWithMessage:serverDetailErrorMessage]; + } + if ([shortErrorMessage isEqualToString:kInvalidDynamicLinkDomainErrorMessage]) { return [FIRAuthErrorUtils invalidDynamicLinkDomainErrorWithMessage:serverDetailErrorMessage]; } From 080c64daba45d3ea09326fc3d42be2aee65f9c72 Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 25 Mar 2019 16:38:28 -0700 Subject: [PATCH 086/214] Rework the IWYU test to handle types with multiple valid sources (#2633) --- scripts/cpplint.py | 54 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/scripts/cpplint.py b/scripts/cpplint.py index 06929c96fd7..4431fa5cb9f 100644 --- a/scripts/cpplint.py +++ b/scripts/cpplint.py @@ -5550,8 +5550,17 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io: The IO factory to use to read the header file. Provided for unittest injection. """ - required = {} # A map of header name to linenumber and the template entity. - # Example of required: { '': (1219, 'less<>') } + # A map of entity to a tuple of line number and tuple of headers. + # Example: { 'less<>': (1219, ('',)) } + # Example: { 'ostream': (1234, ('', '', '')) } + required = {} + + def Require(entity, linenum, *headers): + """Adds an entity at the given line, along with a list of possible headers + in which to find it. The first header is treated as the preferred header. + """ + required[entity] = (linenum, headers) + for linenum in xrange(clean_lines.NumLines()): line = clean_lines.elided[linenum] @@ -5565,19 +5574,19 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, # (We check only the first match per line; good enough.) prefix = line[:matched.start()] if prefix.endswith('std::') or not prefix.endswith('::'): - required[''] = (linenum, 'string') + Require('string', linenum, '') # Ostream is special too -- also non-templatized matched = _RE_PATTERN_OSTREAM.search(line) if matched: if IsSourceFilename(filename): - required[''] = (linenum, 'ostream') + Require('ostream', linenum, '', '') else: - required[''] = (linenum, 'ostream') + Require('ostream', linenum, '', '', '') for pattern, template, header in _re_pattern_headers_maybe_templates: if pattern.search(line): - required[header] = (linenum, template) + Require(template, linenum, header) # The following function is just a speed up, no semantics are changed. if not '<' in line: # Reduces the cpu time usage by skipping lines. @@ -5590,7 +5599,7 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, # (We check only the first match per line; good enough.) prefix = line[:matched.start()] if prefix.endswith('std::') or not prefix.endswith('::'): - required[header] = (linenum, template) + Require(template, linenum, header) # The policy is that if you #include something in foo.h you don't need to # include it again in foo.cc. Here, we will look at possible includes. @@ -5630,13 +5639,34 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, if IsSourceFilename(filename) and not header_found: return + # Keep track of which headers have been reported already + reported = set() + # All the lines have been processed, report the errors found. - for required_header_unstripped in required: - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_dict: - error(filename, required[required_header_unstripped][0], + for template in required: + line_and_headers = required[template] + headers = line_and_headers[1] + found = False + for required_header_unstripped in headers: + if required_header_unstripped in reported: + found = True + break + + if required_header_unstripped.strip('<>"') in include_dict: + found = True + break + + if not found: + preferred_header = headers[0] + reported.add(preferred_header) + if len(headers) < 2: + alternatives = '' + else: + alternatives = ' (or ' + ', '.join(headers[1:]) + ')' + error(filename, line_and_headers[0], 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) + 'Add #include ' + preferred_header + ' for ' + template + + alternatives) _RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') From ce07f8d0c351844eaa75fce660017f3cee043582 Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 25 Mar 2019 20:01:20 -0700 Subject: [PATCH 087/214] Port FSTListenOptions to C++ (#2617) --- .../Tests/Core/FSTEventManagerTests.mm | 4 +- .../Tests/Core/FSTQueryListenerTests.mm | 59 ++++++------ .../SpecTests/FSTSyncEngineTestDriver.mm | 4 +- Firestore/Source/API/FIRDocumentReference.mm | 18 +--- Firestore/Source/API/FIRQuery.mm | 23 ++--- Firestore/Source/Core/FSTEventManager.h | 24 +---- Firestore/Source/Core/FSTEventManager.mm | 53 ++--------- Firestore/Source/Core/FSTFirestoreClient.h | 6 +- Firestore/Source/Core/FSTFirestoreClient.mm | 4 +- .../firestore/api/document_reference.h | 4 +- .../firestore/api/document_reference.mm | 13 +-- .../firebase/firestore/core/CMakeLists.txt | 1 + .../firebase/firestore/core/listen_options.h | 91 +++++++++++++++++++ .../firebase/firestore/testutil/xcgmock.h | 1 - 14 files changed, 168 insertions(+), 137 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/core/listen_options.h diff --git a/Firestore/Example/Tests/Core/FSTEventManagerTests.mm b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm index 98cfe536bc4..e0ecfc577d0 100644 --- a/Firestore/Example/Tests/Core/FSTEventManagerTests.mm +++ b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm @@ -62,7 +62,7 @@ @implementation FSTEventManagerTests - (FSTQueryListener *)noopListenerForQuery:(FSTQuery *)query { return [[FSTQueryListener alloc] initWithQuery:query - options:[FSTListenOptions defaultOptions] + options:ListenOptions::DefaultOptions() viewSnapshotHandler:[](const StatusOr &) {}]; } @@ -102,7 +102,7 @@ - (void)testHandlesUnlistenOnUnknownListenerGracefully { - (FSTQueryListener *)queryListenerForQuery:(FSTQuery *)query withHandler:(ViewSnapshotHandler &&)handler { return [[FSTQueryListener alloc] initWithQuery:query - options:[FSTListenOptions defaultOptions] + options:ListenOptions::DefaultOptions() viewSnapshotHandler:std::move(handler)]; } diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index 7516a8aaf05..234b0b82a19 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -78,14 +78,12 @@ @interface FSTQueryListenerTests : XCTestCase @implementation FSTQueryListenerTests { DelayedConstructor _executor; - FSTListenOptions *_includeMetadataChanges; + ListenOptions _includeMetadataChanges; } - (void)setUp { _executor.Init(dispatch_queue_create("FSTQueryListenerTests Queue", DISPATCH_QUEUE_SERIAL)); - _includeMetadataChanges = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES - includeDocumentMetadataChanges:YES - waitForSyncWhenOnline:NO]; + _includeMetadataChanges = ListenOptions::FromIncludeMetadataChanges(true); } - (void)testRaisesCollectionEvents { @@ -248,9 +246,10 @@ - (void)testRaisesDocumentMetadataEventsOnlyWhenSpecified { FSTTestDoc("rooms/Eros", 1, @{@"name" : @"Eros"}, FSTDocumentStateSynced); FSTDocument *doc3 = FSTTestDoc("rooms/Other", 3, @{@"name" : @"Other"}, FSTDocumentStateSynced); - FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:NO - includeDocumentMetadataChanges:YES - waitForSyncWhenOnline:NO]; + ListenOptions options( + /*include_query_metadata_changes=*/false, + /*include_document_metadata_changes=*/true, + /*wait_for_sync_when_online=*/false); FSTQueryListener *filteredListener = [self listenToQuery:query accumulatingSnapshots:&filteredAccum]; @@ -300,9 +299,10 @@ - (void)testRaisesQueryMetadataEventsOnlyWhenHasPendingWritesOnTheQueryChanges { FSTTestDoc("rooms/Hades", 2, @{@"name" : @"Hades"}, FSTDocumentStateSynced); FSTDocument *doc3 = FSTTestDoc("rooms/Other", 3, @{@"name" : @"Other"}, FSTDocumentStateSynced); - FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES - includeDocumentMetadataChanges:NO - waitForSyncWhenOnline:NO]; + ListenOptions options( + /*include_query_metadata_changes=*/true, + /*include_document_metadata_changes=*/false, + /*wait_for_sync_when_online=*/false); FSTQueryListener *fullListener = [self listenToQuery:query options:options accumulatingSnapshots:&fullAccum]; @@ -373,12 +373,14 @@ - (void)testWillWaitForSyncIfOnline { FSTQuery *query = FSTTestQuery("rooms"); FSTDocument *doc1 = FSTTestDoc("rooms/Eros", 1, @{@"name" : @"Eros"}, FSTDocumentStateSynced); FSTDocument *doc2 = FSTTestDoc("rooms/Hades", 2, @{@"name" : @"Hades"}, FSTDocumentStateSynced); - FSTQueryListener *listener = - [self listenToQuery:query - options:[[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:NO - includeDocumentMetadataChanges:NO - waitForSyncWhenOnline:YES] - accumulatingSnapshots:&events]; + + ListenOptions options( + /*include_query_metadata_changes=*/false, + /*include_document_metadata_changes=*/false, + /*wait_for_sync_when_online=*/true); + FSTQueryListener *listener = [self listenToQuery:query + options:options + accumulatingSnapshots:&events]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt).value(); @@ -412,12 +414,15 @@ - (void)testWillRaiseInitialEventWhenGoingOffline { FSTQuery *query = FSTTestQuery("rooms"); FSTDocument *doc1 = FSTTestDoc("rooms/Eros", 1, @{@"name" : @"Eros"}, FSTDocumentStateSynced); FSTDocument *doc2 = FSTTestDoc("rooms/Hades", 2, @{@"name" : @"Hades"}, FSTDocumentStateSynced); - FSTQueryListener *listener = - [self listenToQuery:query - options:[[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:NO - includeDocumentMetadataChanges:NO - waitForSyncWhenOnline:YES] - accumulatingSnapshots:&events]; + + ListenOptions options( + /*include_query_metadata_changes=*/false, + /*include_document_metadata_changes=*/false, + /*wait_for_sync_when_online=*/true); + + FSTQueryListener *listener = [self listenToQuery:query + options:options + accumulatingSnapshots:&events]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt).value(); @@ -457,7 +462,7 @@ - (void)testWillRaiseInitialEventWhenGoingOfflineAndThereAreNoDocs { FSTQuery *query = FSTTestQuery("rooms"); FSTQueryListener *listener = [self listenToQuery:query - options:[FSTListenOptions defaultOptions] + options:ListenOptions::DefaultOptions() accumulatingSnapshots:&events]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; @@ -483,7 +488,7 @@ - (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { FSTQuery *query = FSTTestQuery("rooms"); FSTQueryListener *listener = [self listenToQuery:query - options:[FSTListenOptions defaultOptions] + options:ListenOptions::DefaultOptions() accumulatingSnapshots:&events]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; @@ -505,12 +510,12 @@ - (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { - (FSTQueryListener *)listenToQuery:(FSTQuery *)query handler:(ViewSnapshotHandler &&)handler { return [[FSTQueryListener alloc] initWithQuery:query - options:[FSTListenOptions defaultOptions] + options:ListenOptions::DefaultOptions() viewSnapshotHandler:std::move(handler)]; } - (FSTQueryListener *)listenToQuery:(FSTQuery *)query - options:(FSTListenOptions *)options + options:(ListenOptions)options accumulatingSnapshots:(std::vector *)values { return [[FSTQueryListener alloc] initWithQuery:query options:options @@ -522,7 +527,7 @@ - (FSTQueryListener *)listenToQuery:(FSTQuery *)query - (FSTQueryListener *)listenToQuery:(FSTQuery *)query accumulatingSnapshots:(std::vector *)values { return [self listenToQuery:query - options:[FSTListenOptions defaultOptions] + options:ListenOptions::DefaultOptions() accumulatingSnapshots:values]; } diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index 768a58202a5..b100c7f049f 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -349,9 +349,7 @@ - (FSTOutstandingWrite *)receiveWriteError:(int)errorCode - (TargetId)addUserListenerWithQuery:(FSTQuery *)query { // TODO(dimond): Allow customizing listen options in spec tests // TODO(dimond): Change spec tests to verify isFromCache on snapshots - FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES - includeDocumentMetadataChanges:YES - waitForSyncWhenOnline:NO]; + ListenOptions options = ListenOptions::FromIncludeMetadataChanges(true); FSTQueryListener *listener = [[FSTQueryListener alloc] initWithQuery:query options:options diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index ac494db2e1d..d11501ba436 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -204,23 +204,15 @@ - (void)getDocumentWithSource:(FIRFirestoreSource)source - (id) addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges listener:(FIRDocumentSnapshotBlock)listener { - FSTListenOptions *options = - [self internalOptionsForIncludeMetadataChanges:includeMetadataChanges]; + ListenOptions options = ListenOptions::FromIncludeMetadataChanges(includeMetadataChanges); return [self addSnapshotListenerInternalWithOptions:options listener:listener]; } -- (id) - addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions - listener:(FIRDocumentSnapshotBlock)listener { +- (id)addSnapshotListenerInternalWithOptions:(ListenOptions)internalOptions + listener:(FIRDocumentSnapshotBlock) + listener { return _documentReference.AddSnapshotListener([self wrapDocumentSnapshotBlock:listener], - internalOptions); -} - -/** Converts the public API options object to the internal options object. */ -- (FSTListenOptions *)internalOptionsForIncludeMetadataChanges:(BOOL)includeMetadataChanges { - return [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:includeMetadataChanges - includeDocumentMetadataChanges:includeMetadataChanges - waitForSyncWhenOnline:NO]; + std::move(internalOptions)); } - (StatusOrCallback)wrapDocumentSnapshotBlock:(FIRDocumentSnapshotBlock)block { diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 33f8857d46f..9b46040bda4 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -118,10 +118,10 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)source return; } - FSTListenOptions *listenOptions = - [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES - includeDocumentMetadataChanges:YES - waitForSyncWhenOnline:YES]; + ListenOptions listenOptions( + /*include_query_metadata_changes=*/true, + /*include_document_metadata_changes=*/true, + /*wait_for_sync_when_online=*/true); dispatch_semaphore_t registered = dispatch_semaphore_create(0); __block id listenerRegistration; @@ -164,13 +164,13 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)source - (id) addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges listener:(FIRQuerySnapshotBlock)listener { - auto options = [self internalOptionsForIncludeMetadataChanges:includeMetadataChanges]; + auto options = ListenOptions::FromIncludeMetadataChanges(includeMetadataChanges); return [self addSnapshotListenerInternalWithOptions:options listener:listener]; } -- (id) - addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions - listener:(FIRQuerySnapshotBlock)listener { +- (id)addSnapshotListenerInternalWithOptions:(ListenOptions)internalOptions + listener: + (FIRQuerySnapshotBlock)listener { Firestore *firestore = self.firestore.wrapped; FSTQuery *query = self.query; @@ -665,13 +665,6 @@ - (FSTBound *)boundFromFieldValues:(NSArray *)fieldValues isBefore:(BOOL)isB return [FSTBound boundWithPosition:components isBefore:isBefore]; } -/** Converts the public API options object to the internal options object. */ -- (FSTListenOptions *)internalOptionsForIncludeMetadataChanges:(BOOL)includeMetadataChanges { - return [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:includeMetadataChanges - includeDocumentMetadataChanges:includeMetadataChanges - waitForSyncWhenOnline:NO]; -} - @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTEventManager.h b/Firestore/Source/Core/FSTEventManager.h index 30be8c08bed..4b9d7b800f8 100644 --- a/Firestore/Source/Core/FSTEventManager.h +++ b/Firestore/Source/Core/FSTEventManager.h @@ -16,6 +16,7 @@ #import +#include "Firestore/core/src/firebase/firestore/core/listen_options.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/util/status.h" @@ -25,26 +26,7 @@ NS_ASSUME_NONNULL_BEGIN -#pragma mark - FSTListenOptions - -@interface FSTListenOptions : NSObject - -+ (instancetype)defaultOptions; - -- (instancetype)initWithIncludeQueryMetadataChanges:(BOOL)includeQueryMetadataChanges - includeDocumentMetadataChanges:(BOOL)includeDocumentMetadataChanges - waitForSyncWhenOnline:(BOOL)waitForSyncWhenOnline - NS_DESIGNATED_INITIALIZER; - -- (instancetype)init NS_UNAVAILABLE; - -@property(nonatomic, assign, readonly) BOOL includeQueryMetadataChanges; - -@property(nonatomic, assign, readonly) BOOL includeDocumentMetadataChanges; - -@property(nonatomic, assign, readonly) BOOL waitForSyncWhenOnline; - -@end +using firebase::firestore::core::ListenOptions; #pragma mark - FSTQueryListener @@ -55,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FSTQueryListener : NSObject - (instancetype)initWithQuery:(FSTQuery *)query - options:(FSTListenOptions *)options + options:(ListenOptions)options viewSnapshotHandler:(firebase::firestore::core::ViewSnapshotHandler &&)viewSnapshotHandler NS_DESIGNATED_INITIALIZER; diff --git a/Firestore/Source/Core/FSTEventManager.mm b/Firestore/Source/Core/FSTEventManager.mm index df69f05b243..b3f2130f919 100644 --- a/Firestore/Source/Core/FSTEventManager.mm +++ b/Firestore/Source/Core/FSTEventManager.mm @@ -28,48 +28,15 @@ #include "Firestore/core/src/firebase/firestore/util/status.h" #include "absl/types/optional.h" +NS_ASSUME_NONNULL_BEGIN + using firebase::firestore::core::DocumentViewChange; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::OnlineState; using firebase::firestore::model::TargetId; -using firebase::firestore::util::Status; using firebase::firestore::util::MakeStatus; - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - FSTListenOptions - -@implementation FSTListenOptions - -+ (instancetype)defaultOptions { - static FSTListenOptions *defaultOptions; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - defaultOptions = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:NO - includeDocumentMetadataChanges:NO - waitForSyncWhenOnline:NO]; - }); - return defaultOptions; -} - -- (instancetype)initWithIncludeQueryMetadataChanges:(BOOL)includeQueryMetadataChanges - includeDocumentMetadataChanges:(BOOL)includeDocumentMetadataChanges - waitForSyncWhenOnline:(BOOL)waitForSyncWhenOnline { - if (self = [super init]) { - _includeQueryMetadataChanges = includeQueryMetadataChanges; - _includeDocumentMetadataChanges = includeDocumentMetadataChanges; - _waitForSyncWhenOnline = waitForSyncWhenOnline; - } - return self; -} - -- (instancetype)init { - HARD_FAIL("FSTListenOptions init not supported"); - return nil; -} - -@end +using firebase::firestore::util::Status; #pragma mark - FSTQueryListenersInfo @@ -113,8 +80,6 @@ @interface FSTQueryListener () /** The last received view snapshot. */ - (const absl::optional &)snapshot; -@property(nonatomic, strong, readonly) FSTListenOptions *options; - /** * Initial snapshots (e.g. from cache) may not be propagated to the ViewSnapshotHandler. * This flag is set to YES once we've actually raised an event. @@ -127,6 +92,8 @@ @interface FSTQueryListener () @end @implementation FSTQueryListener { + ListenOptions _options; + absl::optional _snapshot; /** The ViewSnapshotHandler associated with this query listener. */ @@ -134,11 +101,11 @@ @implementation FSTQueryListener { } - (instancetype)initWithQuery:(FSTQuery *)query - options:(FSTListenOptions *)options + options:(ListenOptions)options viewSnapshotHandler:(ViewSnapshotHandler &&)viewSnapshotHandler { if (self = [super init]) { _query = query; - _options = options; + _options = std::move(options); _viewSnapshotHandler = std::move(viewSnapshotHandler); _raisedInitialEvent = NO; } @@ -153,7 +120,7 @@ - (void)queryDidChangeViewSnapshot:(ViewSnapshot)snapshot { HARD_ASSERT(!snapshot.document_changes().empty() || snapshot.sync_state_changed(), "We got a new snapshot with no changes?"); - if (!self.options.includeDocumentMetadataChanges) { + if (!_options.include_document_metadata_changes()) { // Remove the metadata-only changes. std::vector changes; for (const DocumentViewChange &change : snapshot.document_changes()) { @@ -210,7 +177,7 @@ - (BOOL)shouldRaiseInitialEventForSnapshot:(const ViewSnapshot &)snapshot BOOL maybeOnline = onlineState != OnlineState::Offline; // Don't raise the event if we're online, aren't synced yet (checked // above) and are waiting for a sync. - if (self.options.waitForSyncWhenOnline && maybeOnline) { + if (_options.wait_for_sync_when_online() && maybeOnline) { HARD_ASSERT(snapshot.from_cache(), "Waiting for sync, but snapshot is not from cache."); return NO; } @@ -230,7 +197,7 @@ - (BOOL)shouldRaiseEventForSnapshot:(const ViewSnapshot &)snapshot { BOOL hasPendingWritesChanged = _snapshot.has_value() && _snapshot.value().has_pending_writes() != snapshot.has_pending_writes(); if (snapshot.sync_state_changed() || hasPendingWritesChanged) { - return self.options.includeQueryMetadataChanges; + return _options.include_query_metadata_changes(); } // Generally we should have hit one of the cases above, but it's possible to get here if there diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index 3b4e786107e..629eb4b520d 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -25,6 +25,7 @@ #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/core/listen_options.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" @@ -39,7 +40,6 @@ @class FSTDatabaseID; @class FSTDatabaseInfo; @class FSTDocument; -@class FSTListenOptions; @class FSTMutation; @class FSTQuery; @class FSTQueryListener; @@ -47,6 +47,8 @@ NS_ASSUME_NONNULL_BEGIN +using firebase::firestore::core::ListenOptions; + /** * FirestoreClient is a top-level class that constructs and owns all of the pieces of the client * SDK architecture. It is responsible for creating the worker queue that is shared by all of the @@ -80,7 +82,7 @@ NS_ASSUME_NONNULL_BEGIN /** Starts listening to a query. */ - (FSTQueryListener *)listenToQuery:(FSTQuery *)query - options:(FSTListenOptions *)options + options:(ListenOptions)options viewSnapshotHandler: (firebase::firestore::core::ViewSnapshotHandler &&)viewSnapshotHandler; diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index 76af1cd5d17..4bd38fafc58 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -313,11 +313,11 @@ - (void)shutdownWithCompletion:(nullable FSTVoidErrorBlock)completion { } - (FSTQueryListener *)listenToQuery:(FSTQuery *)query - options:(FSTListenOptions *)options + options:(ListenOptions)options viewSnapshotHandler:(ViewSnapshotHandler &&)viewSnapshotHandler { FSTQueryListener *listener = [[FSTQueryListener alloc] initWithQuery:query - options:options + options:std::move(options) viewSnapshotHandler:std::move(viewSnapshotHandler)]; _workerQueue->Enqueue([self, listener] { [self.eventManager addListener:listener]; }); diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.h b/Firestore/core/src/firebase/firestore/api/document_reference.h index d2bab6464ca..49d74cd5262 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.h +++ b/Firestore/core/src/firebase/firestore/api/document_reference.h @@ -32,6 +32,7 @@ #import "FIRListenerRegistration.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" +#include "Firestore/core/src/firebase/firestore/core/listen_options.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/statusor_callback.h" @@ -39,7 +40,6 @@ NS_ASSUME_NONNULL_BEGIN @class FIRFirestore; -@class FSTListenOptions; @class FSTMutation; namespace firebase { @@ -91,7 +91,7 @@ class DocumentReference { id AddSnapshotListener( util::StatusOrCallback&& listener, - FSTListenOptions* options); + core::ListenOptions options); private: Firestore* firestore_ = nullptr; diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index 75f568e79d7..4ab3196f3fd 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -122,10 +122,10 @@ return; } - FSTListenOptions* options = - [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:true - includeDocumentMetadataChanges:true - waitForSyncWhenOnline:true]; + ListenOptions options( + /*include_query_metadata_changes=*/true, + /*include_document_metadata_changes=*/true, + /*wait_for_sync_when_online=*/true); // TODO(varconst): replace with a synchronization primitive that doesn't // require libdispatch. See @@ -173,12 +173,13 @@ } }; - *listener_registration = AddSnapshotListener(std::move(listener), options); + *listener_registration = + AddSnapshotListener(std::move(listener), std::move(options)); dispatch_semaphore_signal(registered); } id DocumentReference::AddSnapshotListener( - StatusOrCallback&& listener, FSTListenOptions* options) { + StatusOrCallback&& listener, ListenOptions options) { Firestore* firestore = firestore_; FSTQuery* query = [FSTQuery queryWithPath:key_.path()]; DocumentKey key = key_; diff --git a/Firestore/core/src/firebase/firestore/core/CMakeLists.txt b/Firestore/core/src/firebase/firestore/core/CMakeLists.txt index 6e734049fdc..2481596abca 100644 --- a/Firestore/core/src/firebase/firestore/core/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/core/CMakeLists.txt @@ -19,6 +19,7 @@ cc_library( database_info.h filter.cc filter.h + listen_options.h target_id_generator.cc target_id_generator.h query.cc diff --git a/Firestore/core/src/firebase/firestore/core/listen_options.h b/Firestore/core/src/firebase/firestore/core/listen_options.h new file mode 100644 index 00000000000..913fe8693d2 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/listen_options.h @@ -0,0 +1,91 @@ +/* + * Copyright 2019 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_CORE_LISTEN_OPTIONS_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_LISTEN_OPTIONS_H_ + +namespace firebase { +namespace firestore { +namespace core { + +class ListenOptions { + public: + ListenOptions() = default; + + /** + * Creates a new ListenOptions. + * + * @param include_query_metadata_changes Raise events when only metadata of + * the query changes. + * @param include_document_metadata_changes Raise events when only metadata of + * documents changes. + * @param wait_for_sync_when_online Wait for a sync with the server when + * online, but still raise events while offline + */ + ListenOptions(bool include_query_metadata_changes, + bool include_document_metadata_changes, + bool wait_for_sync_when_online) + : include_query_metadata_changes_(include_query_metadata_changes), + include_document_metadata_changes_(include_document_metadata_changes), + wait_for_sync_when_online_(wait_for_sync_when_online) { + } + + /** + * Creates a default ListenOptions, with metadata changes and + * wait_for_sync_when_online disabled. + */ + static ListenOptions DefaultOptions() { + return ListenOptions( + /*include_query_metadata_changes=*/false, + /*include_document_metadata_changes=*/false, + /*wait_for_sync_when_online=*/false); + } + + /** + * Creates a ListenOptions which optionally includes both query and document + * metadata changes. + */ + static ListenOptions FromIncludeMetadataChanges( + bool include_metadata_changes) { + return ListenOptions( + /*include_query_metadata_changes=*/include_metadata_changes, + /*include_document_metadata_changes=*/include_metadata_changes, + /*wait_for_sync_when_online=*/false); + } + + bool include_query_metadata_changes() const { + return include_query_metadata_changes_; + } + + bool include_document_metadata_changes() const { + return include_document_metadata_changes_; + } + + bool wait_for_sync_when_online() const { + return wait_for_sync_when_online_; + } + + private: + bool include_query_metadata_changes_ = false; + bool include_document_metadata_changes_ = false; + bool wait_for_sync_when_online_ = false; +}; + +} // namespace core +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_LISTEN_OPTIONS_H_ diff --git a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h index a4fdc2c8342..96336684c38 100644 --- a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h +++ b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h @@ -168,7 +168,6 @@ OBJC_PRINT_TO(FSTLRUGarbageCollector); OBJC_PRINT_TO(FSTLevelDB); OBJC_PRINT_TO(FSTLevelDBLRUDelegate); OBJC_PRINT_TO(FSTLimboDocumentChange); -OBJC_PRINT_TO(FSTListenOptions); OBJC_PRINT_TO(FSTListenerRegistration); OBJC_PRINT_TO(FSTLocalDocumentsView); OBJC_PRINT_TO(FSTLocalSerializer); From 315effec4a9b719f6f56aef0503b18cc7a4d3626 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 26 Mar 2019 07:11:02 -0700 Subject: [PATCH 088/214] Port FSTQueryListener to C++ (#2618) --- .../Tests/Core/FSTEventManagerTests.mm | 100 +++---- .../Tests/Core/FSTQueryListenerTests.mm | 167 ++++------- .../SpecTests/FSTSyncEngineTestDriver.mm | 33 ++- Firestore/Source/API/FIRDocumentReference.mm | 1 + .../API/FIRListenerRegistration+Internal.h | 9 +- .../Source/API/FIRListenerRegistration.mm | 35 ++- Firestore/Source/API/FIRQuery.mm | 3 +- Firestore/Source/Core/FSTEventManager.h | 33 +-- Firestore/Source/Core/FSTEventManager.mm | 278 +++++------------- Firestore/Source/Core/FSTFirestoreClient.h | 13 +- Firestore/Source/Core/FSTFirestoreClient.mm | 14 +- Firestore/Source/Util/FSTAsyncQueryListener.h | 4 +- .../firestore/api/document_reference.mm | 2 +- .../firebase/firestore/core/query_listener.h | 116 ++++++++ .../firebase/firestore/core/query_listener.mm | 147 +++++++++ .../firestore/immutable/CMakeLists.txt | 4 +- .../firestore/local/memory_query_cache.h | 6 +- .../firestore/util/objc_compatibility.h | 10 + .../firebase/firestore/testutil/xcgmock.h | 3 +- scripts/cpplint.py | 27 ++ 20 files changed, 553 insertions(+), 452 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/core/query_listener.h create mode 100644 Firestore/core/src/firebase/firestore/core/query_listener.mm diff --git a/Firestore/Example/Tests/Core/FSTEventManagerTests.mm b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm index e0ecfc577d0..875cff98880 100644 --- a/Firestore/Example/Tests/Core/FSTEventManagerTests.mm +++ b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm @@ -19,7 +19,9 @@ #import #import +#include #include +#include #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSyncEngine.h" @@ -31,26 +33,31 @@ #include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" +#include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h" +using firebase::firestore::core::ListenOptions; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::DocumentSet; using firebase::firestore::model::OnlineState; using firebase::firestore::util::StatusOr; +using testing::ElementsAre; NS_ASSUME_NONNULL_BEGIN -/** - * Converts an OnlineState to an NSNumber, usually for the purpose of adding - * it to an NSArray or similar container. There's no direct conversion from a - * strongly-typed enum to an integral type that could be passed to an NSNumber - * initializer. - */ -static NSNumber *ToNSNumber(OnlineState state) { - return @(static_cast::type>(state)); +namespace { + +ViewSnapshotHandler NoopViewSnapshotHandler() { + return [](const StatusOr &) {}; +} + +std::shared_ptr NoopQueryListener(FSTQuery *query) { + return QueryListener::Create(query, NoopViewSnapshotHandler()); } +} // namespace + // FSTEventManager implements this delegate privately @interface FSTEventManager () @end @@ -60,16 +67,10 @@ @interface FSTEventManagerTests : XCTestCase @implementation FSTEventManagerTests -- (FSTQueryListener *)noopListenerForQuery:(FSTQuery *)query { - return [[FSTQueryListener alloc] initWithQuery:query - options:ListenOptions::DefaultOptions() - viewSnapshotHandler:[](const StatusOr &) {}]; -} - - (void)testHandlesManyListenersPerQuery { FSTQuery *query = FSTTestQuery("foo/bar"); - FSTQueryListener *listener1 = [self noopListenerForQuery:query]; - FSTQueryListener *listener2 = [self noopListenerForQuery:query]; + auto listener1 = NoopQueryListener(query); + auto listener2 = NoopQueryListener(query); FSTSyncEngine *syncEngineMock = OCMStrictClassMock([FSTSyncEngine class]); OCMExpect([syncEngineMock setSyncEngineDelegate:[OCMArg any]]); @@ -89,7 +90,7 @@ - (void)testHandlesManyListenersPerQuery { - (void)testHandlesUnlistenOnUnknownListenerGracefully { FSTQuery *query = FSTTestQuery("foo/bar"); - FSTQueryListener *listener = [self noopListenerForQuery:query]; + auto listener = NoopQueryListener(query); FSTSyncEngine *syncEngineMock = OCMStrictClassMock([FSTSyncEngine class]); OCMExpect([syncEngineMock setSyncEngineDelegate:[OCMArg any]]); @@ -99,13 +100,6 @@ - (void)testHandlesUnlistenOnUnknownListenerGracefully { OCMVerifyAll((id)syncEngineMock); } -- (FSTQueryListener *)queryListenerForQuery:(FSTQuery *)query - withHandler:(ViewSnapshotHandler &&)handler { - return [[FSTQueryListener alloc] initWithQuery:query - options:ListenOptions::DefaultOptions() - viewSnapshotHandler:std::move(handler)]; -} - - (ViewSnapshot)makeEmptyViewSnapshotWithQuery:(FSTQuery *)query { DocumentSet emptyDocs{query.comparator}; // sync_state_changed has to be `true` to prevent an assertion about a meaningless view snapshot. @@ -118,23 +112,17 @@ - (void)testNotifiesListenersInTheRightOrder { FSTQuery *query2 = FSTTestQuery("bar/baz"); NSMutableArray *eventOrder = [NSMutableArray array]; - FSTQueryListener *listener1 = - [self queryListenerForQuery:query1 - withHandler:[eventOrder](const StatusOr &) { - [eventOrder addObject:@"listener1"]; - }]; + auto listener1 = QueryListener::Create(query1, [eventOrder](const StatusOr &) { + [eventOrder addObject:@"listener1"]; + }); - FSTQueryListener *listener2 = - [self queryListenerForQuery:query2 - withHandler:[eventOrder](const StatusOr &) { - [eventOrder addObject:@"listener2"]; - }]; + auto listener2 = QueryListener::Create(query2, [eventOrder](const StatusOr &) { + [eventOrder addObject:@"listener2"]; + }); - FSTQueryListener *listener3 = - [self queryListenerForQuery:query1 - withHandler:[eventOrder](const StatusOr &) { - [eventOrder addObject:@"listener3"]; - }]; + auto listener3 = QueryListener::Create(query1, [eventOrder](const StatusOr &) { + [eventOrder addObject:@"listener3"]; + }); FSTSyncEngine *syncEngineMock = OCMClassMock([FSTSyncEngine class]); FSTEventManager *eventManager = [FSTEventManager eventManagerWithSyncEngine:syncEngineMock]; @@ -155,27 +143,31 @@ - (void)testNotifiesListenersInTheRightOrder { - (void)testWillForwardOnlineStateChanges { FSTQuery *query = FSTTestQuery("foo/bar"); - FSTQueryListener *fakeListener = OCMClassMock([FSTQueryListener class]); - NSMutableArray *events = [NSMutableArray array]; - OCMStub([fakeListener query]).andReturn(query); - OCMStub([fakeListener applyChangedOnlineState:OnlineState::Unknown]) - .andDo(^(NSInvocation *invocation) { - [events addObject:ToNSNumber(OnlineState::Unknown)]; - }); - OCMStub([fakeListener applyChangedOnlineState:OnlineState::Online]) - .andDo(^(NSInvocation *invocation) { - [events addObject:ToNSNumber(OnlineState::Online)]; - }); + + class FakeQueryListener : public QueryListener { + public: + explicit FakeQueryListener(FSTQuery *query) + : QueryListener(query, ListenOptions::DefaultOptions(), NoopViewSnapshotHandler()) { + } + + void OnOnlineStateChanged(OnlineState online_state) override { + events.push_back(online_state); + } + + std::vector events; + }; + + auto fake_listener = std::make_shared(query); FSTSyncEngine *syncEngineMock = OCMClassMock([FSTSyncEngine class]); OCMExpect([syncEngineMock setSyncEngineDelegate:[OCMArg any]]); FSTEventManager *eventManager = [FSTEventManager eventManagerWithSyncEngine:syncEngineMock]; - [eventManager addListener:fakeListener]; - XCTAssertEqualObjects(events, @[ ToNSNumber(OnlineState::Unknown) ]); + [eventManager addListener:fake_listener]; + XC_ASSERT_THAT(fake_listener->events, ElementsAre(OnlineState::Unknown)); + [eventManager applyChangedOnlineState:OnlineState::Online]; - XCTAssertEqualObjects(events, - (@[ ToNSNumber(OnlineState::Unknown), ToNSNumber(OnlineState::Online) ])); + XC_ASSERT_THAT(fake_listener->events, ElementsAre(OnlineState::Unknown, OnlineState::Online)); } @end diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index 234b0b82a19..eaea44453f9 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -41,6 +41,7 @@ using firebase::firestore::FirestoreErrorCode; using firebase::firestore::core::DocumentViewChange; +using firebase::firestore::core::ListenOptions; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::DocumentKeySet; @@ -71,6 +72,12 @@ ViewSnapshot ExcludingMetadataChanges(const ViewSnapshot &snapshot) { }; } +ViewSnapshotHandler Accumulating(std::vector *values) { + return [values](const StatusOr &maybe_snapshot) { + values->push_back(maybe_snapshot.ValueOrDie()); + }; +} + } // namespace @interface FSTQueryListenerTests : XCTestCase @@ -96,10 +103,8 @@ - (void)testRaisesCollectionEvents { FSTDocument *doc2prime = FSTTestDoc("rooms/Hades", 3, @{@"name" : @"Hades", @"owner" : @"Jonny"}, FSTDocumentStateSynced); - FSTQueryListener *listener = [self listenToQuery:query - options:_includeMetadataChanges - accumulatingSnapshots:&accum]; - FSTQueryListener *otherListener = [self listenToQuery:query accumulatingSnapshots:&otherAccum]; + auto listener = QueryListener::Create(query, _includeMetadataChanges, Accumulating(&accum)); + auto otherListener = QueryListener::Create(query, Accumulating(&otherAccum)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt).value(); @@ -110,9 +115,9 @@ - (void)testRaisesCollectionEvents { DocumentViewChange change3{doc2prime, DocumentViewChange::Type::kModified}; DocumentViewChange change4{doc2prime, DocumentViewChange::Type::kAdded}; - [listener queryDidChangeViewSnapshot:snap1]; - [listener queryDidChangeViewSnapshot:snap2]; - [otherListener queryDidChangeViewSnapshot:snap2]; + listener->OnViewSnapshot(snap1); + listener->OnViewSnapshot(snap2); + otherListener->OnViewSnapshot(snap2); XC_ASSERT_THAT(accum, ElementsAre(snap1, snap2)); XC_ASSERT_THAT(accum[0].document_changes(), ElementsAre(change1, change2)); @@ -133,13 +138,12 @@ - (void)testRaisesErrorEvent { __block std::vector accum; FSTQuery *query = FSTTestQuery("rooms/Eros"); - FSTQueryListener *listener = [self listenToQuery:query - handler:^(const StatusOr &maybe_snapshot) { - accum.push_back(maybe_snapshot.status()); - }]; + auto listener = QueryListener::Create(query, ^(const StatusOr &maybe_snapshot) { + accum.push_back(maybe_snapshot.status()); + }); Status testError{FirestoreErrorCode::Unauthenticated, "Some info"}; - [listener queryDidError:testError]; + listener->OnError(testError); XC_ASSERT_THAT(accum, ElementsAre(testError)); } @@ -148,18 +152,16 @@ - (void)testRaisesEventForEmptyCollectionAfterSync { std::vector accum; FSTQuery *query = FSTTestQuery("rooms"); - FSTQueryListener *listener = [self listenToQuery:query - options:_includeMetadataChanges - accumulatingSnapshots:&accum]; + auto listener = QueryListener::Create(query, _includeMetadataChanges, Accumulating(&accum)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[], absl::nullopt).value(); ViewSnapshot snap2 = FSTTestApplyChanges(view, @[], FSTTestTargetChangeMarkCurrent()).value(); - [listener queryDidChangeViewSnapshot:snap1]; + listener->OnViewSnapshot(snap1); XC_ASSERT_THAT(accum, IsEmpty()); - [listener queryDidChangeViewSnapshot:snap2]; + listener->OnViewSnapshot(snap2); XC_ASSERT_THAT(accum, ElementsAre(snap2)); } @@ -208,11 +210,9 @@ - (void)testDoesNotRaiseEventsForMetadataChangesUnlessSpecified { FSTDocument *doc1 = FSTTestDoc("rooms/Eros", 1, @{@"name" : @"Eros"}, FSTDocumentStateSynced); FSTDocument *doc2 = FSTTestDoc("rooms/Hades", 2, @{@"name" : @"Hades"}, FSTDocumentStateSynced); - FSTQueryListener *filteredListener = [self listenToQuery:query - accumulatingSnapshots:&filteredAccum]; - FSTQueryListener *fullListener = [self listenToQuery:query - options:_includeMetadataChanges - accumulatingSnapshots:&fullAccum]; + auto filteredListener = QueryListener::Create(query, Accumulating(&filteredAccum)); + auto fullListener = + QueryListener::Create(query, _includeMetadataChanges, Accumulating(&fullAccum)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt).value(); @@ -221,13 +221,13 @@ - (void)testDoesNotRaiseEventsForMetadataChangesUnlessSpecified { ViewSnapshot snap2 = FSTTestApplyChanges(view, @[], ackTarget).value(); ViewSnapshot snap3 = FSTTestApplyChanges(view, @[ doc2 ], absl::nullopt).value(); - [filteredListener queryDidChangeViewSnapshot:snap1]; // local event - [filteredListener queryDidChangeViewSnapshot:snap2]; // no event - [filteredListener queryDidChangeViewSnapshot:snap3]; // doc2 update + filteredListener->OnViewSnapshot(snap1); // local event + filteredListener->OnViewSnapshot(snap2); // no event + filteredListener->OnViewSnapshot(snap3); // doc2 update - [fullListener queryDidChangeViewSnapshot:snap1]; // local event - [fullListener queryDidChangeViewSnapshot:snap2]; // state change event - [fullListener queryDidChangeViewSnapshot:snap3]; // doc2 update + fullListener->OnViewSnapshot(snap1); // local event + fullListener->OnViewSnapshot(snap2); // state change event + fullListener->OnViewSnapshot(snap3); // doc2 update XC_ASSERT_THAT(filteredAccum, ElementsAre(ExcludingMetadataChanges(snap1), ExcludingMetadataChanges(snap3))); @@ -251,11 +251,8 @@ ListenOptions options( /*include_document_metadata_changes=*/true, /*wait_for_sync_when_online=*/false); - FSTQueryListener *filteredListener = [self listenToQuery:query - accumulatingSnapshots:&filteredAccum]; - FSTQueryListener *fullListener = [self listenToQuery:query - options:options - accumulatingSnapshots:&fullAccum]; + auto filteredListener = QueryListener::Create(query, Accumulating(&filteredAccum)); + auto fullListener = QueryListener::Create(query, options, Accumulating(&fullAccum)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt).value(); @@ -267,12 +264,12 @@ ListenOptions options( DocumentViewChange change3{doc1Prime, DocumentViewChange::Type::kMetadata}; DocumentViewChange change4{doc3, DocumentViewChange::Type::kAdded}; - [filteredListener queryDidChangeViewSnapshot:snap1]; - [filteredListener queryDidChangeViewSnapshot:snap2]; - [filteredListener queryDidChangeViewSnapshot:snap3]; - [fullListener queryDidChangeViewSnapshot:snap1]; - [fullListener queryDidChangeViewSnapshot:snap2]; - [fullListener queryDidChangeViewSnapshot:snap3]; + filteredListener->OnViewSnapshot(snap1); + filteredListener->OnViewSnapshot(snap2); + filteredListener->OnViewSnapshot(snap3); + fullListener->OnViewSnapshot(snap1); + fullListener->OnViewSnapshot(snap2); + fullListener->OnViewSnapshot(snap3); XC_ASSERT_THAT(filteredAccum, ElementsAre(ExcludingMetadataChanges(snap1), ExcludingMetadataChanges(snap3))); @@ -303,9 +300,7 @@ ListenOptions options( /*include_query_metadata_changes=*/true, /*include_document_metadata_changes=*/false, /*wait_for_sync_when_online=*/false); - FSTQueryListener *fullListener = [self listenToQuery:query - options:options - accumulatingSnapshots:&fullAccum]; + auto fullListener = QueryListener::Create(query, options, Accumulating(&fullAccum)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt).value(); @@ -313,10 +308,10 @@ ListenOptions options( ViewSnapshot snap3 = FSTTestApplyChanges(view, @[ doc3 ], absl::nullopt).value(); ViewSnapshot snap4 = FSTTestApplyChanges(view, @[ doc2Prime ], absl::nullopt).value(); - [fullListener queryDidChangeViewSnapshot:snap1]; - [fullListener queryDidChangeViewSnapshot:snap2]; // Emits no events. - [fullListener queryDidChangeViewSnapshot:snap3]; - [fullListener queryDidChangeViewSnapshot:snap4]; // Metadata change event. + fullListener->OnViewSnapshot(snap1); + fullListener->OnViewSnapshot(snap2); // Emits no events. + fullListener->OnViewSnapshot(snap3); + fullListener->OnViewSnapshot(snap4); // Metadata change event. ViewSnapshot expectedSnap4{ snap4.query(), @@ -344,8 +339,7 @@ - (void)testMetadataOnlyDocumentChangesAreFilteredOutWhenIncludeDocumentMetadata FSTTestDoc("rooms/Eros", 1, @{@"name" : @"Eros"}, FSTDocumentStateSynced); FSTDocument *doc3 = FSTTestDoc("rooms/Other", 3, @{@"name" : @"Other"}, FSTDocumentStateSynced); - FSTQueryListener *filteredListener = [self listenToQuery:query - accumulatingSnapshots:&filteredAccum]; + auto filteredListener = QueryListener::Create(query, Accumulating(&filteredAccum)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt).value(); @@ -353,8 +347,8 @@ - (void)testMetadataOnlyDocumentChangesAreFilteredOutWhenIncludeDocumentMetadata DocumentViewChange change3{doc3, DocumentViewChange::Type::kAdded}; - [filteredListener queryDidChangeViewSnapshot:snap1]; - [filteredListener queryDidChangeViewSnapshot:snap2]; + filteredListener->OnViewSnapshot(snap1); + filteredListener->OnViewSnapshot(snap2); ViewSnapshot expectedSnap2{snap2.query(), snap2.documents(), @@ -378,9 +372,7 @@ ListenOptions options( /*include_query_metadata_changes=*/false, /*include_document_metadata_changes=*/false, /*wait_for_sync_when_online=*/true); - FSTQueryListener *listener = [self listenToQuery:query - options:options - accumulatingSnapshots:&events]; + auto listener = QueryListener::Create(query, options, Accumulating(&events)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt).value(); @@ -388,12 +380,12 @@ ListenOptions options( ViewSnapshot snap3 = FSTTestApplyChanges(view, @[], FSTTestTargetChangeAckDocuments({doc1.key, doc2.key})).value(); - [listener applyChangedOnlineState:OnlineState::Online]; // no event - [listener queryDidChangeViewSnapshot:snap1]; - [listener applyChangedOnlineState:OnlineState::Unknown]; - [listener applyChangedOnlineState:OnlineState::Online]; - [listener queryDidChangeViewSnapshot:snap2]; - [listener queryDidChangeViewSnapshot:snap3]; + listener->OnOnlineStateChanged(OnlineState::Online); // no event + listener->OnViewSnapshot(snap1); + listener->OnOnlineStateChanged(OnlineState::Unknown); + listener->OnOnlineStateChanged(OnlineState::Online); + listener->OnViewSnapshot(snap2); + listener->OnViewSnapshot(snap3); DocumentViewChange change1{doc1, DocumentViewChange::Type::kAdded}; DocumentViewChange change2{doc2, DocumentViewChange::Type::kAdded}; @@ -420,20 +412,18 @@ ListenOptions options( /*include_document_metadata_changes=*/false, /*wait_for_sync_when_online=*/true); - FSTQueryListener *listener = [self listenToQuery:query - options:options - accumulatingSnapshots:&events]; + auto listener = QueryListener::Create(query, options, Accumulating(&events)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt).value(); ViewSnapshot snap2 = FSTTestApplyChanges(view, @[ doc2 ], absl::nullopt).value(); - [listener applyChangedOnlineState:OnlineState::Online]; // no event - [listener queryDidChangeViewSnapshot:snap1]; // no event - [listener applyChangedOnlineState:OnlineState::Offline]; // event - [listener applyChangedOnlineState:OnlineState::Unknown]; // no event - [listener applyChangedOnlineState:OnlineState::Offline]; // no event - [listener queryDidChangeViewSnapshot:snap2]; // another event + listener->OnOnlineStateChanged(OnlineState::Online); // no event + listener->OnViewSnapshot(snap1); // no event + listener->OnOnlineStateChanged(OnlineState::Offline); // event + listener->OnOnlineStateChanged(OnlineState::Unknown); // no event + listener->OnOnlineStateChanged(OnlineState::Offline); // no event + listener->OnViewSnapshot(snap2); // another event DocumentViewChange change1{doc1, DocumentViewChange::Type::kAdded}; DocumentViewChange change2{doc2, DocumentViewChange::Type::kAdded}; @@ -461,16 +451,14 @@ - (void)testWillRaiseInitialEventWhenGoingOfflineAndThereAreNoDocs { std::vector events; FSTQuery *query = FSTTestQuery("rooms"); - FSTQueryListener *listener = [self listenToQuery:query - options:ListenOptions::DefaultOptions() - accumulatingSnapshots:&events]; + auto listener = QueryListener::Create(query, Accumulating(&events)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[], absl::nullopt).value(); - [listener applyChangedOnlineState:OnlineState::Online]; // no event - [listener queryDidChangeViewSnapshot:snap1]; // no event - [listener applyChangedOnlineState:OnlineState::Offline]; // event + listener->OnOnlineStateChanged(OnlineState::Online); // no event + listener->OnViewSnapshot(snap1); // no event + listener->OnOnlineStateChanged(OnlineState::Offline); // event ViewSnapshot expectedSnap{query, /*documents=*/snap1.documents(), @@ -487,15 +475,13 @@ - (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { std::vector events; FSTQuery *query = FSTTestQuery("rooms"); - FSTQueryListener *listener = [self listenToQuery:query - options:ListenOptions::DefaultOptions() - accumulatingSnapshots:&events]; + auto listener = QueryListener::Create(query, Accumulating(&events)); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot snap1 = FSTTestApplyChanges(view, @[], absl::nullopt).value(); - [listener applyChangedOnlineState:OnlineState::Offline]; // no event - [listener queryDidChangeViewSnapshot:snap1]; // event + listener->OnOnlineStateChanged(OnlineState::Offline); // no event + listener->OnViewSnapshot(snap1); // event ViewSnapshot expectedSnap{query, /*documents=*/snap1.documents(), @@ -508,29 +494,6 @@ - (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { XC_ASSERT_THAT(events, ElementsAre(expectedSnap)); } -- (FSTQueryListener *)listenToQuery:(FSTQuery *)query handler:(ViewSnapshotHandler &&)handler { - return [[FSTQueryListener alloc] initWithQuery:query - options:ListenOptions::DefaultOptions() - viewSnapshotHandler:std::move(handler)]; -} - -- (FSTQueryListener *)listenToQuery:(FSTQuery *)query - options:(ListenOptions)options - accumulatingSnapshots:(std::vector *)values { - return [[FSTQueryListener alloc] initWithQuery:query - options:options - viewSnapshotHandler:^(const StatusOr &maybe_snapshot) { - values->push_back(maybe_snapshot.ValueOrDie()); - }]; -} - -- (FSTQueryListener *)listenToQuery:(FSTQuery *)query - accumulatingSnapshots:(std::vector *)values { - return [self listenToQuery:query - options:ListenOptions::DefaultOptions() - accumulatingSnapshots:values]; -} - @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index b100c7f049f..f1e42579480 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -47,17 +47,20 @@ #include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" +#include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" #include "Firestore/core/src/firebase/firestore/util/status.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" #include "Firestore/core/src/firebase/firestore/util/string_format.h" #include "Firestore/core/src/firebase/firestore/util/to_string.h" #include "absl/memory/memory.h" +namespace objc = firebase::firestore::util::objc; using firebase::firestore::FirestoreErrorCode; using firebase::firestore::auth::EmptyCredentialsProvider; using firebase::firestore::auth::HashUser; using firebase::firestore::auth::User; using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::core::ListenOptions; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; @@ -120,9 +123,6 @@ @interface FSTSyncEngineTestDriver () @property(nonatomic, strong, readonly) void (^eventHandler)(FSTQueryEvent *); /** The events received by our eventHandler and not yet retrieved via capturedEventsSinceLastCall */ @property(nonatomic, strong, readonly) NSMutableArray *events; -/** A dictionary for tracking the listens on queries. */ -@property(nonatomic, strong, readonly) - NSMutableDictionary *queryListeners; #pragma mark - Data structures for holding events sent by the write stream. @@ -144,6 +144,9 @@ @implementation FSTSyncEngineTestDriver { std::unordered_map *, HashUser> _outstandingWrites; DocumentKeySet _expectedLimboDocuments; + /** A dictionary for tracking the listens on queries. */ + objc::unordered_map> _queryListeners; + DatabaseInfo _databaseInfo; User _currentUser; EmptyCredentialsProvider _credentialProvider; @@ -199,8 +202,6 @@ - (instancetype)initWithPersistence:(id)persistence }; _events = events; - _queryListeners = [NSMutableDictionary dictionary]; - _currentUser = initialUser; _acknowledgedDocs = [NSMutableArray array]; @@ -350,10 +351,8 @@ - (TargetId)addUserListenerWithQuery:(FSTQuery *)query { // TODO(dimond): Allow customizing listen options in spec tests // TODO(dimond): Change spec tests to verify isFromCache on snapshots ListenOptions options = ListenOptions::FromIncludeMetadataChanges(true); - FSTQueryListener *listener = [[FSTQueryListener alloc] - initWithQuery:query - options:options - viewSnapshotHandler:[self, query](const StatusOr &maybe_snapshot) { + auto listener = std::make_shared( + query, options, [self, query](const StatusOr &maybe_snapshot) { FSTQueryEvent *event = [[FSTQueryEvent alloc] init]; event.query = query; if (maybe_snapshot.ok()) { @@ -363,17 +362,21 @@ - (TargetId)addUserListenerWithQuery:(FSTQuery *)query { } [self.events addObject:event]; - }]; - self.queryListeners[query] = listener; + }); + _queryListeners[query] = listener; TargetId targetID; _workerQueue->EnqueueBlocking([&] { targetID = [self.eventManager addListener:listener]; }); return targetID; } - (void)removeUserListenerWithQuery:(FSTQuery *)query { - FSTQueryListener *listener = self.queryListeners[query]; - [self.queryListeners removeObjectForKey:query]; - _workerQueue->EnqueueBlocking([&] { [self.eventManager removeListener:listener]; }); + auto found_iter = _queryListeners.find(query); + if (found_iter != _queryListeners.end()) { + std::shared_ptr listener = found_iter->second; + _queryListeners.erase(found_iter); + + _workerQueue->EnqueueBlocking([&] { [self.eventManager removeListener:listener]; }); + } } - (void)writeUserMutation:(FSTMutation *)mutation { @@ -411,7 +414,7 @@ - (void)receiveWatchStreamError:(int)errorCode userInfo:(NSDictionaryEnqueueBlocking([&] { _datastore->FailWatchStream(error); // Unlike web, stream should re-open synchronously (if we have any listeners) - if (self.queryListeners.count > 0) { + if (!_queryListeners.empty()) { HARD_ASSERT(_datastore->IsWatchStreamOpen(), "Watch stream is open"); } }); diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index d11501ba436..3459d936dc2 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -48,6 +48,7 @@ using firebase::firestore::api::DocumentReference; using firebase::firestore::api::DocumentSnapshot; using firebase::firestore::api::Firestore; +using firebase::firestore::core::ListenOptions; using firebase::firestore::core::ParsedSetData; using firebase::firestore::core::ParsedUpdateData; using firebase::firestore::model::DocumentKey; diff --git a/Firestore/Source/API/FIRListenerRegistration+Internal.h b/Firestore/Source/API/FIRListenerRegistration+Internal.h index 4cd2d579364..8ba20261782 100644 --- a/Firestore/Source/API/FIRListenerRegistration+Internal.h +++ b/Firestore/Source/API/FIRListenerRegistration+Internal.h @@ -16,18 +16,23 @@ #import "FIRListenerRegistration.h" +#include + +#include "Firestore/core/src/firebase/firestore/core/query_listener.h" + @class FSTAsyncQueryListener; @class FSTFirestoreClient; -@class FSTQueryListener; NS_ASSUME_NONNULL_BEGIN +using firebase::firestore::core::QueryListener; + /** Private implementation of the FIRListenerRegistration protocol. */ @interface FSTListenerRegistration : NSObject - (instancetype)initWithClient:(FSTFirestoreClient *)client asyncListener:(FSTAsyncQueryListener *)asyncListener - internalListener:(FSTQueryListener *)internalListener; + internalListener:(std::shared_ptr)internalListener; @end diff --git a/Firestore/Source/API/FIRListenerRegistration.mm b/Firestore/Source/API/FIRListenerRegistration.mm index 3d4d5ccd8e2..96ccddf9345 100644 --- a/Firestore/Source/API/FIRListenerRegistration.mm +++ b/Firestore/Source/API/FIRListenerRegistration.mm @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" @@ -21,24 +23,20 @@ NS_ASSUME_NONNULL_BEGIN -@interface FSTListenerRegistration () - -/** The client that was used to register this listen. */ -@property(nonatomic, strong, readonly) FSTFirestoreClient *client; - -/** The async listener that is used to mute events synchronously. */ -@property(nonatomic, strong, readonly, nullable) FSTAsyncQueryListener *asyncListener; - -/** The internal FSTQueryListener that can be used to unlisten the query. */ -@property(nonatomic, strong, readwrite, nullable) FSTQueryListener *internalListener; +@implementation FSTListenerRegistration { + /** The client that was used to register this listen. */ + FSTFirestoreClient *_client; -@end + /** The async listener that is used to mute events synchronously. */ + FSTAsyncQueryListener *_asyncListener; -@implementation FSTListenerRegistration + /** The internal QueryListener that can be used to unlisten the query. */ + std::weak_ptr _internalListener; +} - (instancetype)initWithClient:(FSTFirestoreClient *)client asyncListener:(FSTAsyncQueryListener *)asyncListener - internalListener:(FSTQueryListener *)internalListener { + internalListener:(std::shared_ptr)internalListener { if (self = [super init]) { _client = client; _asyncListener = asyncListener; @@ -48,9 +46,14 @@ - (instancetype)initWithClient:(FSTFirestoreClient *)client } - (void)remove { - [self.asyncListener mute]; - [self.client removeListener:self.internalListener]; - _internalListener = nil; + [_asyncListener mute]; + + std::shared_ptr listener = _internalListener.lock(); + if (listener) { + [_client removeListener:listener]; + listener.reset(); + _internalListener.reset(); + } _asyncListener = nil; } diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 9b46040bda4..40c3429e9a8 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -16,6 +16,7 @@ #import "FIRQuery.h" +#include #include #import "FIRDocumentReference.h" @@ -194,7 +195,7 @@ ListenOptions listenOptions( [[FSTAsyncQueryListener alloc] initWithExecutor:self.firestore.client.userExecutor snapshotHandler:std::move(snapshotHandler)]; - FSTQueryListener *internalListener = + std::shared_ptr internalListener = [firestore->client() listenToQuery:query options:internalOptions viewSnapshotHandler:[asyncListener asyncSnapshotHandler]]; diff --git a/Firestore/Source/Core/FSTEventManager.h b/Firestore/Source/Core/FSTEventManager.h index 4b9d7b800f8..c3d9639c072 100644 --- a/Firestore/Source/Core/FSTEventManager.h +++ b/Firestore/Source/Core/FSTEventManager.h @@ -16,7 +16,9 @@ #import -#include "Firestore/core/src/firebase/firestore/core/listen_options.h" +#include + +#include "Firestore/core/src/firebase/firestore/core/query_listener.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/util/status.h" @@ -26,30 +28,7 @@ NS_ASSUME_NONNULL_BEGIN -using firebase::firestore::core::ListenOptions; - -#pragma mark - FSTQueryListener - -/** - * FSTQueryListener takes a series of internal view snapshots and determines when to raise - * user-facing events. - */ -@interface FSTQueryListener : NSObject - -- (instancetype)initWithQuery:(FSTQuery *)query - options:(ListenOptions)options - viewSnapshotHandler:(firebase::firestore::core::ViewSnapshotHandler &&)viewSnapshotHandler - NS_DESIGNATED_INITIALIZER; - -- (instancetype)init NS_UNAVAILABLE; - -- (void)queryDidChangeViewSnapshot:(firebase::firestore::core::ViewSnapshot)snapshot; -- (void)queryDidError:(const firebase::firestore::util::Status &)error; -- (void)applyChangedOnlineState:(firebase::firestore::model::OnlineState)onlineState; - -@property(nonatomic, strong, readonly) FSTQuery *query; - -@end +using firebase::firestore::core::QueryListener; #pragma mark - FSTEventManager @@ -63,8 +42,8 @@ using firebase::firestore::core::ListenOptions; - (instancetype)init __attribute__((unavailable("Use static constructor method."))); -- (firebase::firestore::model::TargetId)addListener:(FSTQueryListener *)listener; -- (void)removeListener:(FSTQueryListener *)listener; +- (firebase::firestore::model::TargetId)addListener:(std::shared_ptr)listener; +- (void)removeListener:(const std::shared_ptr &)listener; - (void)applyChangedOnlineState:(firebase::firestore::model::OnlineState)onlineState; diff --git a/Firestore/Source/Core/FSTEventManager.mm b/Firestore/Source/Core/FSTEventManager.mm index b3f2130f919..aab3c0bbec2 100644 --- a/Firestore/Source/Core/FSTEventManager.mm +++ b/Firestore/Source/Core/FSTEventManager.mm @@ -16,6 +16,7 @@ #import "Firestore/Source/Core/FSTEventManager.h" +#include #include #include @@ -25,11 +26,14 @@ #include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/error_apple.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" #include "Firestore/core/src/firebase/firestore/util/status.h" +#include "absl/algorithm/container.h" #include "absl/types/optional.h" NS_ASSUME_NONNULL_BEGIN +namespace objc = firebase::firestore::util::objc; using firebase::firestore::core::DocumentViewChange; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::core::ViewSnapshotHandler; @@ -40,181 +44,38 @@ #pragma mark - FSTQueryListenersInfo +namespace { + /** * Holds the listeners and the last received ViewSnapshot for a query being tracked by * EventManager. */ -@interface FSTQueryListenersInfo : NSObject -@property(nonatomic, assign, readwrite) TargetId targetID; -@property(nonatomic, strong, readonly) NSMutableArray *listeners; - -- (const absl::optional &)viewSnapshot; -- (void)setViewSnapshot:(const absl::optional &)snapshot; - -@end - -@implementation FSTQueryListenersInfo { - absl::optional _viewSnapshot; -} - -- (const absl::optional &)viewSnapshot { - return _viewSnapshot; -} -- (void)setViewSnapshot:(const absl::optional &)snapshot { - _viewSnapshot = snapshot; -} - -- (instancetype)init { - if (self = [super init]) { - _listeners = [NSMutableArray array]; - } - return self; -} - -@end - -#pragma mark - FSTQueryListener - -@interface FSTQueryListener () - -/** The last received view snapshot. */ -- (const absl::optional &)snapshot; - -/** - * Initial snapshots (e.g. from cache) may not be propagated to the ViewSnapshotHandler. - * This flag is set to YES once we've actually raised an event. - */ -@property(nonatomic, assign, readwrite) BOOL raisedInitialEvent; - -/** The last online state this query listener got. */ -@property(nonatomic, assign, readwrite) OnlineState onlineState; - -@end - -@implementation FSTQueryListener { - ListenOptions _options; - - absl::optional _snapshot; - - /** The ViewSnapshotHandler associated with this query listener. */ - ViewSnapshotHandler _viewSnapshotHandler; -} - -- (instancetype)initWithQuery:(FSTQuery *)query - options:(ListenOptions)options - viewSnapshotHandler:(ViewSnapshotHandler &&)viewSnapshotHandler { - if (self = [super init]) { - _query = query; - _options = std::move(options); - _viewSnapshotHandler = std::move(viewSnapshotHandler); - _raisedInitialEvent = NO; - } - return self; -} - -- (const absl::optional &)snapshot { - return _snapshot; -} - -- (void)queryDidChangeViewSnapshot:(ViewSnapshot)snapshot { - HARD_ASSERT(!snapshot.document_changes().empty() || snapshot.sync_state_changed(), - "We got a new snapshot with no changes?"); - - if (!_options.include_document_metadata_changes()) { - // Remove the metadata-only changes. - std::vector changes; - for (const DocumentViewChange &change : snapshot.document_changes()) { - if (change.type() != DocumentViewChange::Type::kMetadata) { - changes.push_back(change); - } +struct QueryListenersInfo { + TargetId target_id; + std::vector> listeners; + + void Erase(const std::shared_ptr &listener) { + auto found = absl::c_find(listeners, listener); + if (found != listeners.end()) { + listeners.erase(found); } - - snapshot = ViewSnapshot{snapshot.query(), - snapshot.documents(), - snapshot.old_documents(), - std::move(changes), - snapshot.mutated_keys(), - snapshot.from_cache(), - snapshot.sync_state_changed(), - /*excludes_metadata_changes=*/true}; } - if (!self.raisedInitialEvent) { - if ([self shouldRaiseInitialEventForSnapshot:snapshot onlineState:self.onlineState]) { - [self raiseInitialEventForSnapshot:snapshot]; - } - } else if ([self shouldRaiseEventForSnapshot:snapshot]) { - _viewSnapshotHandler(snapshot); - } - - _snapshot = std::move(snapshot); -} - -- (void)queryDidError:(const Status &)error { - _viewSnapshotHandler(error); -} - -- (void)applyChangedOnlineState:(OnlineState)onlineState { - self.onlineState = onlineState; - if (_snapshot.has_value() && !self.raisedInitialEvent && - [self shouldRaiseInitialEventForSnapshot:_snapshot.value() onlineState:onlineState]) { - [self raiseInitialEventForSnapshot:_snapshot.value()]; + const absl::optional &view_snapshot() const { + return snapshot_; } -} - -- (BOOL)shouldRaiseInitialEventForSnapshot:(const ViewSnapshot &)snapshot - onlineState:(OnlineState)onlineState { - HARD_ASSERT(!self.raisedInitialEvent, - "Determining whether to raise initial event, but already had first event."); - // Always raise the first event when we're synced - if (!snapshot.from_cache()) { - return YES; + void set_view_snapshot(const absl::optional &snapshot) { + snapshot_ = snapshot; } - // NOTE: We consider OnlineState.Unknown as online (it should become Offline or Online if we - // wait long enough). - BOOL maybeOnline = onlineState != OnlineState::Offline; - // Don't raise the event if we're online, aren't synced yet (checked - // above) and are waiting for a sync. - if (_options.wait_for_sync_when_online() && maybeOnline) { - HARD_ASSERT(snapshot.from_cache(), "Waiting for sync, but snapshot is not from cache."); - return NO; - } + private: + // Other members are public in this struct, ensure that any reads are + // copies by requiring reads to go through a const getter. + absl::optional snapshot_; +}; - // Raise data from cache if we have any documents or we are offline - return !snapshot.documents().empty() || onlineState == OnlineState::Offline; -} - -- (BOOL)shouldRaiseEventForSnapshot:(const ViewSnapshot &)snapshot { - // We don't need to handle includeDocumentMetadataChanges here because the Metadata only changes - // have already been stripped out if needed. At this point the only changes we will see are the - // ones we should propagate. - if (!snapshot.document_changes().empty()) { - return YES; - } - - BOOL hasPendingWritesChanged = _snapshot.has_value() && _snapshot.value().has_pending_writes() != - snapshot.has_pending_writes(); - if (snapshot.sync_state_changed() || hasPendingWritesChanged) { - return _options.include_query_metadata_changes(); - } - - // Generally we should have hit one of the cases above, but it's possible to get here if there - // were only metadata docChanges and they got stripped out. - return NO; -} - -- (void)raiseInitialEventForSnapshot:(const ViewSnapshot &)snapshot { - HARD_ASSERT(!self.raisedInitialEvent, "Trying to raise initial events for second time"); - ViewSnapshot modifiedSnapshot = ViewSnapshot::FromInitialDocuments( - snapshot.query(), snapshot.documents(), snapshot.mutated_keys(), snapshot.from_cache(), - snapshot.excludes_metadata_changes()); - self.raisedInitialEvent = YES; - _viewSnapshotHandler(modifiedSnapshot); -} - -@end +} // namespace #pragma mark - FSTEventManager @@ -223,13 +84,13 @@ @interface FSTEventManager () - (instancetype)initWithSyncEngine:(FSTSyncEngine *)syncEngine NS_DESIGNATED_INITIALIZER; @property(nonatomic, strong, readonly) FSTSyncEngine *syncEngine; -@property(nonatomic, strong, readonly) - NSMutableDictionary *queries; @property(nonatomic, assign) OnlineState onlineState; @end -@implementation FSTEventManager +@implementation FSTEventManager { + objc::unordered_map _queries; +} + (instancetype)eventManagerWithSyncEngine:(FSTSyncEngine *)syncEngine { return [[FSTEventManager alloc] initWithSyncEngine:syncEngine]; @@ -238,49 +99,45 @@ + (instancetype)eventManagerWithSyncEngine:(FSTSyncEngine *)syncEngine { - (instancetype)initWithSyncEngine:(FSTSyncEngine *)syncEngine { if (self = [super init]) { _syncEngine = syncEngine; - _queries = [NSMutableDictionary dictionary]; - _syncEngine.syncEngineDelegate = self; } return self; } -- (TargetId)addListener:(FSTQueryListener *)listener { - FSTQuery *query = listener.query; - BOOL firstListen = NO; +- (TargetId)addListener:(std::shared_ptr)listener { + FSTQuery *query = listener->query(); - FSTQueryListenersInfo *queryInfo = self.queries[query]; - if (!queryInfo) { - firstListen = YES; - queryInfo = [[FSTQueryListenersInfo alloc] init]; - self.queries[query] = queryInfo; - } - [queryInfo.listeners addObject:listener]; + auto inserted = _queries.emplace(query, QueryListenersInfo{}); + bool first_listen = inserted.second; + QueryListenersInfo &query_info = inserted.first->second; - [listener applyChangedOnlineState:self.onlineState]; + query_info.listeners.push_back(listener); - if (queryInfo.viewSnapshot.has_value()) { - [listener queryDidChangeViewSnapshot:queryInfo.viewSnapshot.value()]; + listener->OnOnlineStateChanged(self.onlineState); + + if (query_info.view_snapshot().has_value()) { + listener->OnViewSnapshot(query_info.view_snapshot().value()); } - if (firstListen) { - queryInfo.targetID = [self.syncEngine listenToQuery:query]; + if (first_listen) { + query_info.target_id = [self.syncEngine listenToQuery:query]; } - return queryInfo.targetID; + return query_info.target_id; } -- (void)removeListener:(FSTQueryListener *)listener { - FSTQuery *query = listener.query; - BOOL lastListen = NO; +- (void)removeListener:(const std::shared_ptr &)listener { + FSTQuery *query = listener->query(); + bool last_listen = false; - FSTQueryListenersInfo *queryInfo = self.queries[query]; - if (queryInfo) { - [queryInfo.listeners removeObject:listener]; - lastListen = (queryInfo.listeners.count == 0); + auto found_iter = _queries.find(query); + if (found_iter != _queries.end()) { + QueryListenersInfo &query_info = found_iter->second; + query_info.Erase(listener); + last_listen = query_info.listeners.empty(); } - if (lastListen) { - [self.queries removeObjectForKey:query]; + if (last_listen) { + _queries.erase(found_iter); [self.syncEngine stopListeningToQuery:query]; } } @@ -288,33 +145,38 @@ - (void)removeListener:(FSTQueryListener *)listener { - (void)handleViewSnapshots:(std::vector &&)viewSnapshots { for (ViewSnapshot &viewSnapshot : viewSnapshots) { FSTQuery *query = viewSnapshot.query(); - FSTQueryListenersInfo *queryInfo = self.queries[query]; - if (queryInfo) { - for (FSTQueryListener *listener in queryInfo.listeners) { - [listener queryDidChangeViewSnapshot:viewSnapshot]; + auto found_iter = _queries.find(query); + if (found_iter != _queries.end()) { + QueryListenersInfo &query_info = found_iter->second; + for (const auto &listener : query_info.listeners) { + listener->OnViewSnapshot(viewSnapshot); } - [queryInfo setViewSnapshot:std::move(viewSnapshot)]; + query_info.set_view_snapshot(std::move(viewSnapshot)); } } } - (void)handleError:(NSError *)error forQuery:(FSTQuery *)query { - FSTQueryListenersInfo *queryInfo = self.queries[query]; - if (queryInfo) { - for (FSTQueryListener *listener in queryInfo.listeners) { - [listener queryDidError:MakeStatus(error)]; + auto found_iter = _queries.find(query); + if (found_iter != _queries.end()) { + QueryListenersInfo &query_info = found_iter->second; + for (const auto &listener : query_info.listeners) { + listener->OnError(MakeStatus(error)); } - } - // Remove all listeners. NOTE: We don't need to call [FSTSyncEngine stopListening] after an error. - [self.queries removeObjectForKey:query]; + // Remove all listeners. NOTE: We don't need to call [FSTSyncEngine stopListening] after an + // error. + _queries.erase(found_iter); + } } - (void)applyChangedOnlineState:(OnlineState)onlineState { self.onlineState = onlineState; - for (FSTQueryListenersInfo *info in self.queries.objectEnumerator) { - for (FSTQueryListener *listener in info.listeners) { - [listener applyChangedOnlineState:onlineState]; + + for (auto &&kv : _queries) { + QueryListenersInfo &info = kv.second; + for (auto &&listener : info.listeners) { + listener->OnOnlineStateChanged(onlineState); } } } diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index 629eb4b520d..8c7d97fdfd0 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -26,6 +26,7 @@ #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/core/listen_options.h" +#include "Firestore/core/src/firebase/firestore/core/query_listener.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" @@ -42,12 +43,12 @@ @class FSTDocument; @class FSTMutation; @class FSTQuery; -@class FSTQueryListener; @class FSTTransaction; NS_ASSUME_NONNULL_BEGIN using firebase::firestore::core::ListenOptions; +using firebase::firestore::core::QueryListener; /** * FirestoreClient is a top-level class that constructs and owns all of the pieces of the client @@ -81,13 +82,13 @@ using firebase::firestore::core::ListenOptions; - (void)enableNetworkWithCompletion:(nullable FSTVoidErrorBlock)completion; /** Starts listening to a query. */ -- (FSTQueryListener *)listenToQuery:(FSTQuery *)query - options:(ListenOptions)options - viewSnapshotHandler: - (firebase::firestore::core::ViewSnapshotHandler &&)viewSnapshotHandler; +- (std::shared_ptr)listenToQuery:(FSTQuery *)query + options:(ListenOptions)options + viewSnapshotHandler:(firebase::firestore::core::ViewSnapshotHandler &&) + viewSnapshotHandler; /** Stops listening to a query previously listened to. */ -- (void)removeListener:(FSTQueryListener *)listener; +- (void)removeListener:(const std::shared_ptr &)listener; /** * Retrieves a document from the cache via the indicated completion. If the doc diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index 4bd38fafc58..83c8993cd01 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -312,20 +312,18 @@ - (void)shutdownWithCompletion:(nullable FSTVoidErrorBlock)completion { }); } -- (FSTQueryListener *)listenToQuery:(FSTQuery *)query - options:(ListenOptions)options - viewSnapshotHandler:(ViewSnapshotHandler &&)viewSnapshotHandler { - FSTQueryListener *listener = - [[FSTQueryListener alloc] initWithQuery:query - options:std::move(options) - viewSnapshotHandler:std::move(viewSnapshotHandler)]; +- (std::shared_ptr)listenToQuery:(FSTQuery *)query + options:(ListenOptions)options + viewSnapshotHandler:(ViewSnapshotHandler &&)viewSnapshotHandler { + auto listener = + std::make_shared(query, std::move(options), std::move(viewSnapshotHandler)); _workerQueue->Enqueue([self, listener] { [self.eventManager addListener:listener]; }); return listener; } -- (void)removeListener:(FSTQueryListener *)listener { +- (void)removeListener:(const std::shared_ptr &)listener { _workerQueue->Enqueue([self, listener] { [self.eventManager removeListener:listener]; }); } diff --git a/Firestore/Source/Util/FSTAsyncQueryListener.h b/Firestore/Source/Util/FSTAsyncQueryListener.h index 82a3712df8a..8588fbe64a6 100644 --- a/Firestore/Source/Util/FSTAsyncQueryListener.h +++ b/Firestore/Source/Util/FSTAsyncQueryListener.h @@ -21,10 +21,8 @@ NS_ASSUME_NONNULL_BEGIN -@class FSTQueryListener; - /** - * A wrapper class around FSTQueryListener that dispatches events asynchronously. + * A wrapper class around QueryListener that dispatches events asynchronously. */ @interface FSTAsyncQueryListener : NSObject diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index 4ab3196f3fd..d08419f99cc 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -211,7 +211,7 @@ ListenOptions options( initWithExecutor:firestore_->client().userExecutor snapshotHandler:std::move(handler)]; - FSTQueryListener* internal_listener = [firestore_->client() + std::shared_ptr internal_listener = [firestore_->client() listenToQuery:query options:options viewSnapshotHandler:[async_listener asyncSnapshotHandler]]; diff --git a/Firestore/core/src/firebase/firestore/core/query_listener.h b/Firestore/core/src/firebase/firestore/core/query_listener.h new file mode 100644 index 00000000000..a5a925ac092 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/query_listener.h @@ -0,0 +1,116 @@ +/* + * Copyright 2019 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_CORE_QUERY_LISTENER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_QUERY_LISTENER_H_ + +#if !defined(__OBJC__) +#error "This header only supports Objective-C++" +#endif // !defined(__OBJC__) + +#import + +#include +#include + +#include "Firestore/core/src/firebase/firestore/core/listen_options.h" +#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#include "Firestore/core/src/firebase/firestore/model/types.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" +#include "absl/types/optional.h" + +@class FSTQuery; + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace core { + +/** + * QueryListener takes a series of internal view snapshots and determines when + * to raise user-facing events. + */ +class QueryListener { + public: + static std::shared_ptr Create(FSTQuery* query, + ListenOptions options, + ViewSnapshotHandler&& listener) { + return std::make_shared(query, std::move(options), + std::move(listener)); + } + + static std::shared_ptr Create(FSTQuery* query, + ViewSnapshotHandler&& listener) { + return std::make_shared( + query, ListenOptions::DefaultOptions(), std::move(listener)); + } + + QueryListener(FSTQuery* query, + ListenOptions options, + ViewSnapshotHandler&& listener) + : query_(query), + options_(std::move(options)), + listener_(std::move(listener)) { + } + virtual ~QueryListener() { + } + + FSTQuery* query() const { + return query_; + } + + /** The last received view snapshot. */ + const absl::optional& snapshot() const { + return snapshot_; + } + + virtual void OnViewSnapshot(ViewSnapshot snapshot); + virtual void OnError(util::Status error); + virtual void OnOnlineStateChanged(model::OnlineState online_state); + + private: + bool ShouldRaiseInitialEvent(const ViewSnapshot& snapshot, + model::OnlineState online_state) const; + bool ShouldRaiseEvent(const ViewSnapshot& snapshot) const; + void RaiseInitialEvent(const ViewSnapshot& snapshot); + + FSTQuery* query_ = nil; + ListenOptions options_; + + /** The ViewSnapshotHandler associated with this query listener. */ + ViewSnapshotHandler listener_; + + /** + * Initial snapshots (e.g. from cache) may not be propagated to the + * ViewSnapshotHandler. This flag is set to true once we've actually raised an + * event. + */ + bool raised_initial_event_ = false; + + /** The last online state this query listener got. */ + model::OnlineState online_state_ = model::OnlineState::Unknown; + + absl::optional snapshot_; +}; + +} // namespace core +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_QUERY_LISTENER_H_ diff --git a/Firestore/core/src/firebase/firestore/core/query_listener.mm b/Firestore/core/src/firebase/firestore/core/query_listener.mm new file mode 100644 index 00000000000..1ee080f0782 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/query_listener.mm @@ -0,0 +1,147 @@ +/* + * Copyright 2019 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 "Firestore/core/src/firebase/firestore/core/query_listener.h" + +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/document_set.h" +#include "Firestore/core/src/firebase/firestore/util/error_apple.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" +#include "absl/types/optional.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace core { + +using model::OnlineState; +using model::TargetId; +using util::MakeStatus; +using util::Status; + +void QueryListener::OnViewSnapshot(ViewSnapshot snapshot) { + HARD_ASSERT( + !snapshot.document_changes().empty() || snapshot.sync_state_changed(), + "We got a new snapshot with no changes?"); + + if (!options_.include_document_metadata_changes()) { + // Remove the metadata-only changes. + std::vector changes; + for (const DocumentViewChange& change : snapshot.document_changes()) { + if (change.type() != DocumentViewChange::Type::kMetadata) { + changes.push_back(change); + } + } + + snapshot = ViewSnapshot{snapshot.query(), + snapshot.documents(), + snapshot.old_documents(), + std::move(changes), + snapshot.mutated_keys(), + snapshot.from_cache(), + snapshot.sync_state_changed(), + /*excludes_metadata_changes=*/true}; + } + + if (!raised_initial_event_) { + if (ShouldRaiseInitialEvent(snapshot, online_state_)) { + RaiseInitialEvent(snapshot); + } + } else if (ShouldRaiseEvent(snapshot)) { + listener_(snapshot); + } + + snapshot_ = std::move(snapshot); +} + +void QueryListener::OnError(Status error) { + listener_(std::move(error)); +} + +void QueryListener::OnOnlineStateChanged(OnlineState online_state) { + online_state_ = online_state; + if (snapshot_.has_value() && !raised_initial_event_ && + ShouldRaiseInitialEvent(snapshot_.value(), online_state)) { + RaiseInitialEvent(snapshot_.value()); + } +} + +bool QueryListener::ShouldRaiseInitialEvent(const ViewSnapshot& snapshot, + OnlineState online_state) const { + HARD_ASSERT(!raised_initial_event_, "Determining whether to raise initial " + "event, but already had first event."); + + // Always raise the first event when we're synced + if (!snapshot.from_cache()) { + return true; + } + + // NOTE: We consider OnlineState::Unknown as online (it should become Offline + // or Online if we wait long enough). + bool maybe_online = online_state != OnlineState::Offline; + + // Don't raise the event if we're online, aren't synced yet (checked + // above) and are waiting for a sync. + if (options_.wait_for_sync_when_online() && maybe_online) { + HARD_ASSERT(snapshot.from_cache(), + "Waiting for sync, but snapshot is not from cache."); + return false; + } + + // Raise data from cache if we have any documents or we are offline + return !snapshot.documents().empty() || online_state == OnlineState::Offline; +} + +bool QueryListener::ShouldRaiseEvent(const ViewSnapshot& snapshot) const { + // We don't need to handle include_document_metadata_changes() here because + // the Metadata only changes have already been stripped out if needed. At this + // point the only changes we will see are the ones we should propagate. + if (!snapshot.document_changes().empty()) { + return true; + } + + bool has_pending_writes_changed = + snapshot_.has_value() && + snapshot_.value().has_pending_writes() != snapshot.has_pending_writes(); + if (snapshot.sync_state_changed() || has_pending_writes_changed) { + return options_.include_query_metadata_changes(); + } + + // Generally we should have hit one of the cases above, but it's possible to + // get here if there were only metadata docChanges and they got stripped out. + return false; +} + +void QueryListener::RaiseInitialEvent(const ViewSnapshot& snapshot) { + HARD_ASSERT(!raised_initial_event_, + "Trying to raise initial events for second time"); + + ViewSnapshot modified_snapshot = ViewSnapshot::FromInitialDocuments( + snapshot.query(), snapshot.documents(), snapshot.mutated_keys(), + snapshot.from_cache(), snapshot.excludes_metadata_changes()); + raised_initial_event_ = true; + listener_(modified_snapshot); +} + +} // namespace core +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END diff --git a/Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt b/Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt index 207863ef9c6..4791ad180e0 100644 --- a/Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt @@ -20,9 +20,9 @@ cc_library( llrb_node.h llrb_node_iterator.h map_entry.h + sorted_container.h + sorted_container.cc sorted_map.h - sorted_map_base.h - sorted_map_base.cc sorted_map_iterator.h sorted_set.h tree_sorted_map.h diff --git a/Firestore/core/src/firebase/firestore/local/memory_query_cache.h b/Firestore/core/src/firebase/firestore/local/memory_query_cache.h index ac34fca078e..747756a6c9f 100644 --- a/Firestore/core/src/firebase/firestore/local/memory_query_cache.h +++ b/Firestore/core/src/firebase/firestore/local/memory_query_cache.h @@ -106,11 +106,7 @@ class MemoryQueryCache : public QueryCache { model::SnapshotVersion last_remote_snapshot_version_; /** Maps a query to the data about that query. */ - std::unordered_map, - util::objc::EqualTo> - queries_; + util::objc::unordered_map queries_; /** * A ordered bidirectional mapping between documents and the remote target diff --git a/Firestore/core/src/firebase/firestore/util/objc_compatibility.h b/Firestore/core/src/firebase/firestore/util/objc_compatibility.h index 74a4d44f89a..5745d77664a 100644 --- a/Firestore/core/src/firebase/firestore/util/objc_compatibility.h +++ b/Firestore/core/src/firebase/firestore/util/objc_compatibility.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "Firestore/core/src/firebase/firestore/util/to_string.h" @@ -101,6 +102,15 @@ class Hash { } }; +/** + * The equivalent of std::unordered_map, where the Key type is an Objective-C + * class. + */ +template ::value>> +using unordered_map = std::unordered_map, EqualTo>; + /** * Creates a debug description of the given `value` by calling `ToString` on it, * converting the result to an `NSString`. Exists mainly to simplify writing diff --git a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h index 96336684c38..381707f3753 100644 --- a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h +++ b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h @@ -60,7 +60,7 @@ class XcTestRecorder { recordFailureWithDescription:[NSString stringWithUTF8String:message] inFile:[NSString stringWithUTF8String:file_] atLine:line_ - expected:YES]; + expected:true]; } private: @@ -191,7 +191,6 @@ OBJC_PRINT_TO(FSTObjectValue); OBJC_PRINT_TO(FSTPatchMutation); OBJC_PRINT_TO(FSTQuery); OBJC_PRINT_TO(FSTQueryData); -OBJC_PRINT_TO(FSTQueryListener); OBJC_PRINT_TO(FSTReferenceValue); OBJC_PRINT_TO(FSTRelationFilter); OBJC_PRINT_TO(FSTSerializerBeta); diff --git a/scripts/cpplint.py b/scripts/cpplint.py index 4431fa5cb9f..26d8f7f983c 100644 --- a/scripts/cpplint.py +++ b/scripts/cpplint.py @@ -211,6 +211,7 @@ 'readability/namespace', 'readability/nolint', 'readability/nul', + 'readability/objc', 'readability/strings', 'readability/todo', 'readability/utf8', @@ -4623,6 +4624,30 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): include_state.SetLastHeader(canonical_include) +_BANNED_OBJC_WORDS = (r'\bYES\b', r'\bNO\b', r'\bBOOL\b') +_BANNED_OBJC_REPLACEMENTS = { + 'YES': 'true', + 'NO': 'false', + 'BOOL': 'bool', +} + +_BANNED_OBJC_WORDS_RE = re.compile('(' + '|'.join(_BANNED_OBJC_WORDS) + ')') + + +def CheckObjcConversion(filename, lines, error): + if 'Firestore/core/' not in filename: + return + + for linenum, line in enumerate(lines): + match = _BANNED_OBJC_WORDS_RE.search(line) + if match: + word = match.group(1) + replacement = _BANNED_OBJC_REPLACEMENTS[word] + error(filename, linenum, 'readability/objc', 4, + 'Migrate %s to %s' % + (word, replacement)) + + def _GetTextInside(text, start_pattern): r"""Retrieves all the text between matching open and close parentheses. @@ -6002,6 +6027,8 @@ def ProcessFileData(filename, file_extension, lines, error, ResetNolintSuppressions() + CheckObjcConversion(filename, lines, error) + CheckForCopyright(filename, lines, error) ProcessGlobalSuppresions(lines) RemoveMultiLineComments(filename, lines, error) From 92747db846f95a6dfd5c9c2e65a319c95359d408 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 26 Mar 2019 08:24:07 -0700 Subject: [PATCH 089/214] Remove default.profraw files (#2638) --- .gitignore | 3 +++ Example/default.profraw | Bin 2056 -> 0 bytes default.profraw | Bin 2056 -> 0 bytes 3 files changed, 3 insertions(+) delete mode 100644 Example/default.profraw delete mode 100644 default.profraw diff --git a/.gitignore b/.gitignore index 1281d8072ea..b9e91a0383b 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,6 @@ Ninja # CocoaPods generate gen/ + +# b/111916494 +default.profraw diff --git a/Example/default.profraw b/Example/default.profraw deleted file mode 100644 index 5caaf10542c59713f173beac6e81226aca03c097..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2056 zcmZoHO3N=Q$obF000E*8oX!mff-#9il}-S~Az_DR1x zk}!1wA`o>DRSXQAP=q5h`* zjH9VG56w;_#Vm6_awXUSs$M`G)jhEA{3Di<*2a$+tj8g^`es-GZ*YCb#E0T&kR*-^>w ze6=ZE<7&lkk?Sv@>K$aTt6%^3sMy&huf%KGrZ#wQz1yh}22qzFi>jUvYQC42g7o75 zhh1R)od8v@po*#<=3k!eb`M;Ry`O&d?Z1}|^TRb~K-D*(sfUH*0(VV!%gNxw+RQ&>VRP`|P1qJS2^!-%eyf+~FQPVT0#s^UK z3>v8Fg`p0R@jZOd;Hr6`1e9M94pDbO8&y5bzuhyMx>}!io`$J!fU5t1L%oDc<>sx9 zlRv=JcR3h=VCpA8)f?bY-{SBx=7H%PGno1rQ1t;g)WgDklpYO% z;TQr;M0#o&^BKm9TT}Z^`!O5xxK5Q{B_JwYwpdT2yK4pWYmFt-XJ$;Y*mQT|kLqPh zSB6BCKCiC%v*GSJqo=FnnasA`-mAPcEc$#?AG>9%c*c5}(;rry5Z|!SX#0*6cFqUW zcel@zW?z#m4Z>^B+wSU#I$gtw*duF@*j_mvU*M8buT>tx> zao~&?9b?}e-f^~ecb~79KO@e3SLv|^XER9eef8IltlY#p{89nDY*?=r<6Kf Ww0c`zn(7N|)`rijW4Lp1o*DolLLuP* diff --git a/default.profraw b/default.profraw deleted file mode 100644 index d102825dd2dd51aeb4258726ffe48cd40a5c1bae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2056 zcmZoHO3N=Q$obF000E*%Jl2)hbS5qF(?cH`F>*(d$( zNW#T7==IGd;@eF&yr1FHT24)r(f zXB+Qt_fm z)4TR*MzM36EXjbXH$YPl3(t)lR;We{}QG$1;;+F3kKL zQ1uVWu$zCuPU=p5ll9&}70roDJh&JRK-DiOM^z6qUr^xgMc+>a&U*u*A2mI5YJ31y z&rpG?UKr{C8Q;SP4X&C8NRTLM#yl{cV+K<{1FAj%hk97JkJ6(d zFdRdGiAYZ^V?M)JacgSdX+LH|9@nYzs{}-)%NFZtba$;_eyy=&`pk?;7Mt!){87DZ z>B^9Z(&yDRe>U7bXY_QHJd@eB+k2IlhDD!m>SMQT70*~NbNa)o6XF{d8g1Wk!p`|% z`tJ65((KE!EYBtVh}h;Ky5|nJfMMgS@2wTGz4os<78zC?ea~!{-;sTP|JqNRi|c>C zGY*^)qhsv5!#mE_?(XyT@@K@E?<&1joZ59DWd7VIvkx9)k&y%Qb Xi&k%|OH+M;&D!u;bqsed&Qk*bVzxNx From f28e76802de2aefc7bb0e10bcb4f181d6bb97641 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 26 Mar 2019 08:29:17 -0700 Subject: [PATCH 090/214] Port FSTListenerRegistration to C++ (#2619) * Replace FSTQueryListener with C++ QueryListener * objc::unordered_map * Add a lint check for Objective-C banned words --- Firestore/Source/API/FIRDocumentReference.mm | 5 +- .../API/FIRListenerRegistration+Internal.h | 13 +-- .../Source/API/FIRListenerRegistration.mm | 32 ++---- Firestore/Source/API/FIRQuery.mm | 7 +- .../firestore/api/document_reference.h | 4 +- .../firestore/api/document_reference.mm | 15 +-- .../firestore/api/listener_registration.h | 99 +++++++++++++++++++ .../firestore/api/listener_registration.mm | 41 ++++++++ 8 files changed, 168 insertions(+), 48 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/api/listener_registration.h create mode 100644 Firestore/core/src/firebase/firestore/api/listener_registration.mm diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index 3459d936dc2..7d5d1e3e054 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -212,8 +212,9 @@ - (void)getDocumentWithSource:(FIRFirestoreSource)source - (id)addSnapshotListenerInternalWithOptions:(ListenOptions)internalOptions listener:(FIRDocumentSnapshotBlock) listener { - return _documentReference.AddSnapshotListener([self wrapDocumentSnapshotBlock:listener], - std::move(internalOptions)); + ListenerRegistration result = _documentReference.AddSnapshotListener( + [self wrapDocumentSnapshotBlock:listener], std::move(internalOptions)); + return [[FSTListenerRegistration alloc] initWithRegistration:std::move(result)]; } - (StatusOrCallback)wrapDocumentSnapshotBlock:(FIRDocumentSnapshotBlock)block { diff --git a/Firestore/Source/API/FIRListenerRegistration+Internal.h b/Firestore/Source/API/FIRListenerRegistration+Internal.h index 8ba20261782..e3f1cd59247 100644 --- a/Firestore/Source/API/FIRListenerRegistration+Internal.h +++ b/Firestore/Source/API/FIRListenerRegistration+Internal.h @@ -16,23 +16,16 @@ #import "FIRListenerRegistration.h" -#include - -#include "Firestore/core/src/firebase/firestore/core/query_listener.h" - -@class FSTAsyncQueryListener; -@class FSTFirestoreClient; +#include "Firestore/core/src/firebase/firestore/api/listener_registration.h" NS_ASSUME_NONNULL_BEGIN -using firebase::firestore::core::QueryListener; +using firebase::firestore::api::ListenerRegistration; /** Private implementation of the FIRListenerRegistration protocol. */ @interface FSTListenerRegistration : NSObject -- (instancetype)initWithClient:(FSTFirestoreClient *)client - asyncListener:(FSTAsyncQueryListener *)asyncListener - internalListener:(std::shared_ptr)internalListener; +- (instancetype)initWithRegistration:(ListenerRegistration &&)registration; @end diff --git a/Firestore/Source/API/FIRListenerRegistration.mm b/Firestore/Source/API/FIRListenerRegistration.mm index 96ccddf9345..b69b9fbdb95 100644 --- a/Firestore/Source/API/FIRListenerRegistration.mm +++ b/Firestore/Source/API/FIRListenerRegistration.mm @@ -18,43 +18,25 @@ #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" -#import "Firestore/Source/Core/FSTFirestoreClient.h" -#import "Firestore/Source/Util/FSTAsyncQueryListener.h" +#include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h" NS_ASSUME_NONNULL_BEGIN -@implementation FSTListenerRegistration { - /** The client that was used to register this listen. */ - FSTFirestoreClient *_client; - - /** The async listener that is used to mute events synchronously. */ - FSTAsyncQueryListener *_asyncListener; +using firebase::firestore::util::DelayedConstructor; - /** The internal QueryListener that can be used to unlisten the query. */ - std::weak_ptr _internalListener; +@implementation FSTListenerRegistration { + DelayedConstructor _registration; } -- (instancetype)initWithClient:(FSTFirestoreClient *)client - asyncListener:(FSTAsyncQueryListener *)asyncListener - internalListener:(std::shared_ptr)internalListener { +- (instancetype)initWithRegistration:(ListenerRegistration &&)registration { if (self = [super init]) { - _client = client; - _asyncListener = asyncListener; - _internalListener = internalListener; + _registration.Init(std::move(registration)); } return self; } - (void)remove { - [_asyncListener mute]; - - std::shared_ptr listener = _internalListener.lock(); - if (listener) { - [_client removeListener:listener]; - listener.reset(); - _internalListener.reset(); - } - _asyncListener = nil; + _registration->Remove(); } @end diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 40c3429e9a8..2b39e45ae06 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -199,9 +199,10 @@ ListenOptions listenOptions( [firestore->client() listenToQuery:query options:internalOptions viewSnapshotHandler:[asyncListener asyncSnapshotHandler]]; - return [[FSTListenerRegistration alloc] initWithClient:self.firestore.client - asyncListener:asyncListener - internalListener:internalListener]; + + return [[FSTListenerRegistration alloc] + initWithRegistration:ListenerRegistration(firestore->client(), asyncListener, + std::move(internalListener))]; } - (FIRQuery *)queryWhereField:(NSString *)field isEqualTo:(id)value { diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.h b/Firestore/core/src/firebase/firestore/api/document_reference.h index 49d74cd5262..491c31fbb80 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.h +++ b/Firestore/core/src/firebase/firestore/api/document_reference.h @@ -29,9 +29,9 @@ #import "FIRDocumentReference.h" #import "FIRFirestoreSource.h" -#import "FIRListenerRegistration.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" +#include "Firestore/core/src/firebase/firestore/api/listener_registration.h" #include "Firestore/core/src/firebase/firestore/core/listen_options.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" @@ -89,7 +89,7 @@ class DocumentReference { void GetDocument(FIRFirestoreSource source, util::StatusOrCallback&& completion); - id AddSnapshotListener( + ListenerRegistration AddSnapshotListener( util::StatusOrCallback&& listener, core::ListenOptions options); diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index d08419f99cc..1925ebfcd7e 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -127,12 +127,16 @@ ListenOptions options( /*include_document_metadata_changes=*/true, /*wait_for_sync_when_online=*/true); + // Create an empty ListenerRegistration captured in the closure below and + // then, once the listen has started, assign to it and signal the listener + // that we succeeded. + // // TODO(varconst): replace with a synchronization primitive that doesn't // require libdispatch. See // https://github.com/firebase/firebase-ios-sdk/blob/3ccbdcdc65c93c4621c045c3c6d15de9dcefa23f/Firestore/Source/Core/FSTFirestoreClient.mm#L161 // for an example. dispatch_semaphore_t registered = dispatch_semaphore_create(0); - auto listener_registration = std::make_shared>(); + auto listener_registration = std::make_shared(); StatusOrCallback listener = [listener_registration, registered, completion, source](StatusOr maybe_snapshot) { @@ -146,7 +150,7 @@ ListenOptions options( // Remove query first before passing event to user to avoid user actions // affecting the now stale query. dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER); - [*listener_registration remove]; + listener_registration->Remove(); if (!snapshot.exists() && snapshot.metadata().from_cache()) { // TODO(dimond): Reconsider how to raise missing documents when @@ -178,7 +182,7 @@ ListenOptions options( dispatch_semaphore_signal(registered); } -id DocumentReference::AddSnapshotListener( +ListenerRegistration DocumentReference::AddSnapshotListener( StatusOrCallback&& listener, ListenOptions options) { Firestore* firestore = firestore_; FSTQuery* query = [FSTQuery queryWithPath:key_.path()]; @@ -215,9 +219,8 @@ ListenOptions options( listenToQuery:query options:options viewSnapshotHandler:[async_listener asyncSnapshotHandler]]; - return [[FSTListenerRegistration alloc] initWithClient:firestore_->client() - asyncListener:async_listener - internalListener:internal_listener]; + return ListenerRegistration(firestore_->client(), async_listener, + internal_listener); } bool operator==(const DocumentReference& lhs, const DocumentReference& rhs) { diff --git a/Firestore/core/src/firebase/firestore/api/listener_registration.h b/Firestore/core/src/firebase/firestore/api/listener_registration.h new file mode 100644 index 00000000000..bb66a9266e4 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/listener_registration.h @@ -0,0 +1,99 @@ +/* + * Copyright 2019 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_API_LISTENER_REGISTRATION_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_LISTENER_REGISTRATION_H_ + +#if !defined(__OBJC__) +#error "This header only supports Objective-C++" +#endif // !defined(__OBJC__) + +#import + +#include +#include + +#include "Firestore/core/src/firebase/firestore/core/query_listener.h" + +@class FSTAsyncQueryListener; +@class FSTFirestoreClient; + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace api { + +/** + * An internal handle that encapsulates a user's ability to request that we + * stop listening to a query. When a user calls Remove(), ListenerRegistration + * will synchronously mute the listener and then send a request to the + * FirestoreClient to actually unlisten. + * + * ListenerRegistration will not automaticlaly stop listening if it is + * destroyed. We allow users to fire and forget listens if they never want to + * stop them. + * + * Getting shutdown code right is tricky so ListenerRegistration is very + * forgiving. It will tolerate: + * + * * Multiple calls to Remove(), + * * calls to Remove() after we send an error, + * * calls to Remove() even after deleting the App in which the listener was + * started. + * + * ListenerRegistration is default constructible to facilitate the pattern in + * DocumentReference::GetDocument, where the closure that implements a listener + * needs to be able to use the ListenerRegistration thats returned from + * starting the listener. The default ListenerRegistration acts as a shared + * placeholder that's filled in later once the listener is started. + */ +class ListenerRegistration { + public: + ListenerRegistration() = default; + + ListenerRegistration(FSTFirestoreClient* client, + FSTAsyncQueryListener* async_listener, + std::shared_ptr internal_listener) + : client_(client), + async_listener_(async_listener), + internal_listener_(std::move(internal_listener)) { + } + + /** + * Removes the listener being tracked by this FIRListenerRegistration. After + * the initial call, subsequent calls have no effect. + */ + void Remove(); + + private: + /** The client that was used to register this listen. */ + FSTFirestoreClient* client_ = nil; + + /** The async listener that is used to mute events synchronously. */ + FSTAsyncQueryListener* async_listener_ = nil; + + /** The internal QueryListener that can be used to unlisten the query. */ + std::weak_ptr internal_listener_; +}; + +} // namespace api +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_LISTENER_REGISTRATION_H_ diff --git a/Firestore/core/src/firebase/firestore/api/listener_registration.mm b/Firestore/core/src/firebase/firestore/api/listener_registration.mm new file mode 100644 index 00000000000..4c9ac859ead --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/listener_registration.mm @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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/api/listener_registration.h" + +#import "Firestore/Source/Core/FSTFirestoreClient.h" +#import "Firestore/Source/Util/FSTAsyncQueryListener.h" + +namespace firebase { +namespace firestore { +namespace api { + +void ListenerRegistration::Remove() { + [async_listener_ mute]; + async_listener_ = nil; + + std::shared_ptr listener = internal_listener_.lock(); + if (listener) { + [client_ removeListener:listener]; + listener.reset(); + internal_listener_.reset(); + client_ = nil; + } +} + +} // namespace api +} // namespace firestore +} // namespace firebase From bddf94e48beeea515b4f7169a7c1633c114c82a8 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 26 Mar 2019 10:18:02 -0700 Subject: [PATCH 091/214] fix race condition that checkin can be deleted right after fetched (#2438) (#2631) --- .../Tests/FIRInstanceIDCheckinStoreTest.m | 62 ++++++++++++++++++- Firebase/InstanceID/FIRIMessageCode.h | 2 + .../InstanceID/FIRInstanceIDCheckinService.m | 5 +- .../InstanceID/FIRInstanceIDCheckinStore.m | 40 ++++++------ .../InstanceID/FIRInstanceIDKeyPairStore.m | 6 +- Firebase/InstanceID/FIRInstanceIDKeychain.m | 7 +-- Firebase/InstanceID/FIRInstanceIDStore.m | 10 +-- .../Sources/ZipBuilder/FrameworkBuilder.swift | 2 +- 8 files changed, 99 insertions(+), 35 deletions(-) diff --git a/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m b/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m index 28a4dc9a291..9aea10043c0 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m @@ -45,6 +45,8 @@ - (NSString *)bundleIdentifierForKeychainAccount; static NSString *const kScope = @"test-scope"; static NSString *const kSecret = @"test-secret"; static NSString *const kToken = @"test-token"; +static NSString *const kFakeErrorDomain = @"fakeDomain"; +static const NSUInteger kFakeErrorCode = -1; static int64_t const kLastCheckinTimestamp = 123456; @@ -111,7 +113,6 @@ - (void)testCheckinSaveFailsOnKeychainWriteFailure { FIRInstanceIDBackupExcludedPlist *checkinPlist = [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName subDirectory:kSubDirectoryName]; - FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; fakeKeychain.cannotWriteToKeychain = YES; @@ -134,6 +135,65 @@ - (void)testCheckinSaveFailsOnKeychainWriteFailure { [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; } +- (void)testCheckinSaveFailsOnPlistWriteFailure { + XCTestExpectation *checkinSaveFailsExpectation = + [self expectationWithDescription:@"Checkin save should fail after plist write failure"]; + FIRInstanceIDBackupExcludedPlist *checkinPlist = + [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName + subDirectory:kSubDirectoryName]; + id plistMock = OCMPartialMock(checkinPlist); + NSError *error = [NSError errorWithDomain:kFakeErrorDomain code:kFakeErrorCode userInfo:nil]; + OCMStub([plistMock writeDictionary:[OCMArg any] error:[OCMArg setTo:error]]).andReturn(NO); + + FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; + + FIRInstanceIDCheckinStore *checkinStore = + [[FIRInstanceIDCheckinStore alloc] initWithCheckinPlist:plistMock keychain:fakeKeychain]; + + __block FIRInstanceIDCheckinPreferences *preferences = + [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:kAuthID secretToken:kSecret]; + [preferences updateWithCheckinPlistContents:[[self class] newCheckinPlistPreferences]]; + [checkinStore saveCheckinPreferences:preferences + handler:^(NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqual(error.code, kFakeErrorCode); + + preferences = [checkinStore cachedCheckinPreferences]; + XCTAssertNil(preferences.deviceID); + XCTAssertNil(preferences.secretToken); + XCTAssertFalse([preferences hasValidCheckinInfo]); + [checkinSaveFailsExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; +} + +- (void)testCheckinSaveSuccess { + XCTestExpectation *checkinSaveSuccessExpectation = + [self expectationWithDescription:@"Checkin save should succeed"]; + FIRInstanceIDBackupExcludedPlist *checkinPlist = + [[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName + subDirectory:kSubDirectoryName]; + id plistMock = OCMPartialMock(checkinPlist); + + FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init]; + FIRInstanceIDCheckinStore *checkinStore = + [[FIRInstanceIDCheckinStore alloc] initWithCheckinPlist:plistMock keychain:fakeKeychain]; + + __block FIRInstanceIDCheckinPreferences *preferences = + [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:kAuthID secretToken:kSecret]; + [preferences updateWithCheckinPlistContents:[[self class] newCheckinPlistPreferences]]; + [checkinStore saveCheckinPreferences:preferences + handler:^(NSError *error) { + XCTAssertNil(error); + + preferences = [checkinStore cachedCheckinPreferences]; + XCTAssertEqualObjects(preferences.deviceID, kAuthID); + XCTAssertEqualObjects(preferences.secretToken, kSecret); + [checkinSaveSuccessExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; +} + // Write fake checkin data to legacy location, then test if migration worked. - (void)testCheckinMigrationMovesToNewLocationInKeychain { XCTestExpectation *checkinMigrationExpectation = diff --git a/Firebase/InstanceID/FIRIMessageCode.h b/Firebase/InstanceID/FIRIMessageCode.h index db653d7cc48..579b8b2c56f 100644 --- a/Firebase/InstanceID/FIRIMessageCode.h +++ b/Firebase/InstanceID/FIRIMessageCode.h @@ -77,6 +77,8 @@ typedef NS_ENUM(NSInteger, FIRInstanceIDMessageCode) { kFIRInstanceIDMessageCodeKeyPair002 = 9002, kFIRInstanceIDMessageCodeKeyPairMigrationError = 9004, kFIRInstanceIDMessageCodeKeyPairMigrationSuccess = 9005, + kFIRInstanceIDMessageCodeKeyPairNoLegacyKeyPair = 9006, + // FIRInstanceIDKeyPairStore.m kFIRInstanceIDMessageCodeKeyPairStore000 = 10000, kFIRInstanceIDMessageCodeKeyPairStore001 = 10001, diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinService.m b/Firebase/InstanceID/FIRInstanceIDCheckinService.m index 8a2711d6231..7392a9dd5d4 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinService.m +++ b/Firebase/InstanceID/FIRInstanceIDCheckinService.m @@ -133,8 +133,9 @@ - (void)checkinWithExistingCheckin:(FIRInstanceIDCheckinPreferences *)existingCh // Somehow the server clock gets out of sync with the device clock. // Reset the last checkin timestamp in case this happens. if (lastCheckinTimestampMillis > currentTimestampMillis) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService002, - @"Invalid last checkin timestamp in future."); + FIRInstanceIDLoggerDebug( + kFIRInstanceIDMessageCodeService002, @"Invalid last checkin timestamp %@ in future.", + [NSDate dateWithTimeIntervalSince1970:lastCheckinTimestampMillis / 1000.0]); lastCheckinTimestampMillis = currentTimestampMillis; } diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinStore.m b/Firebase/InstanceID/FIRInstanceIDCheckinStore.m index 96f80712625..fe97440a87e 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinStore.m +++ b/Firebase/InstanceID/FIRInstanceIDCheckinStore.m @@ -108,8 +108,21 @@ - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences return; } + // Save all other checkin preferences in a plist + NSError *error; + if (![self.plist writeDictionary:checkinPlistContents error:&error]) { + FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore003, + @"Failed to save checkin plist contents." + @"Will delete auth credentials"); + [self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService + account:self.bundleIdentifierForKeychainAccount + handler:nil]; + if (handler) { + handler(error); + } + return; + } // Save the deviceID and secret in the Keychain - __block BOOL shouldContinue = YES; if (!preferences.hasPreCachedAuthCredentials) { NSData *data = [checkinKeychainContent dataUsingEncoding:NSUTF8StringEncoding]; [self.keychain setData:data @@ -121,30 +134,15 @@ - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences if (handler) { handler(error); } - shouldContinue = NO; return; } + if (handler) { + handler(nil); + } }]; + } else { + handler(nil); } - if (!shouldContinue) { - return; - } - - // Save all other checkin preferences in a plist - NSError *error; - if (![self.plist writeDictionary:checkinPlistContents error:&error]) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore003, - @"Failed to save checkin plist contents." - @"Will delete auth credentials"); - [self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService - account:self.bundleIdentifierForKeychainAccount - handler:nil]; - if (handler) { - handler(error); - } - return; - } - handler(nil); } - (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *error))handler { diff --git a/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m b/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m index 7b715a7c556..20b5cea6751 100644 --- a/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m +++ b/Firebase/InstanceID/FIRInstanceIDKeyPairStore.m @@ -159,7 +159,7 @@ - (BOOL)hasCachedKeyPairs { if ([self cachedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType error:&error] == nil) { if (error) { FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPairStore000, - @"Failed to get cached keyPair %@", error); + @"Failed to get the cached keyPair %@", error); } error = nil; [self removeKeyPairCreationTimePlistWithError:&error]; @@ -316,7 +316,7 @@ + (FIRInstanceIDKeyPair *)keyPairForPrivateKeyTag:(NSString *)privateKeyTag *error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeMissingKeyPair]; } FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPair000, - @"No keypair info is retrieved with tag %@", privateKeyTag); + @"No keypair info is found with tag %@", privateKeyTag); return nil; } @@ -344,6 +344,8 @@ - (void)migrateKeyPairCacheIfNeededWithHandler:(void (^)(NSError *error))handler publicKeyTag:legacyPublicKeyTag error:&error]; if (![keyPair isValid]) { + FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPairNoLegacyKeyPair, + @"There's no legacy keypair so no need to do migration."); if (handler) { handler(nil); } diff --git a/Firebase/InstanceID/FIRInstanceIDKeychain.m b/Firebase/InstanceID/FIRInstanceIDKeychain.m index c5f1606508d..81c73727e44 100644 --- a/Firebase/InstanceID/FIRInstanceIDKeychain.m +++ b/Firebase/InstanceID/FIRInstanceIDKeychain.m @@ -60,10 +60,9 @@ - (CFTypeRef)itemWithQuery:(NSDictionary *)keychainQuery { if (keyRef) { CFRelease(keyRef); } - FIRInstanceIDLoggerDebug( - kFIRInstanceIDKeychainReadItemError, - @"No info is retrieved from Keychain OSStatus: %d with the keychain query %@", - (int)status, keychainQuery); + FIRInstanceIDLoggerDebug(kFIRInstanceIDKeychainReadItemError, + @"Info is not found in Keychain. OSStatus: %d. Keychain query: %@", + (int)status, keychainQuery); } }); return keyRef; diff --git a/Firebase/InstanceID/FIRInstanceIDStore.m b/Firebase/InstanceID/FIRInstanceIDStore.m index 7467cdac2d3..1c7a0d0ac71 100644 --- a/Firebase/InstanceID/FIRInstanceIDStore.m +++ b/Firebase/InstanceID/FIRInstanceIDStore.m @@ -153,11 +153,13 @@ - (void)resetCredentialsIfNeeded { if (oldCheckinPreferences) { [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) { if (!error) { - FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore002, - @"Removed cached checkin preferences from Keychain."); + FIRInstanceIDLoggerDebug( + kFIRInstanceIDMessageCodeStore002, + @"Removed cached checkin preferences from Keychain because this is a fresh install."); } else { - FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore003, - @"Couldn't remove cached checkin preferences. Error: %@", error); + FIRInstanceIDLoggerError( + kFIRInstanceIDMessageCodeStore003, + @"Couldn't remove cached checkin preferences for a fresh install. Error: %@", error); } if (oldCheckinPreferences.deviceID.length && oldCheckinPreferences.secretToken.length) { FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore006, diff --git a/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift b/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift index c57349522e5..dc42bde77ef 100755 --- a/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift +++ b/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift @@ -101,7 +101,7 @@ struct FrameworkBuilder { // } // // // TODO: Figure out if we need the MD5 at all. - let md5 = podName + let md5 = podName // let md5 = Shell.calculateMD5(for: podInfo.installedLocation) // Get (or create) the cache directory for storing built frameworks. From aef87815bbe757e309b722dca6ca1ecfa27f1a03 Mon Sep 17 00:00:00 2001 From: Corrob Date: Tue, 26 Mar 2019 10:18:45 -0700 Subject: [PATCH 092/214] Create a swift utility for uploading project health metrics from Travis (#2612) * Create a swift utility for collecting code coverage and uploading to a database * Update the readme * Code clean up * Fix line length * Change to a still supported commandline kit * Run swift formatter * Add copyright * Move example report under the Tests dir * Add a TODO for Firestore, Functions, and InAppMessaging * Change todo name to GitHub name * Put line breaks in the example report --- Metrics/.gitignore | 4 + Metrics/Package.resolved | 16 + Metrics/Package.swift | 39 + Metrics/README.md | 20 + Metrics/Sources/Metrics/main.swift | 59 + .../Sources/MetricsLib/CoverageReport.swift | 62 + .../Sources/MetricsLib/UploadMetrics.swift | 76 + .../MetricsTests/CoverageReportTests.swift | 39 + .../MetricsTests/UploadMetricsTests.swift | 52 + .../Tests/MetricsTests/example_report.json | 41997 ++++++++++++++++ 10 files changed, 42364 insertions(+) create mode 100644 Metrics/.gitignore create mode 100644 Metrics/Package.resolved create mode 100644 Metrics/Package.swift create mode 100644 Metrics/README.md create mode 100644 Metrics/Sources/Metrics/main.swift create mode 100644 Metrics/Sources/MetricsLib/CoverageReport.swift create mode 100644 Metrics/Sources/MetricsLib/UploadMetrics.swift create mode 100644 Metrics/Tests/MetricsTests/CoverageReportTests.swift create mode 100644 Metrics/Tests/MetricsTests/UploadMetricsTests.swift create mode 100644 Metrics/Tests/MetricsTests/example_report.json diff --git a/Metrics/.gitignore b/Metrics/.gitignore new file mode 100644 index 00000000000..02c087533d1 --- /dev/null +++ b/Metrics/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj diff --git a/Metrics/Package.resolved b/Metrics/Package.resolved new file mode 100644 index 00000000000..0e9231894bf --- /dev/null +++ b/Metrics/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "CommandLineKit", + "repositoryURL": "https://github.com/objecthub/swift-commandlinekit", + "state": { + "branch": null, + "revision": "b6c7786a8fbbe32a819474d3a85f8d93db63052c", + "version": "0.2.5" + } + } + ] + }, + "version": 1 +} diff --git a/Metrics/Package.swift b/Metrics/Package.swift new file mode 100644 index 00000000000..bbcd417c841 --- /dev/null +++ b/Metrics/Package.swift @@ -0,0 +1,39 @@ +// swift-tools-version:4.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. +/* + * Copyright 2019 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 PackageDescription + +let package = Package( + name: "Metrics", + dependencies: [ + .package(url: "https://github.com/objecthub/swift-commandlinekit", from: "0.2.5"), + ], + targets: [ + .target( + name: "MetricsLib" + ), + .target( + name: "Metrics", + dependencies: ["CommandLineKit", "MetricsLib"] + ), + .testTarget( + name: "MetricsTests", + dependencies: ["MetricsLib"] + ), + ] +) diff --git a/Metrics/README.md b/Metrics/README.md new file mode 100644 index 00000000000..84949682e86 --- /dev/null +++ b/Metrics/README.md @@ -0,0 +1,20 @@ +# Metrics + +A swift utility for collecting project health metrics on Travis and uploading to a database. It +currently only supports parsing a Code Coverage report generated from XCov. + +## Run the coverage parser + +``` +swift build +.build/debug/Metrics -c=example_report.json -o=database.json -p=99 +``` + +This generates a database.json file that can be executed through a pre-existing Java uploader that +will push the data to a Cloud SQL database. + +## Run the unit tests + +``` +swift test +``` diff --git a/Metrics/Sources/Metrics/main.swift b/Metrics/Sources/Metrics/main.swift new file mode 100644 index 00000000000..194d2a46a3d --- /dev/null +++ b/Metrics/Sources/Metrics/main.swift @@ -0,0 +1,59 @@ +/* + * Copyright 2019 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 CommandLineKit +import Foundation +import MetricsLib + +var flags = Flags() +let coveragePath = flags.string("c", + "coverage", + description: "Required - The path of the JSON coverage report generated by XCov.") +let outputPath = flags.string("o", + "output", + description: "Required - The path to write the database JSON info.") +let pullRequest = flags.int("p", + "pull_request", + description: "Required - The number of the pull request that corresponds to this coverage run.") +do { + try flags.parse() + if !coveragePath.wasSet { + print("Please specify the path of the JSON coverage report from XCov. -c or --coverage") + exit(1) + } + if !outputPath.wasSet { + print("Please specify output location for the database JSON file. -o or --output") + exit(1) + } + if !pullRequest.wasSet { + print("Please specify the corresponding pull request number. -p or --pull_request") + exit(1) + } + let pullRequestTable = TableUpdate(table_name: "PullRequests", + column_names: ["pull_request_id"], + replace_measurements: [[Double(pullRequest.value!)]]) + let coverageReport = try CoverageReport.load(path: coveragePath.value!) + let coverageTable = TableUpdate.createFrom(coverage: coverageReport, + pullRequest: pullRequest.value!) + let json = try UploadMetrics(tables: [pullRequestTable, coverageTable]).json() + try json.write(to: NSURL(fileURLWithPath: outputPath.value!) as URL, + atomically: false, + encoding: .utf8) + print("Successfully created \(outputPath.value!)") +} catch { + print("Error occurred: \(error)") + exit(1) +} diff --git a/Metrics/Sources/MetricsLib/CoverageReport.swift b/Metrics/Sources/MetricsLib/CoverageReport.swift new file mode 100644 index 00000000000..a20b35621c0 --- /dev/null +++ b/Metrics/Sources/MetricsLib/CoverageReport.swift @@ -0,0 +1,62 @@ +/* + * Copyright 2019 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 Foundation + +/// Represents a code coverage report generated by XCov (maps to the JSON report one to one). +public struct CoverageReport: Decodable { + public var targets: [Target] + public var coverage: Double + + public init(targets: [Target], coverage: Double) { + self.targets = targets + self.coverage = coverage + } + + /// Creates a CoverageReport from a JSON file generated by XCov. + public static func load(path: String) throws -> CoverageReport { + let data = try Data(contentsOf: NSURL(fileURLWithPath: path) as URL) + let decoder = JSONDecoder() + return try decoder.decode(CoverageReport.self, from: data) + } +} + +/// An XCov target. +public struct Target: Decodable { + public var name: String + public var files: [File] + public var coverage: Double + + public init(name: String, coverage: Double) { + self.name = name + self.coverage = coverage + files = [] + } +} + +/// An XCov file. +public struct File: Decodable { + public var name: String + public var coverage: Double + public var type: String + public var functions: [Function] +} + +/// An XCov function. +public struct Function: Decodable { + public var name: String + public var coverage: Double +} diff --git a/Metrics/Sources/MetricsLib/UploadMetrics.swift b/Metrics/Sources/MetricsLib/UploadMetrics.swift new file mode 100644 index 00000000000..805a9fac4c4 --- /dev/null +++ b/Metrics/Sources/MetricsLib/UploadMetrics.swift @@ -0,0 +1,76 @@ +/* + * Copyright 2019 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 Foundation + +/// A mapping of SDKs to an ID that will represent that SDK in the database. +let TARGET_TO_SDK_INDEX = [ + "Auth_Example_iOS.app": 0.0, + "Core_Example_iOS.app": 1.0, + "Database_Example_iOS.app": 2.0, + "DynamicLinks_Example_iOS.app": 3.0, + "InstanceID_Example_iOS.app": 4.0, + "Messaging_Example_iOS.app": 5.0, + "Storage_Example_iOS.app": 6.0, + // TODO(Corrob): Add support for Firestore, Functions, and InAppMessaging. +] + +/// Represents a set of metric table updates to upload to the database. +public struct UploadMetrics: Encodable { + public var tables: [TableUpdate] + + public init(tables: [TableUpdate]) { + self.tables = tables + } + + /// Converts the metric table updates to a JSON format this is compatible with the Java uploader. + public func json() throws -> String { + let json = try JSONEncoder().encode(self) + return String(data: json, encoding: .utf8)! + } +} + +/// An update to a metrics table with the new data to uplaod to the database. +public struct TableUpdate: Encodable { + public var table_name: String + public var column_names: [String] + public var replace_measurements: [[Double]] + + public init(table_name: String, column_names: [String], replace_measurements: [[Double]]) { + self.table_name = table_name + self.column_names = column_names + self.replace_measurements = replace_measurements + } + + /// Creates a table update for code coverage by parsing a coverage report from XCov. + public static func createFrom(coverage: CoverageReport, pullRequest: Int) -> TableUpdate { + var metrics = [[Double]]() + for target in coverage.targets { + let sdkKey = TARGET_TO_SDK_INDEX[target.name] + if sdkKey == nil { + print("WARNING - target \(target.name) has no mapping to an SDK id. Skipping...") + } else { + var row = [Double]() + row.append(Double(pullRequest)) + row.append(sdkKey!) + row.append(target.coverage) + metrics.append(row) + } + } + let columnNames = ["pull_request_id", "sdk_id", "coverage_percent"] + return TableUpdate(table_name: "Coverage1", column_names: columnNames, replace_measurements: metrics) + } +} diff --git a/Metrics/Tests/MetricsTests/CoverageReportTests.swift b/Metrics/Tests/MetricsTests/CoverageReportTests.swift new file mode 100644 index 00000000000..87bbe299fa1 --- /dev/null +++ b/Metrics/Tests/MetricsTests/CoverageReportTests.swift @@ -0,0 +1,39 @@ +/* + * Copyright 2019 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 MetricsLib +import XCTest + +let EXAMPLE_REPORT = "Tests/MetricsTests/example_report.json" + +final class CoverageReportTests: XCTestCase { + func testShouldParseTotalCoverage() throws { + let report = try CoverageReport.load(path: EXAMPLE_REPORT) + XCTAssertEqual(report.coverage, 0.5490569575543673) + } + + func testShouldParseTargets() throws { + let report = try CoverageReport.load(path: EXAMPLE_REPORT) + XCTAssertEqual(report.targets.count, 14) + XCTAssertEqual(report.targets[0].name, "Auth_Example_iOS.app") + XCTAssertEqual(report.targets[0].coverage, 0.8241201927002532) + XCTAssertEqual(report.targets[0].files.count, 97) + } + + func testShouldFailOnMissingFile() throws { + XCTAssertThrowsError(try CoverageReport.load(path: "IDontExist")) + } +} diff --git a/Metrics/Tests/MetricsTests/UploadMetricsTests.swift b/Metrics/Tests/MetricsTests/UploadMetricsTests.swift new file mode 100644 index 00000000000..2491417db37 --- /dev/null +++ b/Metrics/Tests/MetricsTests/UploadMetricsTests.swift @@ -0,0 +1,52 @@ +/* + * Copyright 2019 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 MetricsLib +import XCTest + +let PULL_REQUEST = 777 + +final class UploadMetricsTests: XCTestCase { + func testShouldCreateTableUpdateFromCoverageReport() { + let target_one = Target(name: "Auth_Example_iOS.app", coverage: 0.1) + let target_two = Target(name: "Core_Example_iOS.app", coverage: 0.2) + let report = CoverageReport(targets: [target_one, target_two], coverage: 0.15) + let metricsUpdate = TableUpdate.createFrom(coverage: report, pullRequest: PULL_REQUEST) + XCTAssertEqual(metricsUpdate.table_name, "Coverage1") + XCTAssertEqual(metricsUpdate.replace_measurements.count, 2) + XCTAssertEqual(metricsUpdate.replace_measurements[0], + [Double(PULL_REQUEST), 0, target_one.coverage]) + XCTAssertEqual(metricsUpdate.replace_measurements[1], + [Double(PULL_REQUEST), 1, target_two.coverage]) + } + + func testShouldIgnoreUnkownTargets() { + let target = Target(name: "Unknown_Target", coverage: 0.3) + let report = CoverageReport(targets: [target], coverage: 0.15) + let metrics = TableUpdate.createFrom(coverage: report, pullRequest: PULL_REQUEST) + XCTAssertEqual(metrics.table_name, "Coverage1") + XCTAssertEqual(metrics.replace_measurements.count, 0) + } + + func testShouldConvertToJson() throws { + let table = TableUpdate(table_name: "name", + column_names: ["col"], + replace_measurements: [[0], [2]]) + let metrics = UploadMetrics(tables: [table]) + let json = try metrics.json() + XCTAssertEqual(json, "{\"tables\":[{\"replace_measurements\":[[0],[2]],\"column_names\":[\"col\"],\"table_name\":\"name\"}]}") + } +} diff --git a/Metrics/Tests/MetricsTests/example_report.json b/Metrics/Tests/MetricsTests/example_report.json new file mode 100644 index 00000000000..08f775a87d6 --- /dev/null +++ b/Metrics/Tests/MetricsTests/example_report.json @@ -0,0 +1,41997 @@ +{ + "coverage": 0.5490569575543673, + "targets": [ + { + "name": "Auth_Example_iOS.app", + "coverage": 0.8241201927002532, + "files": [ + { + "name": "FIRGameCenterAuthCredential.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FIRGameCenterAuthCredential initWithProvider:]", + "coverage": 0 + }, + { + "name": "-[FIRGameCenterAuthCredential initWithPlayerID:publicKeyURL:signature:salt:timestamp:displayName:]", + "coverage": 0 + }, + { + "name": "-[FIRGameCenterAuthCredential prepareVerifyAssertionRequest:]", + "coverage": 0 + }, + { + "name": "+[FIRGameCenterAuthCredential supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRGameCenterAuthCredential initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRGameCenterAuthCredential encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthExceptionUtils.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[FIRAuthExceptionUtils raiseInvalidParameterExceptionWithReason:]", + "coverage": 0 + }, + { + "name": "+[FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthWebView.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthWebView initWithFrame:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebView initializeSubviews]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebView layoutSubviews]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebView createWebView]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebView createSpinner]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthWebViewController.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthWebViewController initWithURL:delegate:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebViewController loadView]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebViewController viewDidAppear:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebViewController cancel]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebViewController webView:shouldStartLoadWithRequest:navigationType:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebViewController webViewDidStartLoad:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebViewController webViewDidFinishLoad:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthWebViewController webView:didFailLoadWithError:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRGameCenterAuthProvider.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FIRGameCenterAuthProvider init]", + "coverage": 0 + }, + { + "name": "+[FIRGameCenterAuthProvider getCredentialWithCompletion:]", + "coverage": 0 + }, + { + "name": "__57+[FIRGameCenterAuthProvider getCredentialWithCompletion:]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "FIRSecureTokenResponse.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FIRSecureTokenResponse expectedKind]", + "coverage": 0 + }, + { + "name": "-[FIRSecureTokenResponse setWithDictionary:error:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthDefaultUIDelegate.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthDefaultUIDelegate initWithViewController:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthDefaultUIDelegate presentViewController:animated:completion:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthDefaultUIDelegate dismissViewControllerAnimated:completion:]", + "coverage": 0 + }, + { + "name": "+[FIRAuthDefaultUIDelegate defaultUIDelegate]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthTokenResult.m", + "coverage": 0.2727272727272727, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthTokenResult initWithToken:expirationDate:authDate:issuedAtDate:signInProvider:claims:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthTokenResult supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRAuthTokenResult initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthTokenResult encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRSecureTokenRequest.m", + "coverage": 0.27941176470588236, + "type": "objc", + "functions": [ + { + "name": "+[FIRSecureTokenRequest authCodeRequestWithCode:requestConfiguration:]", + "coverage": 0 + }, + { + "name": "+[FIRSecureTokenRequest refreshRequestWithRefreshToken:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "+[FIRSecureTokenRequest grantTypeStringWithGrantType:]", + "coverage": 0 + }, + { + "name": "-[FIRSecureTokenRequest initWithGrantType:scope:refreshToken:code:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRSecureTokenRequest requestConfiguration]", + "coverage": 0 + }, + { + "name": "-[FIRSecureTokenRequest requestURL]", + "coverage": 0 + }, + { + "name": "-[FIRSecureTokenRequest containsPostBody]", + "coverage": 0 + }, + { + "name": "-[FIRSecureTokenRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 0 + }, + { + "name": "+[FIRSecureTokenRequest host]", + "coverage": 0 + }, + { + "name": "+[FIRSecureTokenRequest setHost:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAppDelegate.m", + "coverage": 0.3333333333333333, + "type": "objc", + "functions": [ + { + "name": "-[FIRAppDelegate application:didFinishLaunchingWithOptions:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillResignActive:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidEnterBackground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationWillEnterForeground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidBecomeActive:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillTerminate:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthDataResult.m", + "coverage": 0.3333333333333333, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthDataResult initWithUser:additionalUserInfo:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthDataResult supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRAuthDataResult initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthDataResult encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIREmailPasswordAuthCredential.m", + "coverage": 0.34782608695652173, + "type": "objc", + "functions": [ + { + "name": "-[FIREmailPasswordAuthCredential initWithProvider:]", + "coverage": 0 + }, + { + "name": "-[FIREmailPasswordAuthCredential initWithEmail:password:]", + "coverage": 1 + }, + { + "name": "-[FIREmailPasswordAuthCredential initWithEmail:link:]", + "coverage": 1 + }, + { + "name": "-[FIREmailPasswordAuthCredential prepareVerifyAssertionRequest:]", + "coverage": 0 + }, + { + "name": "+[FIREmailPasswordAuthCredential supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIREmailPasswordAuthCredential initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIREmailPasswordAuthCredential encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRPhoneAuthCredential.m", + "coverage": 0.36363636363636365, + "type": "objc", + "functions": [ + { + "name": "-[FIRPhoneAuthCredential initWithTemporaryProof:phoneNumber:providerID:]", + "coverage": 1 + }, + { + "name": "-[FIRPhoneAuthCredential initWithProvider:]", + "coverage": 0 + }, + { + "name": "-[FIRPhoneAuthCredential initWithProviderID:verificationID:verificationCode:]", + "coverage": 1 + }, + { + "name": "+[FIRPhoneAuthCredential supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRPhoneAuthCredential initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRPhoneAuthCredential encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRTwitterAuthProvider.m", + "coverage": 0.375, + "type": "objc", + "functions": [ + { + "name": "-[FIRTwitterAuthProvider init]", + "coverage": 0 + }, + { + "name": "+[FIRTwitterAuthProvider credentialWithToken:secret:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRFacebookAuthProvider.m", + "coverage": 0.375, + "type": "objc", + "functions": [ + { + "name": "-[FIRFacebookAuthProvider init]", + "coverage": 0 + }, + { + "name": "+[FIRFacebookAuthProvider credentialWithAccessToken:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRGitHubAuthProvider.m", + "coverage": 0.375, + "type": "objc", + "functions": [ + { + "name": "-[FIRGitHubAuthProvider init]", + "coverage": 0 + }, + { + "name": "+[FIRGitHubAuthProvider credentialWithToken:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRGoogleAuthProvider.m", + "coverage": 0.375, + "type": "objc", + "functions": [ + { + "name": "-[FIRGoogleAuthProvider init]", + "coverage": 0 + }, + { + "name": "+[FIRGoogleAuthProvider credentialWithIDToken:accessToken:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRGitHubAuthCredential.m", + "coverage": 0.38461538461538464, + "type": "objc", + "functions": [ + { + "name": "-[FIRGitHubAuthCredential initWithProvider:]", + "coverage": 0 + }, + { + "name": "-[FIRGitHubAuthCredential initWithToken:]", + "coverage": 1 + }, + { + "name": "-[FIRGitHubAuthCredential prepareVerifyAssertionRequest:]", + "coverage": 1 + }, + { + "name": "+[FIRGitHubAuthCredential supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRGitHubAuthCredential initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRGitHubAuthCredential encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRFacebookAuthCredential.m", + "coverage": 0.38461538461538464, + "type": "objc", + "functions": [ + { + "name": "-[FIRFacebookAuthCredential initWithProvider:]", + "coverage": 0 + }, + { + "name": "-[FIRFacebookAuthCredential initWithAccessToken:]", + "coverage": 1 + }, + { + "name": "-[FIRFacebookAuthCredential prepareVerifyAssertionRequest:]", + "coverage": 1 + }, + { + "name": "+[FIRFacebookAuthCredential supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRFacebookAuthCredential initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRFacebookAuthCredential encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthCredential.m", + "coverage": 0.3888888888888889, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthCredential init]", + "coverage": 0 + }, + { + "name": "-[FIRAuthCredential initWithProvider:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthCredential prepareVerifyAssertionRequest:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRTwitterAuthCredential.m", + "coverage": 0.4, + "type": "objc", + "functions": [ + { + "name": "-[FIRTwitterAuthCredential initWithProvider:]", + "coverage": 0 + }, + { + "name": "-[FIRTwitterAuthCredential initWithToken:secret:]", + "coverage": 1 + }, + { + "name": "-[FIRTwitterAuthCredential prepareVerifyAssertionRequest:]", + "coverage": 1 + }, + { + "name": "+[FIRTwitterAuthCredential supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRTwitterAuthCredential initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRTwitterAuthCredential encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRGoogleAuthCredential.m", + "coverage": 0.4, + "type": "objc", + "functions": [ + { + "name": "-[FIRGoogleAuthCredential initWithProvider:]", + "coverage": 0 + }, + { + "name": "-[FIRGoogleAuthCredential initWithIDToken:accessToken:]", + "coverage": 1 + }, + { + "name": "-[FIRGoogleAuthCredential prepareVerifyAssertionRequest:]", + "coverage": 1 + }, + { + "name": "+[FIRGoogleAuthCredential supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRGoogleAuthCredential initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRGoogleAuthCredential encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIROAuthCredential.m", + "coverage": 0.4772727272727273, + "type": "objc", + "functions": [ + { + "name": "-[FIROAuthCredential initWithProvider:]", + "coverage": 0 + }, + { + "name": "-[FIROAuthCredential initWithProviderID:IDToken:accessToken:pendingToken:]", + "coverage": 1 + }, + { + "name": "-[FIROAuthCredential initWithProviderID:sessionID:OAuthResponseURLString:]", + "coverage": 1 + }, + { + "name": "-[FIROAuthCredential prepareVerifyAssertionRequest:]", + "coverage": 1 + }, + { + "name": "+[FIROAuthCredential supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIROAuthCredential initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIROAuthCredential encodeWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthURLPresenter.m", + "coverage": 0.496551724137931, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthURLPresenter presentURL:UIDelegate:callbackMatcher:completion:]", + "coverage": 0.8461538461538461 + }, + { + "name": "__72-[FIRAuthURLPresenter presentURL:UIDelegate:callbackMatcher:completion:]_block_invoke", + "coverage": 0.6875 + }, + { + "name": "-[FIRAuthURLPresenter canHandleURL:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRAuthURLPresenter safariViewControllerDidFinish:]", + "coverage": 0 + }, + { + "name": "__53-[FIRAuthURLPresenter safariViewControllerDidFinish:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuthURLPresenter webViewController:canHandleURL:]", + "coverage": 0 + }, + { + "name": "__54-[FIRAuthURLPresenter webViewController:canHandleURL:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuthURLPresenter webViewControllerDidCancel:]", + "coverage": 0 + }, + { + "name": "__50-[FIRAuthURLPresenter webViewControllerDidCancel:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuthURLPresenter webViewController:didFailWithError:]", + "coverage": 0 + }, + { + "name": "__58-[FIRAuthURLPresenter webViewController:didFailWithError:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuthURLPresenter finishPresentationWithURL:error:]", + "coverage": 0.9166666666666666 + }, + { + "name": "__55-[FIRAuthURLPresenter finishPresentationWithURL:error:]_block_invoke", + "coverage": 1 + }, + { + "name": "__55-[FIRAuthURLPresenter finishPresentationWithURL:error:]_block_invoke.42", + "coverage": 1 + }, + { + "name": "__55-[FIRAuthURLPresenter finishPresentationWithURL:error:]_block_invoke_2", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthAPNSTokenManager.m", + "coverage": 0.49743589743589745, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthAPNSTokenManager initWithApplication:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAPNSTokenManager getTokenWithCallback:]", + "coverage": 1 + }, + { + "name": "__48-[FIRAuthAPNSTokenManager getTokenWithCallback:]_block_invoke", + "coverage": 1 + }, + { + "name": "__48-[FIRAuthAPNSTokenManager getTokenWithCallback:]_block_invoke.13", + "coverage": 1 + }, + { + "name": "-[FIRAuthAPNSTokenManager setToken:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAPNSTokenManager cancelWithError:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAPNSTokenManager callBackWithToken:error:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthAPNSTokenManager isProductionApp]", + "coverage": 0.08411214953271028 + } + ] + }, + { + "name": "FIRViewController.m", + "coverage": 0.5, + "type": "objc", + "functions": [ + { + "name": "-[FIRViewController viewDidLoad]", + "coverage": 1 + }, + { + "name": "-[FIRViewController didReceiveMemoryWarning]", + "coverage": 0 + } + ] + }, + { + "name": "FIREmailAuthProvider.m", + "coverage": 0.5454545454545454, + "type": "objc", + "functions": [ + { + "name": "-[FIREmailAuthProvider init]", + "coverage": 0 + }, + { + "name": "+[FIREmailAuthProvider credentialWithEmail:password:]", + "coverage": 1 + }, + { + "name": "+[FIREmailAuthProvider credentialWithEmail:link:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRSendVerificationCodeResponse.m", + "coverage": 0.5714285714285714, + "type": "objc", + "functions": [ + { + "name": "-[FIRSendVerificationCodeResponse expectedKind]", + "coverage": 0 + }, + { + "name": "-[FIRSendVerificationCodeResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRCreateAuthURIRequest.m", + "coverage": 0.6666666666666666, + "type": "objc", + "functions": [ + { + "name": "-[FIRCreateAuthURIRequest initWithIdentifier:continueURI:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRCreateAuthURIRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 0.5454545454545454 + } + ] + }, + { + "name": "FIRGetProjectConfigResponse.m", + "coverage": 0.6666666666666666, + "type": "objc", + "functions": [ + { + "name": "-[FIRGetProjectConfigResponse setWithDictionary:error:]", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FIRAuthKeychain.m", + "coverage": 0.7413793103448276, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthKeychain initWithService:]", + "coverage": 0.9090909090909091 + }, + { + "name": "-[FIRAuthKeychain dataForKey:error:]", + "coverage": 0.6363636363636364 + }, + { + "name": "-[FIRAuthKeychain setData:forKey:error:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRAuthKeychain removeDataForKey:error:]", + "coverage": 0.5714285714285714 + }, + { + "name": "-[FIRAuthKeychain itemWithQuery:error:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAuthKeychain setItemWithQuery:attributes:error:]", + "coverage": 0.7 + }, + { + "name": "-[FIRAuthKeychain deleteItemWithQuery:error:]", + "coverage": 0.5 + }, + { + "name": "-[FIRAuthKeychain deleteLegacyItemWithKey:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthKeychain genericPasswordQueryWithKey:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthKeychain legacyGenericPasswordQueryWithKey:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRIdentityToolkitRequest.m", + "coverage": 0.7692307692307693, + "type": "objc", + "functions": [ + { + "name": "-[FIRIdentityToolkitRequest initWithEndpoint:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRIdentityToolkitRequest containsPostBody]", + "coverage": 1 + }, + { + "name": "-[FIRIdentityToolkitRequest requestURL]", + "coverage": 1 + }, + { + "name": "-[FIRIdentityToolkitRequest requestConfiguration]", + "coverage": 1 + }, + { + "name": "+[FIRIdentityToolkitRequest host]", + "coverage": 0 + }, + { + "name": "+[FIRIdentityToolkitRequest setHost:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRVerifyPhoneNumberResponse.m", + "coverage": 0.7857142857142857, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyPhoneNumberResponse expectedKind]", + "coverage": 0 + }, + { + "name": "-[FIRVerifyPhoneNumberResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRDependency.m", + "coverage": 0.7857142857142857, + "type": "objc", + "functions": [ + { + "name": "+[FIRDependency dependencyWithProtocol:]", + "coverage": 0 + }, + { + "name": "+[FIRDependency dependencyWithProtocol:isRequired:]", + "coverage": 1 + }, + { + "name": "-[FIRDependency initWithProtocol:isRequired:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRBundleUtil.m", + "coverage": 0.7872340425531915, + "type": "objc", + "functions": [ + { + "name": "+[FIRBundleUtil relevantBundles]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil optionsDictionaryPathWithResourceName:andFileType:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil relevantURLSchemes]", + "coverage": 0 + }, + { + "name": "+[FIRBundleUtil hasBundleIdentifierPrefix:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil bundleIdentifierByRemovingLastPartFrom:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRUser.m", + "coverage": 0.7953100158982512, + "type": "objc", + "functions": [ + { + "name": "callInMainThreadWithError", + "coverage": 1 + }, + { + "name": "__callInMainThreadWithError_block_invoke", + "coverage": 1 + }, + { + "name": "callInMainThreadWithUserAndError", + "coverage": 1 + }, + { + "name": "__callInMainThreadWithUserAndError_block_invoke", + "coverage": 1 + }, + { + "name": "callInMainThreadWithAuthDataResultAndError", + "coverage": 1 + }, + { + "name": "__callInMainThreadWithAuthDataResultAndError_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRUser retrieveUserWithAuth:accessToken:accessTokenExpirationDate:refreshToken:anonymous:callback:]", + "coverage": 1 + }, + { + "name": "__102+[FIRUser retrieveUserWithAuth:accessToken:accessTokenExpirationDate:refreshToken:anonymous:callback:]_block_invoke", + "coverage": 0.8571428571428571 + }, + { + "name": "__102+[FIRUser retrieveUserWithAuth:accessToken:accessTokenExpirationDate:refreshToken:anonymous:callback:]_block_invoke_2", + "coverage": 0.6 + }, + { + "name": "-[FIRUser initWithTokenService:]", + "coverage": 1 + }, + { + "name": "+[FIRUser supportsSecureCoding]", + "coverage": 1 + }, + { + "name": "-[FIRUser initWithCoder:]", + "coverage": 0.96 + }, + { + "name": "-[FIRUser encodeWithCoder:]", + "coverage": 1 + }, + { + "name": "-[FIRUser setAuth:]", + "coverage": 1 + }, + { + "name": "-[FIRUser providerID]", + "coverage": 1 + }, + { + "name": "-[FIRUser providerData]", + "coverage": 1 + }, + { + "name": "-[FIRUser getAccountInfoRefreshingCache:]", + "coverage": 1 + }, + { + "name": "__41-[FIRUser getAccountInfoRefreshingCache:]_block_invoke", + "coverage": 0.875 + }, + { + "name": "__41-[FIRUser getAccountInfoRefreshingCache:]_block_invoke_2", + "coverage": 0.7692307692307693 + }, + { + "name": "-[FIRUser updateWithGetAccountInfoResponse:]", + "coverage": 1 + }, + { + "name": "-[FIRUser executeUserUpdateWithChanges:callback:]", + "coverage": 1 + }, + { + "name": "__49-[FIRUser executeUserUpdateWithChanges:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "__49-[FIRUser executeUserUpdateWithChanges:callback:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__49-[FIRUser executeUserUpdateWithChanges:callback:]_block_invoke_3", + "coverage": 0.8947368421052632 + }, + { + "name": "__49-[FIRUser executeUserUpdateWithChanges:callback:]_block_invoke_4", + "coverage": 0.5 + }, + { + "name": "__49-[FIRUser executeUserUpdateWithChanges:callback:]_block_invoke_5", + "coverage": 0 + }, + { + "name": "-[FIRUser updateKeychain:]", + "coverage": 1 + }, + { + "name": "-[FIRUser setTokenService:callback:]", + "coverage": 1 + }, + { + "name": "__36-[FIRUser setTokenService:callback:]_block_invoke", + "coverage": 0.5 + }, + { + "name": "-[FIRUser updateEmail:password:callback:]", + "coverage": 1 + }, + { + "name": "__41-[FIRUser updateEmail:password:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "__41-[FIRUser updateEmail:password:callback:]_block_invoke.205", + "coverage": 0.95 + }, + { + "name": "__41-[FIRUser updateEmail:password:callback:]_block_invoke_2", + "coverage": 0.9230769230769231 + }, + { + "name": "__41-[FIRUser updateEmail:password:callback:]_block_invoke_3", + "coverage": 0.7407407407407407 + }, + { + "name": "-[FIRUser updateEmail:completion:]", + "coverage": 1 + }, + { + "name": "__34-[FIRUser updateEmail:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__34-[FIRUser updateEmail:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRUser updatePassword:completion:]", + "coverage": 1 + }, + { + "name": "__37-[FIRUser updatePassword:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__37-[FIRUser updatePassword:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRUser internalUpdateOrLinkPhoneNumberCredential:isLinkOperation:completion:]", + "coverage": 1 + }, + { + "name": "__80-[FIRUser internalUpdateOrLinkPhoneNumberCredential:isLinkOperation:completion:]_block_invoke", + "coverage": 0.9210526315789473 + }, + { + "name": "__80-[FIRUser internalUpdateOrLinkPhoneNumberCredential:isLinkOperation:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__80-[FIRUser internalUpdateOrLinkPhoneNumberCredential:isLinkOperation:completion:]_block_invoke_3", + "coverage": 0.46153846153846156 + }, + { + "name": "-[FIRUser updatePhoneNumberCredential:completion:]", + "coverage": 1 + }, + { + "name": "__50-[FIRUser updatePhoneNumberCredential:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__50-[FIRUser updatePhoneNumberCredential:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRUser profileChangeRequest]", + "coverage": 1 + }, + { + "name": "__31-[FIRUser profileChangeRequest]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRUser setDisplayName:]", + "coverage": 1 + }, + { + "name": "-[FIRUser setPhotoURL:]", + "coverage": 1 + }, + { + "name": "-[FIRUser rawAccessToken]", + "coverage": 1 + }, + { + "name": "-[FIRUser accessTokenExpirationDate]", + "coverage": 1 + }, + { + "name": "-[FIRUser reloadWithCompletion:]", + "coverage": 1 + }, + { + "name": "__32-[FIRUser reloadWithCompletion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__32-[FIRUser reloadWithCompletion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRUser reauthenticateWithCredential:completion:]", + "coverage": 1 + }, + { + "name": "__51-[FIRUser reauthenticateWithCredential:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRUser reauthenticateAndRetrieveDataWithCredential:completion:]", + "coverage": 1 + }, + { + "name": "__66-[FIRUser reauthenticateAndRetrieveDataWithCredential:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__66-[FIRUser reauthenticateAndRetrieveDataWithCredential:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__66-[FIRUser reauthenticateAndRetrieveDataWithCredential:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRUser refreshToken]", + "coverage": 1 + }, + { + "name": "__23-[FIRUser refreshToken]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRUser getIDTokenWithCompletion:]", + "coverage": 0 + }, + { + "name": "-[FIRUser getIDTokenForcingRefresh:completion:]", + "coverage": 0 + }, + { + "name": "__47-[FIRUser getIDTokenForcingRefresh:completion:]_block_invoke", + "coverage": 0 + }, + { + "name": "__47-[FIRUser getIDTokenForcingRefresh:completion:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRUser getIDTokenResultWithCompletion:]", + "coverage": 1 + }, + { + "name": "__42-[FIRUser getIDTokenResultWithCompletion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__42-[FIRUser getIDTokenResultWithCompletion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRUser getIDTokenResultForcingRefresh:completion:]", + "coverage": 1 + }, + { + "name": "__53-[FIRUser getIDTokenResultForcingRefresh:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__53-[FIRUser getIDTokenResultForcingRefresh:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__53-[FIRUser getIDTokenResultForcingRefresh:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRUser parseIDToken:error:]", + "coverage": 0.75 + }, + { + "name": "-[FIRUser internalGetTokenWithCallback:]", + "coverage": 1 + }, + { + "name": "-[FIRUser internalGetTokenForcingRefresh:callback:]", + "coverage": 1 + }, + { + "name": "__51-[FIRUser internalGetTokenForcingRefresh:callback:]_block_invoke", + "coverage": 0.7857142857142857 + }, + { + "name": "-[FIRUser linkWithCredential:completion:]", + "coverage": 1 + }, + { + "name": "__41-[FIRUser linkWithCredential:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRUser linkAndRetrieveDataWithCredential:completion:]", + "coverage": 1 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke", + "coverage": 0.6848484848484848 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke.415", + "coverage": 0 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_2.431", + "coverage": 0 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_4", + "coverage": 0 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke.448", + "coverage": 1 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke.452", + "coverage": 1 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_2.453", + "coverage": 1 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke.457", + "coverage": 0.9464285714285714 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_2.463", + "coverage": 1 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_3.467", + "coverage": 0.88 + }, + { + "name": "__56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_4.468", + "coverage": 0.5 + }, + { + "name": "-[FIRUser unlinkFromProvider:completion:]", + "coverage": 1 + }, + { + "name": "__41-[FIRUser unlinkFromProvider:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__41-[FIRUser unlinkFromProvider:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__41-[FIRUser unlinkFromProvider:completion:]_block_invoke.493", + "coverage": 0.8382352941176471 + }, + { + "name": "__41-[FIRUser unlinkFromProvider:completion:]_block_invoke_2.500", + "coverage": 0.5365853658536586 + }, + { + "name": "__41-[FIRUser unlinkFromProvider:completion:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "-[FIRUser sendEmailVerificationWithCompletion:]", + "coverage": 0 + }, + { + "name": "-[FIRUser sendEmailVerificationWithActionCodeSettings:completion:]", + "coverage": 0 + }, + { + "name": "-[FIRUser sendEmailVerificationWithNullableActionCodeSettings:completion:]", + "coverage": 0 + }, + { + "name": "__74-[FIRUser sendEmailVerificationWithNullableActionCodeSettings:completion:]_block_invoke", + "coverage": 0 + }, + { + "name": "__74-[FIRUser sendEmailVerificationWithNullableActionCodeSettings:completion:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__74-[FIRUser sendEmailVerificationWithNullableActionCodeSettings:completion:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "-[FIRUser deleteWithCompletion:]", + "coverage": 0 + }, + { + "name": "__32-[FIRUser deleteWithCompletion:]_block_invoke", + "coverage": 0 + }, + { + "name": "__32-[FIRUser deleteWithCompletion:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__32-[FIRUser deleteWithCompletion:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "-[FIRUser signOutIfTokenIsInvalidWithError:]", + "coverage": 1 + }, + { + "name": "-[FIRUserProfileChangeRequest initWithUser:]", + "coverage": 1 + }, + { + "name": "-[FIRUserProfileChangeRequest displayName]", + "coverage": 0 + }, + { + "name": "-[FIRUserProfileChangeRequest setDisplayName:]", + "coverage": 1 + }, + { + "name": "__46-[FIRUserProfileChangeRequest setDisplayName:]_block_invoke", + "coverage": 0.5 + }, + { + "name": "-[FIRUserProfileChangeRequest photoURL]", + "coverage": 0 + }, + { + "name": "-[FIRUserProfileChangeRequest setPhotoURL:]", + "coverage": 1 + }, + { + "name": "__43-[FIRUserProfileChangeRequest setPhotoURL:]_block_invoke", + "coverage": 0.5 + }, + { + "name": "-[FIRUserProfileChangeRequest hasUpdates]", + "coverage": 1 + }, + { + "name": "-[FIRUserProfileChangeRequest commitChangesWithCompletion:]", + "coverage": 1 + }, + { + "name": "__59-[FIRUserProfileChangeRequest commitChangesWithCompletion:]_block_invoke", + "coverage": 0.8181818181818182 + }, + { + "name": "__59-[FIRUserProfileChangeRequest commitChangesWithCompletion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__59-[FIRUserProfileChangeRequest commitChangesWithCompletion:]_block_invoke.711", + "coverage": 0.8235294117647058 + } + ] + }, + { + "name": "FIRConfiguration.m", + "coverage": 0.8076923076923077, + "type": "objc", + "functions": [ + { + "name": "+[FIRConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__34+[FIRConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration init]", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration setLoggerLevel:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRLogger.m", + "coverage": 0.8118811881188119, + "type": "objc", + "functions": [ + { + "name": "FIRLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__FIRLoggerInitializeASL_block_invoke", + "coverage": 0.9333333333333333 + }, + { + "name": "FIRSetAnalyticsDebugMode", + "coverage": 0 + }, + { + "name": "FIRSetLoggerLevel", + "coverage": 0 + }, + { + "name": "FIRResetLogger", + "coverage": 1 + }, + { + "name": "FIRSetLoggerUserDefaults", + "coverage": 1 + }, + { + "name": "FIRIsLoggableLevel", + "coverage": 0 + }, + { + "name": "FIRLogBasic", + "coverage": 1 + }, + { + "name": "Definition at 158:95", + "coverage": 1 + }, + { + "name": "+[FIRLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + }, + { + "name": "FIROAuthProvider.m", + "coverage": 0.8430379746835444, + "type": "objc", + "functions": [ + { + "name": "+[FIROAuthProvider credentialWithProviderID:IDToken:accessToken:pendingToken:]", + "coverage": 0 + }, + { + "name": "+[FIROAuthProvider credentialWithProviderID:IDToken:accessToken:]", + "coverage": 1 + }, + { + "name": "+[FIROAuthProvider credentialWithProviderID:accessToken:]", + "coverage": 1 + }, + { + "name": "+[FIROAuthProvider providerWithProviderID:]", + "coverage": 0 + }, + { + "name": "+[FIROAuthProvider providerWithProviderID:auth:]", + "coverage": 1 + }, + { + "name": "-[FIROAuthProvider getCredentialWithUIDelegate:completion:]", + "coverage": 0.9344262295081968 + }, + { + "name": "__59-[FIROAuthProvider getCredentialWithUIDelegate:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__59-[FIROAuthProvider getCredentialWithUIDelegate:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__59-[FIROAuthProvider getCredentialWithUIDelegate:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "__59-[FIROAuthProvider getCredentialWithUIDelegate:completion:]_block_invoke.25", + "coverage": 0.9142857142857143 + }, + { + "name": "__59-[FIROAuthProvider getCredentialWithUIDelegate:completion:]_block_invoke_2.26", + "coverage": 1 + }, + { + "name": "__59-[FIROAuthProvider getCredentialWithUIDelegate:completion:]_block_invoke.35", + "coverage": 1 + }, + { + "name": "-[FIROAuthProvider initWithProviderID:auth:]", + "coverage": 1 + }, + { + "name": "-[FIROAuthProvider OAuthResponseForURL:error:]", + "coverage": 0.8484848484848485 + }, + { + "name": "-[FIROAuthProvider getHeadFulLiteURLWithEventID:sessionID:completion:]", + "coverage": 1 + }, + { + "name": "__70-[FIROAuthProvider getHeadFulLiteURLWithEventID:sessionID:completion:]_block_invoke", + "coverage": 0.6086956521739131 + }, + { + "name": "-[FIROAuthProvider customParametersStringWithError:]", + "coverage": 0 + }, + { + "name": "-[FIROAuthProvider hashforString:]", + "coverage": 0.8888888888888888 + }, + { + "name": "-[FIROAuthProvider hexStringFromData:]", + "coverage": 1 + }, + { + "name": "-[FIROAuthProvider httpArgumentsStringForArgsDictionary:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuth.m", + "coverage": 0.8576283987915407, + "type": "objc", + "functions": [ + { + "name": "-[FIRActionCodeInfo dataForKey:]", + "coverage": 0.875 + }, + { + "name": "-[FIRActionCodeInfo initWithOperation:email:newEmail:]", + "coverage": 1 + }, + { + "name": "+[FIRActionCodeInfo actionCodeOperationForRequestType:]", + "coverage": 0.4666666666666667 + }, + { + "name": "+[FIRAuth load]", + "coverage": 1 + }, + { + "name": "+[FIRAuth initialize]", + "coverage": 1 + }, + { + "name": "+[FIRAuth auth]", + "coverage": 1 + }, + { + "name": "+[FIRAuth authWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth initWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth initWithAPIKey:appName:]", + "coverage": 1 + }, + { + "name": "__34-[FIRAuth initWithAPIKey:appName:]_block_invoke", + "coverage": 0.8611111111111112 + }, + { + "name": "-[FIRAuth dealloc]", + "coverage": 0.7894736842105263 + }, + { + "name": "-[FIRAuth currentUser]", + "coverage": 1 + }, + { + "name": "__22-[FIRAuth currentUser]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth fetchProvidersForEmail:completion:]", + "coverage": 1 + }, + { + "name": "__45-[FIRAuth fetchProvidersForEmail:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__45-[FIRAuth fetchProvidersForEmail:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__45-[FIRAuth fetchProvidersForEmail:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInWithProvider:UIDelegate:completion:]", + "coverage": 1 + }, + { + "name": "__52-[FIRAuth signInWithProvider:UIDelegate:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__52-[FIRAuth signInWithProvider:UIDelegate:completion:]_block_invoke_2", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRAuth fetchSignInMethodsForEmail:completion:]", + "coverage": 1 + }, + { + "name": "__49-[FIRAuth fetchSignInMethodsForEmail:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__49-[FIRAuth fetchSignInMethodsForEmail:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__49-[FIRAuth fetchSignInMethodsForEmail:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInWithEmail:password:completion:]", + "coverage": 1 + }, + { + "name": "__47-[FIRAuth signInWithEmail:password:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__47-[FIRAuth signInWithEmail:password:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInWithEmail:link:completion:]", + "coverage": 1 + }, + { + "name": "__43-[FIRAuth signInWithEmail:link:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__43-[FIRAuth signInWithEmail:link:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInWithEmail:password:callback:]", + "coverage": 1 + }, + { + "name": "__45-[FIRAuth signInWithEmail:password:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInAndRetrieveDataWithEmail:password:completion:]", + "coverage": 1 + }, + { + "name": "__62-[FIRAuth signInAndRetrieveDataWithEmail:password:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth internalSignInAndRetrieveDataWithEmail:password:completion:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInAndRetrieveDataWithGameCenterCredential:callback:]", + "coverage": 0 + }, + { + "name": "__66-[FIRAuth signInAndRetrieveDataWithGameCenterCredential:callback:]_block_invoke", + "coverage": 0 + }, + { + "name": "__66-[FIRAuth signInAndRetrieveDataWithGameCenterCredential:callback:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRAuth internalSignInAndRetrieveDataWithEmail:link:callback:]", + "coverage": 0.8653846153846154 + }, + { + "name": "__64-[FIRAuth internalSignInAndRetrieveDataWithEmail:link:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "__64-[FIRAuth internalSignInAndRetrieveDataWithEmail:link:callback:]_block_invoke_2", + "coverage": 0.7368421052631579 + }, + { + "name": "-[FIRAuth signInWithCredential:completion:]", + "coverage": 1 + }, + { + "name": "__43-[FIRAuth signInWithCredential:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInAndRetrieveDataWithCredential:completion:]", + "coverage": 1 + }, + { + "name": "__58-[FIRAuth signInAndRetrieveDataWithCredential:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth internalSignInWithCredential:callback:]", + "coverage": 1 + }, + { + "name": "__49-[FIRAuth internalSignInWithCredential:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth internalSignInAndRetrieveDataWithCredential:isReauthentication:callback:]", + "coverage": 0.9615384615384616 + }, + { + "name": "__83-[FIRAuth internalSignInAndRetrieveDataWithCredential:isReauthentication:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "__83-[FIRAuth internalSignInAndRetrieveDataWithCredential:isReauthentication:callback:]_block_invoke.333", + "coverage": 1 + }, + { + "name": "__83-[FIRAuth internalSignInAndRetrieveDataWithCredential:isReauthentication:callback:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__83-[FIRAuth internalSignInAndRetrieveDataWithCredential:isReauthentication:callback:]_block_invoke.365", + "coverage": 0.8648648648648649 + }, + { + "name": "__83-[FIRAuth internalSignInAndRetrieveDataWithCredential:isReauthentication:callback:]_block_invoke_2.374", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInWithCredential:callback:]", + "coverage": 0 + }, + { + "name": "__41-[FIRAuth signInWithCredential:callback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth signInAnonymouslyAndRetrieveDataWithCompletion:]", + "coverage": 1 + }, + { + "name": "__58-[FIRAuth signInAnonymouslyAndRetrieveDataWithCompletion:]_block_invoke", + "coverage": 0.7105263157894737 + }, + { + "name": "__58-[FIRAuth signInAnonymouslyAndRetrieveDataWithCompletion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__58-[FIRAuth signInAnonymouslyAndRetrieveDataWithCompletion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInAnonymouslyWithCompletion:]", + "coverage": 1 + }, + { + "name": "__43-[FIRAuth signInAnonymouslyWithCompletion:]_block_invoke", + "coverage": 0.84375 + }, + { + "name": "__43-[FIRAuth signInAnonymouslyWithCompletion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__43-[FIRAuth signInAnonymouslyWithCompletion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInWithCustomToken:completion:]", + "coverage": 1 + }, + { + "name": "__44-[FIRAuth signInWithCustomToken:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__44-[FIRAuth signInWithCustomToken:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInAndRetrieveDataWithCustomToken:completion:]", + "coverage": 1 + }, + { + "name": "__59-[FIRAuth signInAndRetrieveDataWithCustomToken:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth createUserWithEmail:password:completion:]", + "coverage": 1 + }, + { + "name": "__51-[FIRAuth createUserWithEmail:password:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__51-[FIRAuth createUserWithEmail:password:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__51-[FIRAuth createUserWithEmail:password:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth createUserAndRetrieveDataWithEmail:password:completion:]", + "coverage": 1 + }, + { + "name": "__66-[FIRAuth createUserAndRetrieveDataWithEmail:password:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__66-[FIRAuth createUserAndRetrieveDataWithEmail:password:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__66-[FIRAuth createUserAndRetrieveDataWithEmail:password:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth confirmPasswordResetWithCode:newPassword:completion:]", + "coverage": 1 + }, + { + "name": "__63-[FIRAuth confirmPasswordResetWithCode:newPassword:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__63-[FIRAuth confirmPasswordResetWithCode:newPassword:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__63-[FIRAuth confirmPasswordResetWithCode:newPassword:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth checkActionCode:completion:]", + "coverage": 1 + }, + { + "name": "__38-[FIRAuth checkActionCode:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__38-[FIRAuth checkActionCode:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__38-[FIRAuth checkActionCode:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "__38-[FIRAuth checkActionCode:completion:]_block_invoke.470", + "coverage": 1 + }, + { + "name": "-[FIRAuth verifyPasswordResetCode:completion:]", + "coverage": 1 + }, + { + "name": "__46-[FIRAuth verifyPasswordResetCode:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth applyActionCode:completion:]", + "coverage": 1 + }, + { + "name": "__38-[FIRAuth applyActionCode:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__38-[FIRAuth applyActionCode:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__38-[FIRAuth applyActionCode:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth sendPasswordResetWithEmail:completion:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth sendPasswordResetWithEmail:actionCodeSettings:completion:]", + "coverage": 0 + }, + { + "name": "-[FIRAuth sendPasswordResetWithNullableActionCodeSettings:email:completion:]", + "coverage": 1 + }, + { + "name": "__76-[FIRAuth sendPasswordResetWithNullableActionCodeSettings:email:completion:]_block_invoke", + "coverage": 0.8095238095238095 + }, + { + "name": "__76-[FIRAuth sendPasswordResetWithNullableActionCodeSettings:email:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__76-[FIRAuth sendPasswordResetWithNullableActionCodeSettings:email:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth sendSignInLinkToEmail:actionCodeSettings:completion:]", + "coverage": 1 + }, + { + "name": "__63-[FIRAuth sendSignInLinkToEmail:actionCodeSettings:completion:]_block_invoke", + "coverage": 0.75 + }, + { + "name": "__63-[FIRAuth sendSignInLinkToEmail:actionCodeSettings:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__63-[FIRAuth sendSignInLinkToEmail:actionCodeSettings:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRAuth updateCurrentUser:completion:]", + "coverage": 1 + }, + { + "name": "__40-[FIRAuth updateCurrentUser:completion:]_block_invoke", + "coverage": 0.9545454545454546 + }, + { + "name": "__40-[FIRAuth updateCurrentUser:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__40-[FIRAuth updateCurrentUser:completion:]_block_invoke.539", + "coverage": 0.625 + }, + { + "name": "__40-[FIRAuth updateCurrentUser:completion:]_block_invoke_2.540", + "coverage": 0 + }, + { + "name": "__40-[FIRAuth updateCurrentUser:completion:]_block_invoke.544", + "coverage": 1 + }, + { + "name": "__40-[FIRAuth updateCurrentUser:completion:]_block_invoke.556", + "coverage": 1 + }, + { + "name": "__40-[FIRAuth updateCurrentUser:completion:]_block_invoke_2.557", + "coverage": 1 + }, + { + "name": "-[FIRAuth signOut:]", + "coverage": 1 + }, + { + "name": "__19-[FIRAuth signOut:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth signOutByForceWithUserID:error:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRAuth isSignInWithEmailLink:]", + "coverage": 0.76 + }, + { + "name": "FIRAuthParseURL", + "coverage": 1 + }, + { + "name": "-[FIRAuth addAuthStateDidChangeListener:]", + "coverage": 1 + }, + { + "name": "__41-[FIRAuth addAuthStateDidChangeListener:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth removeAuthStateDidChangeListener:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth addIDTokenDidChangeListener:]", + "coverage": 0.8636363636363636 + }, + { + "name": "__39-[FIRAuth addIDTokenDidChangeListener:]_block_invoke", + "coverage": 1 + }, + { + "name": "__39-[FIRAuth addIDTokenDidChangeListener:]_block_invoke.604", + "coverage": 1 + }, + { + "name": "-[FIRAuth removeIDTokenDidChangeListener:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth useAppLanguage]", + "coverage": 0 + }, + { + "name": "__25-[FIRAuth useAppLanguage]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth languageCode]", + "coverage": 0 + }, + { + "name": "-[FIRAuth setLanguageCode:]", + "coverage": 0 + }, + { + "name": "__27-[FIRAuth setLanguageCode:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth additionalFrameworkMarker]", + "coverage": 0 + }, + { + "name": "-[FIRAuth setAdditionalFrameworkMarker:]", + "coverage": 0 + }, + { + "name": "__40-[FIRAuth setAdditionalFrameworkMarker:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth APNSToken]", + "coverage": 0 + }, + { + "name": "__20-[FIRAuth APNSToken]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth setAPNSToken:]", + "coverage": 0 + }, + { + "name": "-[FIRAuth setAPNSToken:type:]", + "coverage": 0 + }, + { + "name": "__29-[FIRAuth setAPNSToken:type:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth handleAPNSTokenError:]", + "coverage": 0 + }, + { + "name": "__32-[FIRAuth handleAPNSTokenError:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth canHandleNotification:]", + "coverage": 0 + }, + { + "name": "__33-[FIRAuth canHandleNotification:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth canHandleURL:]", + "coverage": 0 + }, + { + "name": "__24-[FIRAuth canHandleURL:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuth signInWithPhoneCredential:operation:callback:]", + "coverage": 0.6923076923076923 + }, + { + "name": "-[FIRAuth internalSignInAndRetrieveDataWithCustomToken:completion:]", + "coverage": 1 + }, + { + "name": "__67-[FIRAuth internalSignInAndRetrieveDataWithCustomToken:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__67-[FIRAuth internalSignInAndRetrieveDataWithCustomToken:completion:]_block_invoke_2", + "coverage": 0.7222222222222222 + }, + { + "name": "-[FIRAuth internalCreateUserWithEmail:password:completion:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth internalSignInAnonymouslyWithCompletion:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth possiblyPostAuthStateChangeNotification]", + "coverage": 1 + }, + { + "name": "__50-[FIRAuth possiblyPostAuthStateChangeNotification]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuth updateKeychainWithUser:error:]", + "coverage": 0.8333333333333334 + }, + { + "name": "+[FIRAuth setKeychainServiceNameForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRAuth keychainServiceNameForAppName:]", + "coverage": 1 + }, + { + "name": "+[FIRAuth deleteKeychainServiceNameForAppName:]", + "coverage": 0 + }, + { + "name": "-[FIRAuth scheduleAutoTokenRefresh]", + "coverage": 1 + }, + { + "name": "-[FIRAuth scheduleAutoTokenRefreshWithDelay:retry:]", + "coverage": 1 + }, + { + "name": "__51-[FIRAuth scheduleAutoTokenRefreshWithDelay:retry:]_block_invoke", + "coverage": 0.7878787878787878 + }, + { + "name": "__51-[FIRAuth scheduleAutoTokenRefreshWithDelay:retry:]_block_invoke_2", + "coverage": 0.9375 + }, + { + "name": "-[FIRAuth completeSignInWithAccessToken:accessTokenExpirationDate:refreshToken:anonymous:callback:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInFlowAuthResultCallbackByDecoratingCallback:]", + "coverage": 1 + }, + { + "name": "__60-[FIRAuth signInFlowAuthResultCallbackByDecoratingCallback:]_block_invoke", + "coverage": 0.6956521739130435 + }, + { + "name": "__60-[FIRAuth signInFlowAuthResultCallbackByDecoratingCallback:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__60-[FIRAuth signInFlowAuthResultCallbackByDecoratingCallback:]_block_invoke.765", + "coverage": 0 + }, + { + "name": "__60-[FIRAuth signInFlowAuthResultCallbackByDecoratingCallback:]_block_invoke.769", + "coverage": 1 + }, + { + "name": "-[FIRAuth signInFlowAuthDataResultCallbackByDecoratingCallback:]", + "coverage": 1 + }, + { + "name": "__64-[FIRAuth signInFlowAuthDataResultCallbackByDecoratingCallback:]_block_invoke", + "coverage": 0.6956521739130435 + }, + { + "name": "__64-[FIRAuth signInFlowAuthDataResultCallbackByDecoratingCallback:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__64-[FIRAuth signInFlowAuthDataResultCallbackByDecoratingCallback:]_block_invoke.779", + "coverage": 0 + }, + { + "name": "__64-[FIRAuth signInFlowAuthDataResultCallbackByDecoratingCallback:]_block_invoke.783", + "coverage": 1 + }, + { + "name": "-[FIRAuth updateCurrentUser:byForce:savingToDisk:error:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth saveUser:error:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth getUser:error:]", + "coverage": 0.7727272727272727 + }, + { + "name": "+[FIRAuth componentsToRegister]", + "coverage": 1 + }, + { + "name": "__31+[FIRAuth componentsToRegister]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRAuth configureWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRAuth appWillBeDeleted:]", + "coverage": 0 + }, + { + "name": "__28-[FIRAuth appWillBeDeleted:]_block_invoke", + "coverage": 0 + }, + { + "name": "__28-[FIRAuth appWillBeDeleted:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRAuth getTokenForcingRefresh:withCallback:]", + "coverage": 1 + }, + { + "name": "__47-[FIRAuth getTokenForcingRefresh:withCallback:]_block_invoke", + "coverage": 0.82 + }, + { + "name": "__47-[FIRAuth getTokenForcingRefresh:withCallback:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__47-[FIRAuth getTokenForcingRefresh:withCallback:]_block_invoke.837", + "coverage": 0 + }, + { + "name": "__47-[FIRAuth getTokenForcingRefresh:withCallback:]_block_invoke.841", + "coverage": 1 + }, + { + "name": "__47-[FIRAuth getTokenForcingRefresh:withCallback:]_block_invoke.845", + "coverage": 0 + }, + { + "name": "__47-[FIRAuth getTokenForcingRefresh:withCallback:]_block_invoke_2.846", + "coverage": 0 + }, + { + "name": "-[FIRAuth getUserID]", + "coverage": 1 + } + ] + }, + { + "name": "FIRSecureTokenService.m", + "coverage": 0.8592592592592593, + "type": "objc", + "functions": [ + { + "name": "-[FIRSecureTokenService init]", + "coverage": 1 + }, + { + "name": "-[FIRSecureTokenService initWithRequestConfiguration:authorizationCode:]", + "coverage": 0 + }, + { + "name": "-[FIRSecureTokenService initWithRequestConfiguration:accessToken:accessTokenExpirationDate:refreshToken:]", + "coverage": 1 + }, + { + "name": "-[FIRSecureTokenService fetchAccessTokenForcingRefresh:callback:]", + "coverage": 1 + }, + { + "name": "__65-[FIRSecureTokenService fetchAccessTokenForcingRefresh:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "__65-[FIRSecureTokenService fetchAccessTokenForcingRefresh:callback:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRSecureTokenService rawAccessToken]", + "coverage": 1 + }, + { + "name": "+[FIRSecureTokenService supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRSecureTokenService initWithCoder:]", + "coverage": 0.875 + }, + { + "name": "-[FIRSecureTokenService encodeWithCoder:]", + "coverage": 1 + }, + { + "name": "-[FIRSecureTokenService requestAccessToken:]", + "coverage": 0.8928571428571429 + }, + { + "name": "__44-[FIRSecureTokenService requestAccessToken:]_block_invoke", + "coverage": 0.8125 + }, + { + "name": "-[FIRSecureTokenService hasValidAccessToken]", + "coverage": 1 + } + ] + }, + { + "name": "FIRUserMetadata.m", + "coverage": 0.8636363636363636, + "type": "objc", + "functions": [ + { + "name": "-[FIRUserMetadata initWithCreationDate:lastSignInDate:]", + "coverage": 1 + }, + { + "name": "+[FIRUserMetadata supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRUserMetadata initWithCoder:]", + "coverage": 1 + }, + { + "name": "-[FIRUserMetadata encodeWithCoder:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRGetAccountInfoResponse.m", + "coverage": 0.875, + "type": "objc", + "functions": [ + { + "name": "-[FIRGetAccountInfoResponseProviderUserInfo initWithDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIRGetAccountInfoResponseUser initWithDictionary:]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FIRGetAccountInfoResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRApp.m", + "coverage": 0.8848, + "type": "objc", + "functions": [ + { + "name": "+[FIRApp configure]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithOptions:]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithName:options:]", + "coverage": 0.9333333333333333 + }, + { + "name": "+[FIRApp defaultApp]", + "coverage": 1 + }, + { + "name": "+[FIRApp appNamed:]", + "coverage": 1 + }, + { + "name": "+[FIRApp allApps]", + "coverage": 1 + }, + { + "name": "+[FIRApp resetApps]", + "coverage": 1 + }, + { + "name": "-[FIRApp deleteApp:]", + "coverage": 0.7916666666666666 + }, + { + "name": "+[FIRApp addAppToAppDictionary:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRApp initInstanceWithName:options:]", + "coverage": 1 + }, + { + "name": "-[FIRApp configureCore]", + "coverage": 0.7962962962962963 + }, + { + "name": "-[FIRApp options]", + "coverage": 1 + }, + { + "name": "-[FIRApp setDataCollectionDefaultEnabled:]", + "coverage": 0.9354838709677419 + }, + { + "name": "-[FIRApp isDataCollectionDefaultEnabled]", + "coverage": 1 + }, + { + "name": "+[FIRApp sendNotificationsToSDKs:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForMissingOptions]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForSubspecConfigurationFailureWithDomain:errorCode:service:reason:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForInvalidAppID]", + "coverage": 0 + }, + { + "name": "+[FIRApp isDefaultAppConfigured]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerLibrary:withVersion:]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerInternalLibrary:withName:withVersion:]", + "coverage": 1 + }, + { + "name": "__55+[FIRApp registerInternalLibrary:withName:withVersion:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRApp firebaseUserAgent]", + "coverage": 1 + }, + { + "name": "-[FIRApp checkExpectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp isAppIDValid]", + "coverage": 1 + }, + { + "name": "+[FIRApp validateAppID:]", + "coverage": 0.9473684210526315 + }, + { + "name": "+[FIRApp actualBundleID]", + "coverage": 0 + }, + { + "name": "+[FIRApp validateAppIDFormat:withVersion:]", + "coverage": 0.8450704225352113 + }, + { + "name": "+[FIRApp validateAppIDFingerprint:withVersion:]", + "coverage": 0.8620689655172413 + }, + { + "name": "-[FIRApp expectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp clearDataCollectionSwitchFromUserDefaults]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromUserDefaultsForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromPlist]", + "coverage": 1 + }, + { + "name": "__43+[FIRApp readDataCollectionSwitchFromPlist]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRApp sendLogsWithServiceName:version:error:]", + "coverage": 0.2631578947368421 + } + ] + }, + { + "name": "FIRAuthBackend.m", + "coverage": 0.9025755879059351, + "type": "objc", + "functions": [ + { + "name": "+[FIRAuthBackend implementation]", + "coverage": 0.6666666666666666 + }, + { + "name": "+[FIRAuthBackend setBackendImplementation:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend createAuthURI:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend getAccountInfo:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend getProjectConfig:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend setAccountInfo:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend verifyAssertion:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend verifyCustomToken:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend verifyPassword:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend emailLinkSignin:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend secureToken:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend getOOBConfirmationCode:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend signUpNewUser:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend deleteAccount:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend signInWithGameCenter:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend sendVerificationCode:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend verifyPhoneNumber:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend verifyClient:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend resetPassword:callback:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthBackend authUserAgent]", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCIssuerImplementation init]", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCIssuerImplementation asyncPostToURLWithRequestConfiguration:URL:body:contentType:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[FIRAuthBackendRPCImplementation init]", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation createAuthURI:callback:]", + "coverage": 1 + }, + { + "name": "__58-[FIRAuthBackendRPCImplementation createAuthURI:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation getAccountInfo:callback:]", + "coverage": 1 + }, + { + "name": "__59-[FIRAuthBackendRPCImplementation getAccountInfo:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation getProjectConfig:callback:]", + "coverage": 1 + }, + { + "name": "__61-[FIRAuthBackendRPCImplementation getProjectConfig:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation setAccountInfo:callback:]", + "coverage": 1 + }, + { + "name": "__59-[FIRAuthBackendRPCImplementation setAccountInfo:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation verifyAssertion:callback:]", + "coverage": 1 + }, + { + "name": "__60-[FIRAuthBackendRPCImplementation verifyAssertion:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation verifyCustomToken:callback:]", + "coverage": 1 + }, + { + "name": "__62-[FIRAuthBackendRPCImplementation verifyCustomToken:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation verifyPassword:callback:]", + "coverage": 1 + }, + { + "name": "__59-[FIRAuthBackendRPCImplementation verifyPassword:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation emailLinkSignin:callback:]", + "coverage": 1 + }, + { + "name": "__60-[FIRAuthBackendRPCImplementation emailLinkSignin:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation secureToken:callback:]", + "coverage": 0 + }, + { + "name": "__56-[FIRAuthBackendRPCImplementation secureToken:callback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRAuthBackendRPCImplementation getOOBConfirmationCode:callback:]", + "coverage": 1 + }, + { + "name": "__67-[FIRAuthBackendRPCImplementation getOOBConfirmationCode:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation signUpNewUser:callback:]", + "coverage": 1 + }, + { + "name": "__58-[FIRAuthBackendRPCImplementation signUpNewUser:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation deleteAccount:callback:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation sendVerificationCode:callback:]", + "coverage": 1 + }, + { + "name": "__65-[FIRAuthBackendRPCImplementation sendVerificationCode:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation verifyPhoneNumber:callback:]", + "coverage": 1 + }, + { + "name": "__62-[FIRAuthBackendRPCImplementation verifyPhoneNumber:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation verifyClient:callback:]", + "coverage": 1 + }, + { + "name": "__57-[FIRAuthBackendRPCImplementation verifyClient:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation resetPassword:callback:]", + "coverage": 1 + }, + { + "name": "__58-[FIRAuthBackendRPCImplementation resetPassword:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthBackendRPCImplementation signInWithGameCenter:callback:]", + "coverage": 1 + }, + { + "name": "__65-[FIRAuthBackendRPCImplementation signInWithGameCenter:callback:]_block_invoke", + "coverage": 0.7272727272727273 + }, + { + "name": "-[FIRAuthBackendRPCImplementation postWithRequest:response:callback:]", + "coverage": 0.9624060150375939 + }, + { + "name": "__69-[FIRAuthBackendRPCImplementation postWithRequest:response:callback:]_block_invoke", + "coverage": 0.9148936170212766 + }, + { + "name": "+[FIRAuthBackendRPCImplementation clientErrorWithServerErrorMessage:errorDictionary:response:]", + "coverage": 0.8870292887029289 + } + ] + }, + { + "name": "FIRAuthErrorUtils.m", + "coverage": 0.9050736497545008, + "type": "objc", + "functions": [ + { + "name": "FIRAuthErrorDescription", + "coverage": 0.9047619047619048 + }, + { + "name": "FIRAuthErrorCodeString", + "coverage": 0.9444444444444444 + }, + { + "name": "+[FIRAuthErrorUtils errorWithCode:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils errorWithCode:message:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils errorWithCode:underlyingError:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils errorWithCode:userInfo:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils RPCRequestEncodingErrorWithUnderlyingError:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils JSONSerializationErrorForUnencodableType]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils JSONSerializationErrorWithUnderlyingError:]", + "coverage": 0 + }, + { + "name": "+[FIRAuthErrorUtils networkErrorWithUnderlyingError:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils unexpectedErrorResponseWithData:underlyingError:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils unexpectedErrorResponseWithDeserializedResponse:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils malformedJWTErrorWithToken:underlyingError:]", + "coverage": 0 + }, + { + "name": "+[FIRAuthErrorUtils unexpectedResponseWithData:underlyingError:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:underlyingError:]", + "coverage": 0.8 + }, + { + "name": "+[FIRAuthErrorUtils RPCResponseDecodingErrorWithDeserializedResponse:underlyingError:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils emailAlreadyInUseErrorWithEmail:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils userDisabledErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils wrongPasswordErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils tooManyRequestsErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidCustomTokenErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils customTokenMistmatchErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidCredentialErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils requiresRecentLoginErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidUserTokenErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidEmailErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils accountExistsWithDifferentCredentialErrorWithEmail:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils providerAlreadyLinkedError]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils noSuchProviderError]", + "coverage": 0 + }, + { + "name": "+[FIRAuthErrorUtils userTokenExpiredErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils userNotFoundErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidAPIKeyError]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils userMismatchError]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils credentialAlreadyInUseErrorWithMessage:credential:email:]", + "coverage": 0.8947368421052632 + }, + { + "name": "+[FIRAuthErrorUtils operationNotAllowedErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils appNotAuthorizedError]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils expiredActionCodeErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidActionCodeErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidMessagePayloadErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidSenderErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidRecipientEmailErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingIosBundleIDErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingAndroidPackageNameErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils unauthorizedDomainErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidContinueURIErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingContinueURIErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingEmailErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingPhoneNumberErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidPhoneNumberErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingVerificationCodeErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidVerificationCodeErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingVerificationIDErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidVerificationIDErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils sessionExpiredErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingAppCredentialWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidAppCredentialWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils quotaExceededErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils missingAppTokenErrorWithUnderlyingError:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils localPlayerNotAuthenticatedError]", + "coverage": 0 + }, + { + "name": "+[FIRAuthErrorUtils gameKitNotLinkedError]", + "coverage": 0 + }, + { + "name": "+[FIRAuthErrorUtils notificationNotForwardedError]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils appNotVerifiedErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils captchaCheckFailedErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils webContextAlreadyPresentedErrorWithMessage:]", + "coverage": 0 + }, + { + "name": "+[FIRAuthErrorUtils webContextCancelledErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils appVerificationUserInteractionFailureWithReason:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils webSignInUserInteractionFailureWithReason:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils URLResponseErrorWithCode:message:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils nullUserErrorWithMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthErrorUtils invalidDynamicLinkDomainErrorWithMessage:]", + "coverage": 0 + }, + { + "name": "+[FIRAuthErrorUtils keychainErrorWithFunction:status:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthAppCredential.m", + "coverage": 0.9130434782608695, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthAppCredential initWithReceipt:secret:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthAppCredential supportsSecureCoding]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppCredential initWithCoder:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAuthAppCredential encodeWithCoder:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAdditionalUserInfo.m", + "coverage": 0.925, + "type": "objc", + "functions": [ + { + "name": "+[FIRAdditionalUserInfo userInfoWithVerifyAssertionResponse:]", + "coverage": 1 + }, + { + "name": "-[FIRAdditionalUserInfo initWithProviderID:profile:username:isNewUser:]", + "coverage": 1 + }, + { + "name": "+[FIRAdditionalUserInfo supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRAdditionalUserInfo initWithCoder:]", + "coverage": 1 + }, + { + "name": "-[FIRAdditionalUserInfo encodeWithCoder:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRVerifyPhoneNumberRequest.m", + "coverage": 0.9272727272727272, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyPhoneNumberRequest initWithTemporaryProof:phoneNumber:operation:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRVerifyPhoneNumberRequest initWithVerificationID:verificationCode:operation:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "FIRAuthOperationString", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRVerifyPhoneNumberRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRUserInfoImpl.m", + "coverage": 0.9387755102040817, + "type": "objc", + "functions": [ + { + "name": "+[FIRUserInfoImpl userInfoWithGetAccountInfoResponseProviderUserInfo:]", + "coverage": 1 + }, + { + "name": "-[FIRUserInfoImpl initWithProviderID:userID:displayName:photoURL:email:phoneNumber:]", + "coverage": 1 + }, + { + "name": "+[FIRUserInfoImpl supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[FIRUserInfoImpl initWithCoder:]", + "coverage": 1 + }, + { + "name": "-[FIRUserInfoImpl encodeWithCoder:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthNotificationManager.m", + "coverage": 0.9391304347826087, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthNotificationManager initWithApplication:appCredentialManager:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthNotificationManager checkNotificationForwardingWithCallback:]", + "coverage": 1 + }, + { + "name": "__70-[FIRAuthNotificationManager checkNotificationForwardingWithCallback:]_block_invoke", + "coverage": 0.8260869565217391 + }, + { + "name": "__70-[FIRAuthNotificationManager checkNotificationForwardingWithCallback:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__70-[FIRAuthNotificationManager checkNotificationForwardingWithCallback:]_block_invoke.33", + "coverage": 1 + }, + { + "name": "-[FIRAuthNotificationManager canHandleNotification:]", + "coverage": 0.9310344827586207 + }, + { + "name": "-[FIRAuthNotificationManager callBack]", + "coverage": 1 + } + ] + }, + { + "name": "FIROptions.m", + "coverage": 0.9489795918367347, + "type": "objc", + "functions": [ + { + "name": "+[FIROptions defaultOptions]", + "coverage": 0.8461538461538461 + }, + { + "name": "+[FIROptions initialize]", + "coverage": 1 + }, + { + "name": "+[FIROptions defaultOptionsDictionary]", + "coverage": 1 + }, + { + "name": "+[FIROptions plistFilePathWithName:]", + "coverage": 1 + }, + { + "name": "+[FIROptions resetDefaultOptions]", + "coverage": 1 + }, + { + "name": "-[FIROptions initInternalWithOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithContentsOfFile:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithGoogleAppID:GCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions APIKey]", + "coverage": 1 + }, + { + "name": "-[FIROptions checkEditingLocked]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAPIKey:]", + "coverage": 1 + }, + { + "name": "-[FIROptions clientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions trackingID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setTrackingID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions GCMSenderID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions projectID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setProjectID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions androidClientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAndroidClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions googleAppID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGoogleAppID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions libraryVersionID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setLibraryVersionID:]", + "coverage": 0 + }, + { + "name": "-[FIROptions databaseURL]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDatabaseURL:]", + "coverage": 1 + }, + { + "name": "-[FIROptions storageBucket]", + "coverage": 1 + }, + { + "name": "-[FIROptions setStorageBucket:]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDeepLinkURLScheme:]", + "coverage": 1 + }, + { + "name": "-[FIROptions bundleID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionaryWithInfoDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIROptions isMeasurementEnabled]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIROptions isAnalyticsCollectionExpicitlySet]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionEnabled]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionDeactivated]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsEnabled]", + "coverage": 0 + }, + { + "name": "-[FIROptions isSignInEnabled]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAuthWebUtils.m", + "coverage": 0.9507042253521126, + "type": "objc", + "functions": [ + { + "name": "+[FIRAuthWebUtils randomStringWithLength:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthWebUtils isCallbackSchemeRegisteredForCustomURLScheme:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthWebUtils isExpectedCallbackURL:eventID:authType:callbackScheme:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthWebUtils fetchAuthDomainWithRequestConfiguration:completion:]", + "coverage": 1 + }, + { + "name": "__70+[FIRAuthWebUtils fetchAuthDomainWithRequestConfiguration:completion:]_block_invoke", + "coverage": 0.7142857142857143 + }, + { + "name": "+[FIRAuthWebUtils queryItemValue:from:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthWebUtils dictionaryWithHttpArgumentsString:]", + "coverage": 0.9642857142857143 + }, + { + "name": "+[FIRAuthWebUtils stringByUnescapingFromURLArgument:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAnalyticsConfiguration.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "+[FIRAnalyticsConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__43+[FIRAnalyticsConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration postNotificationName:value:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAnalyticsConfiguration setMinimumSessionInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setSessionTimeoutInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:persistSetting:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRPhoneAuthProvider.m", + "coverage": 0.9618320610687023, + "type": "objc", + "functions": [ + { + "name": "-[FIRPhoneAuthProvider initWithAuth:]", + "coverage": 1 + }, + { + "name": "-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]", + "coverage": 1 + }, + { + "name": "__64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "__64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke.33", + "coverage": 1 + }, + { + "name": "__64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke_2.42", + "coverage": 0.9302325581395349 + }, + { + "name": "__64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke_3.43", + "coverage": 1 + }, + { + "name": "__64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke.52", + "coverage": 1 + }, + { + "name": "__64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke_2.63", + "coverage": 0.5714285714285714 + }, + { + "name": "-[FIRPhoneAuthProvider credentialWithVerificationID:verificationCode:]", + "coverage": 1 + }, + { + "name": "+[FIRPhoneAuthProvider provider]", + "coverage": 1 + }, + { + "name": "+[FIRPhoneAuthProvider providerWithAuth:]", + "coverage": 1 + }, + { + "name": "-[FIRPhoneAuthProvider reCAPTCHATokenForURL:error:]", + "coverage": 0.8717948717948718 + }, + { + "name": "-[FIRPhoneAuthProvider internalVerifyPhoneNumber:completion:]", + "coverage": 1 + }, + { + "name": "__61-[FIRPhoneAuthProvider internalVerifyPhoneNumber:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__61-[FIRPhoneAuthProvider internalVerifyPhoneNumber:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRPhoneAuthProvider verifyClientAndSendVerificationCodeToPhoneNumber:retryOnInvalidAppCredential:callback:]", + "coverage": 1 + }, + { + "name": "__110-[FIRPhoneAuthProvider verifyClientAndSendVerificationCodeToPhoneNumber:retryOnInvalidAppCredential:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "__110-[FIRPhoneAuthProvider verifyClientAndSendVerificationCodeToPhoneNumber:retryOnInvalidAppCredential:callback:]_block_invoke.163", + "coverage": 1 + }, + { + "name": "__110-[FIRPhoneAuthProvider verifyClientAndSendVerificationCodeToPhoneNumber:retryOnInvalidAppCredential:callback:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRPhoneAuthProvider verifyClientWithCompletion:]", + "coverage": 1 + }, + { + "name": "__51-[FIRPhoneAuthProvider verifyClientWithCompletion:]_block_invoke", + "coverage": 1 + }, + { + "name": "__51-[FIRPhoneAuthProvider verifyClientWithCompletion:]_block_invoke_2", + "coverage": 0.8333333333333334 + }, + { + "name": "__51-[FIRPhoneAuthProvider verifyClientWithCompletion:]_block_invoke_3", + "coverage": 0.5 + }, + { + "name": "-[FIRPhoneAuthProvider reCAPTCHAURLWithEventID:completion:]", + "coverage": 1 + }, + { + "name": "__59-[FIRPhoneAuthProvider reCAPTCHAURLWithEventID:completion:]_block_invoke", + "coverage": 0.7586206896551724 + } + ] + }, + { + "name": "FIRVerifyAssertionRequest.m", + "coverage": 0.9710144927536232, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyAssertionRequest initWithProviderID:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRVerifyAssertionRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 0.9655172413793104 + } + ] + }, + { + "name": "FIRComponentContainer.m", + "coverage": 0.9818181818181818, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:]", + "coverage": 1 + }, + { + "name": "__55+[FIRComponentContainer registerAsComponentRegistrant:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:inSet:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:registrants:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer populateComponentsFromRegisteredClasses:forApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer instantiateInstanceForProtocol:withBlock:]", + "coverage": 0.9285714285714286 + }, + { + "name": "-[FIRComponentContainer instanceForProtocol:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer removeAllCachedInstances]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthAppDelegateProxy.m", + "coverage": 0.9820359281437125, + "type": "objc", + "functions": [ + { + "name": "noop", + "coverage": 1 + }, + { + "name": "isIOS9orLater", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRAuthAppDelegateProxy initWithApplication:]", + "coverage": 1 + }, + { + "name": "__47-[FIRAuthAppDelegateProxy initWithApplication:]_block_invoke", + "coverage": 1 + }, + { + "name": "__47-[FIRAuthAppDelegateProxy initWithApplication:]_block_invoke.167", + "coverage": 1 + }, + { + "name": "__47-[FIRAuthAppDelegateProxy initWithApplication:]_block_invoke.177", + "coverage": 1 + }, + { + "name": "__47-[FIRAuthAppDelegateProxy initWithApplication:]_block_invoke.184", + "coverage": 1 + }, + { + "name": "__47-[FIRAuthAppDelegateProxy initWithApplication:]_block_invoke.194", + "coverage": 1 + }, + { + "name": "__47-[FIRAuthAppDelegateProxy initWithApplication:]_block_invoke.201", + "coverage": 1 + }, + { + "name": "__47-[FIRAuthAppDelegateProxy initWithApplication:]_block_invoke.208", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy addHandler:]", + "coverage": 1 + }, + { + "name": "+[FIRAuthAppDelegateProxy sharedInstance]", + "coverage": 1 + }, + { + "name": "__41+[FIRAuthAppDelegateProxy sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy object:selector:application:didRegisterForRemoteNotificationsWithDeviceToken:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy object:selector:application:didFailToRegisterForRemoteNotificationsWithError:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy object:selector:application:didReceiveRemoteNotification:fetchCompletionHandler:]", + "coverage": 0.9411764705882353 + }, + { + "name": "-[FIRAuthAppDelegateProxy object:selector:application:didReceiveRemoteNotification:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy object:selector:application:openURL:options:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy object:selector:application:openURL:sourceApplication:annotation:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRAuthAppDelegateProxy object:selector:application:handleOpenURL:]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIRAuthAppDelegateProxy delegateCanHandleURL:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy handlers]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy replaceSelector:withBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppDelegateProxy originalImplementationForSelector:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRVerifyAssertionResponse.m", + "coverage": 0.9827586206896551, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyAssertionResponse setWithDictionary:error:]", + "coverage": 0.9827586206896551 + } + ] + }, + { + "name": "FIRGetOOBConfirmationCodeRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRGetOOBConfirmationCodeRequest requestTypeStringValueForRequestType:]", + "coverage": 1 + }, + { + "name": "+[FIRGetOOBConfirmationCodeRequest passwordResetRequestWithEmail:actionCodeSettings:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "+[FIRGetOOBConfirmationCodeRequest verifyEmailRequestWithAccessToken:actionCodeSettings:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "+[FIRGetOOBConfirmationCodeRequest signInWithEmailLinkRequest:actionCodeSettings:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRGetOOBConfirmationCodeRequest initWithRequestType:email:accessToken:actionCodeSettings:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRGetOOBConfirmationCodeRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRGetOOBConfirmationCodeResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRGetOOBConfirmationCodeResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRGetProjectConfigRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRGetProjectConfigRequest initWithRequestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRGetProjectConfigRequest containsPostBody]", + "coverage": 1 + } + ] + }, + { + "name": "NSData+FIRBase64.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[NSData(FIRBase64) fir_base64URLEncodedStringWithOptions:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRResetPasswordRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRResetPasswordRequest initWithOobCode:newPassword:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRResetPasswordRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRResetPasswordResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRResetPasswordResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthUserDefaultsStorage.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthUserDefaultsStorage initWithService:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthUserDefaultsStorage dataForKey:error:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthUserDefaultsStorage setData:forKey:error:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthUserDefaultsStorage removeDataForKey:error:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthUserDefaultsStorage clear]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthSettings.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthSettings init]", + "coverage": 1 + } + ] + }, + { + "name": "FIRSendVerificationCodeRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRSendVerificationCodeRequest initWithPhoneNumber:appCredential:reCAPTCHAToken:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRSendVerificationCodeRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthSerialTaskQueue.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthSerialTaskQueue init]", + "coverage": 1 + }, + { + "name": "-[FIRAuthSerialTaskQueue enqueueTask:]", + "coverage": 1 + }, + { + "name": "__38-[FIRAuthSerialTaskQueue enqueueTask:]_block_invoke", + "coverage": 1 + }, + { + "name": "__38-[FIRAuthSerialTaskQueue enqueueTask:]_block_invoke_2", + "coverage": 1 + } + ] + }, + { + "name": "FIRSetAccountInfoRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRSetAccountInfoRequest initWithRequestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRSetAccountInfoRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRSetAccountInfoResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRSetAccountInfoResponseProviderUserInfo initWithDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIRSetAccountInfoResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRSignInWithGameCenterRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRSignInWithGameCenterRequest initWithPlayerID:publicKeyURL:signature:salt:timestamp:displayName:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRSignInWithGameCenterRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRSignInWithGameCenterResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRSignInWithGameCenterResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRSignUpNewUserRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRSignUpNewUserRequest initWithEmail:password:displayName:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRSignUpNewUserRequest initWithRequestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRSignUpNewUserRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRSignUpNewUserResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRSignUpNewUserResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRVerifyClientRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyClientRequest initWithAppToken:isSandbox:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRVerifyClientRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRCreateAuthURIResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRCreateAuthURIResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRVerifyCustomTokenRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyCustomTokenRequest initWithToken:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRVerifyCustomTokenRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRVerifyCustomTokenResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyCustomTokenResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRVerifyPasswordRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyPasswordRequest initWithEmail:password:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRVerifyPasswordRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRVerifyPasswordResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyPasswordResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthGlobalWorkQueue.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "FIRAuthGlobalWorkQueue", + "coverage": 1 + }, + { + "name": "__FIRAuthGlobalWorkQueue_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthDispatcher.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRAuthDispatcher sharedInstance]", + "coverage": 1 + }, + { + "name": "__35+[FIRAuthDispatcher sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthDispatcher dispatchAfterDelay:queue:task:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAppAssociationRegistration.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRAppAssociationRegistration registeredObjectWithHost:key:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthAppCredentialManager.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthAppCredentialManager initWithKeychain:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppCredentialManager maximumNumberOfPendingReceipts]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppCredentialManager didStartVerificationWithReceipt:timeout:callback:]", + "coverage": 1 + }, + { + "name": "__80-[FIRAuthAppCredentialManager didStartVerificationWithReceipt:timeout:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppCredentialManager canFinishVerificationWithReceipt:secret:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppCredentialManager clearCredential]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppCredentialManager saveData]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAppCredentialManager callBackWithReceipt:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponent.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponent componentWithProtocol:creationBlock:]", + "coverage": 1 + }, + { + "name": "+[FIRComponent componentWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRComponent initWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentType.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentType instanceForProtocol:inContainer:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthAPNSToken.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthAPNSToken initWithData:type:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthAPNSToken string]", + "coverage": 1 + } + ] + }, + { + "name": "FIRActionCodeSettings.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRActionCodeSettings init]", + "coverage": 1 + }, + { + "name": "-[FIRActionCodeSettings setIOSBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIRActionCodeSettings setAndroidPackageName:installIfNotAvailable:minimumVersion:]", + "coverage": 1 + } + ] + }, + { + "name": "main.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "main", + "coverage": 1 + } + ] + }, + { + "name": "FIRVerifyClientResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRVerifyClientResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRDeleteAccountRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRDeleteAccountRequest initWitLocalID:accessToken:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRDeleteAccountRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRDeleteAccountResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRDeleteAccountResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIREmailLinkSignInRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIREmailLinkSignInRequest initWithEmail:oobCode:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIREmailLinkSignInRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIREmailLinkSignInResponse.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIREmailLinkSignInResponse setWithDictionary:error:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRGetAccountInfoRequest.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRGetAccountInfoRequest initWithAccessToken:requestConfiguration:]", + "coverage": 1 + }, + { + "name": "-[FIRGetAccountInfoRequest unencodedHTTPRequestBodyWithError:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthRequestConfiguration.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthRequestConfiguration initWithAPIKey:]", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "Core_Example_iOS.app", + "coverage": 0.8861788617886179, + "files": [ + { + "name": "FIRAppDelegate.m", + "coverage": 0.26666666666666666, + "type": "objc", + "functions": [ + { + "name": "-[FIRAppDelegate application:didFinishLaunchingWithOptions:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillResignActive:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidEnterBackground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationWillEnterForeground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidBecomeActive:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillTerminate:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRViewController.m", + "coverage": 0.5, + "type": "objc", + "functions": [ + { + "name": "-[FIRViewController viewDidLoad]", + "coverage": 1 + }, + { + "name": "-[FIRViewController didReceiveMemoryWarning]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDependency.m", + "coverage": 0.7857142857142857, + "type": "objc", + "functions": [ + { + "name": "+[FIRDependency dependencyWithProtocol:]", + "coverage": 0 + }, + { + "name": "+[FIRDependency dependencyWithProtocol:isRequired:]", + "coverage": 1 + }, + { + "name": "-[FIRDependency initWithProtocol:isRequired:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRBundleUtil.m", + "coverage": 0.7872340425531915, + "type": "objc", + "functions": [ + { + "name": "+[FIRBundleUtil relevantBundles]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil optionsDictionaryPathWithResourceName:andFileType:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil relevantURLSchemes]", + "coverage": 0 + }, + { + "name": "+[FIRBundleUtil hasBundleIdentifierPrefix:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil bundleIdentifierByRemovingLastPartFrom:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRConfiguration.m", + "coverage": 0.8076923076923077, + "type": "objc", + "functions": [ + { + "name": "+[FIRConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__34+[FIRConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration init]", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration setLoggerLevel:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRLogger.m", + "coverage": 0.8118811881188119, + "type": "objc", + "functions": [ + { + "name": "FIRLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__FIRLoggerInitializeASL_block_invoke", + "coverage": 0.9333333333333333 + }, + { + "name": "FIRSetAnalyticsDebugMode", + "coverage": 0 + }, + { + "name": "FIRSetLoggerLevel", + "coverage": 0 + }, + { + "name": "FIRResetLogger", + "coverage": 1 + }, + { + "name": "FIRSetLoggerUserDefaults", + "coverage": 1 + }, + { + "name": "FIRIsLoggableLevel", + "coverage": 0 + }, + { + "name": "FIRLogBasic", + "coverage": 1 + }, + { + "name": "Definition at 158:95", + "coverage": 1 + }, + { + "name": "+[FIRLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRApp.m", + "coverage": 0.8848, + "type": "objc", + "functions": [ + { + "name": "+[FIRApp configure]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithOptions:]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithName:options:]", + "coverage": 0.9333333333333333 + }, + { + "name": "+[FIRApp defaultApp]", + "coverage": 1 + }, + { + "name": "+[FIRApp appNamed:]", + "coverage": 1 + }, + { + "name": "+[FIRApp allApps]", + "coverage": 1 + }, + { + "name": "+[FIRApp resetApps]", + "coverage": 1 + }, + { + "name": "-[FIRApp deleteApp:]", + "coverage": 0.7916666666666666 + }, + { + "name": "+[FIRApp addAppToAppDictionary:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRApp initInstanceWithName:options:]", + "coverage": 1 + }, + { + "name": "-[FIRApp configureCore]", + "coverage": 0.7962962962962963 + }, + { + "name": "-[FIRApp options]", + "coverage": 1 + }, + { + "name": "-[FIRApp setDataCollectionDefaultEnabled:]", + "coverage": 0.9354838709677419 + }, + { + "name": "-[FIRApp isDataCollectionDefaultEnabled]", + "coverage": 1 + }, + { + "name": "+[FIRApp sendNotificationsToSDKs:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForMissingOptions]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForSubspecConfigurationFailureWithDomain:errorCode:service:reason:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForInvalidAppID]", + "coverage": 0 + }, + { + "name": "+[FIRApp isDefaultAppConfigured]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerLibrary:withVersion:]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerInternalLibrary:withName:withVersion:]", + "coverage": 1 + }, + { + "name": "__55+[FIRApp registerInternalLibrary:withName:withVersion:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRApp firebaseUserAgent]", + "coverage": 1 + }, + { + "name": "-[FIRApp checkExpectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp isAppIDValid]", + "coverage": 1 + }, + { + "name": "+[FIRApp validateAppID:]", + "coverage": 0.9473684210526315 + }, + { + "name": "+[FIRApp actualBundleID]", + "coverage": 0 + }, + { + "name": "+[FIRApp validateAppIDFormat:withVersion:]", + "coverage": 0.8450704225352113 + }, + { + "name": "+[FIRApp validateAppIDFingerprint:withVersion:]", + "coverage": 0.8620689655172413 + }, + { + "name": "-[FIRApp expectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp clearDataCollectionSwitchFromUserDefaults]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromUserDefaultsForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromPlist]", + "coverage": 1 + }, + { + "name": "__43+[FIRApp readDataCollectionSwitchFromPlist]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRApp sendLogsWithServiceName:version:error:]", + "coverage": 0.2631578947368421 + } + ] + }, + { + "name": "FIROptions.m", + "coverage": 0.9489795918367347, + "type": "objc", + "functions": [ + { + "name": "+[FIROptions defaultOptions]", + "coverage": 0.8461538461538461 + }, + { + "name": "+[FIROptions initialize]", + "coverage": 1 + }, + { + "name": "+[FIROptions defaultOptionsDictionary]", + "coverage": 1 + }, + { + "name": "+[FIROptions plistFilePathWithName:]", + "coverage": 1 + }, + { + "name": "+[FIROptions resetDefaultOptions]", + "coverage": 1 + }, + { + "name": "-[FIROptions initInternalWithOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithContentsOfFile:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithGoogleAppID:GCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions APIKey]", + "coverage": 1 + }, + { + "name": "-[FIROptions checkEditingLocked]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAPIKey:]", + "coverage": 1 + }, + { + "name": "-[FIROptions clientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions trackingID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setTrackingID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions GCMSenderID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions projectID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setProjectID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions androidClientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAndroidClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions googleAppID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGoogleAppID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions libraryVersionID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setLibraryVersionID:]", + "coverage": 0 + }, + { + "name": "-[FIROptions databaseURL]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDatabaseURL:]", + "coverage": 1 + }, + { + "name": "-[FIROptions storageBucket]", + "coverage": 1 + }, + { + "name": "-[FIROptions setStorageBucket:]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDeepLinkURLScheme:]", + "coverage": 1 + }, + { + "name": "-[FIROptions bundleID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionaryWithInfoDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIROptions isMeasurementEnabled]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIROptions isAnalyticsCollectionExpicitlySet]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionEnabled]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionDeactivated]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsEnabled]", + "coverage": 0 + }, + { + "name": "-[FIROptions isSignInEnabled]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAnalyticsConfiguration.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "+[FIRAnalyticsConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__43+[FIRAnalyticsConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration postNotificationName:value:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAnalyticsConfiguration setMinimumSessionInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setSessionTimeoutInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:persistSetting:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentContainer.m", + "coverage": 0.9818181818181818, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:]", + "coverage": 1 + }, + { + "name": "__55+[FIRComponentContainer registerAsComponentRegistrant:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:inSet:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:registrants:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer populateComponentsFromRegisteredClasses:forApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer instantiateInstanceForProtocol:withBlock:]", + "coverage": 0.9285714285714286 + }, + { + "name": "-[FIRComponentContainer instanceForProtocol:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer removeAllCachedInstances]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentType.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentType instanceForProtocol:inContainer:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponent.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponent componentWithProtocol:creationBlock:]", + "coverage": 1 + }, + { + "name": "+[FIRComponent componentWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRComponent initWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAppAssociationRegistration.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRAppAssociationRegistration registeredObjectWithHost:key:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "main.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "main", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "Database_Example_iOS.app", + "coverage": 0.6189063408958697, + "files": [ + { + "name": "FTupleStringNode.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FTupleStringNode initWithString:andNode:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRNoopAuthTokenProvider.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FIRNoopAuthTokenProvider fetchTokenForcingRefresh:withCallback:]", + "coverage": 0 + }, + { + "name": "__66-[FIRNoopAuthTokenProvider fetchTokenForcingRefresh:withCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRNoopAuthTokenProvider listenForTokenChanges:]", + "coverage": 0 + } + ] + }, + { + "name": "FPendingPut.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FPendingPut initWithPath:andData:andPriority:]", + "coverage": 0 + }, + { + "name": "-[FPendingPut encodeWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FPendingPut initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FPendingPutPriority initWithPath:andPriority:]", + "coverage": 0 + }, + { + "name": "-[FPendingPutPriority encodeWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FPendingPutPriority initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FPendingUpdate initWithPath:andData:]", + "coverage": 0 + }, + { + "name": "-[FPendingUpdate encodeWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FPendingUpdate initWithCoder:]", + "coverage": 0 + } + ] + }, + { + "name": "FListenComplete.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FListenComplete initWithSource:path:]", + "coverage": 0 + }, + { + "name": "-[FListenComplete operationForChild:]", + "coverage": 0 + }, + { + "name": "-[FListenComplete description]", + "coverage": 0 + } + ] + }, + { + "name": "FIRServerValue.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[FIRServerValue timestamp]", + "coverage": 0 + } + ] + }, + { + "name": "FValueEventRegistration.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FValueEventRegistration initWithRepo:handle:callback:cancelCallback:]", + "coverage": 0 + }, + { + "name": "-[FValueEventRegistration responseTo:]", + "coverage": 0 + }, + { + "name": "-[FValueEventRegistration createEventFrom:query:]", + "coverage": 0 + }, + { + "name": "-[FValueEventRegistration fireEvent:queue:]", + "coverage": 0 + }, + { + "name": "__43-[FValueEventRegistration fireEvent:queue:]_block_invoke", + "coverage": 0 + }, + { + "name": "__43-[FValueEventRegistration fireEvent:queue:]_block_invoke.57", + "coverage": 0 + }, + { + "name": "-[FValueEventRegistration createCancelEventFromError:path:]", + "coverage": 0 + }, + { + "name": "-[FValueEventRegistration matches:]", + "coverage": 0 + } + ] + }, + { + "name": "FKeepSyncedEventRegistration.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[FKeepSyncedEventRegistration instance]", + "coverage": 0 + }, + { + "name": "__40+[FKeepSyncedEventRegistration instance]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FKeepSyncedEventRegistration responseTo:]", + "coverage": 0 + }, + { + "name": "-[FKeepSyncedEventRegistration createEventFrom:query:]", + "coverage": 0 + }, + { + "name": "-[FKeepSyncedEventRegistration fireEvent:queue:]", + "coverage": 0 + }, + { + "name": "-[FKeepSyncedEventRegistration createCancelEventFromError:path:]", + "coverage": 0 + }, + { + "name": "-[FKeepSyncedEventRegistration handle]", + "coverage": 0 + }, + { + "name": "-[FKeepSyncedEventRegistration matches:]", + "coverage": 0 + } + ] + }, + { + "name": "FChildEventRegistration.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FChildEventRegistration initWithRepo:handle:callbacks:cancelCallback:]", + "coverage": 0 + }, + { + "name": "-[FChildEventRegistration responseTo:]", + "coverage": 0 + }, + { + "name": "-[FChildEventRegistration createEventFrom:query:]", + "coverage": 0 + }, + { + "name": "-[FChildEventRegistration fireEvent:queue:]", + "coverage": 0 + }, + { + "name": "__43-[FChildEventRegistration fireEvent:queue:]_block_invoke", + "coverage": 0 + }, + { + "name": "__43-[FChildEventRegistration fireEvent:queue:]_block_invoke.68", + "coverage": 0 + }, + { + "name": "-[FChildEventRegistration createCancelEventFromError:path:]", + "coverage": 0 + }, + { + "name": "-[FChildEventRegistration matches:]", + "coverage": 0 + } + ] + }, + { + "name": "FCancelEvent.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FCancelEvent initWithEventRegistration:error:path:]", + "coverage": 0 + }, + { + "name": "-[FCancelEvent fireEventOnQueue:]", + "coverage": 0 + }, + { + "name": "-[FCancelEvent isCancelEvent]", + "coverage": 0 + }, + { + "name": "-[FCancelEvent description]", + "coverage": 0 + } + ] + }, + { + "name": "FEventEmitter.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FEventEmitter initWithAllowedEvents:queue:]", + "coverage": 0 + }, + { + "name": "-[FEventEmitter getInitialEventForType:]", + "coverage": 0 + }, + { + "name": "-[FEventEmitter triggerEventType:data:]", + "coverage": 0 + }, + { + "name": "-[FEventEmitter triggerListener:withData:]", + "coverage": 0 + }, + { + "name": "__42-[FEventEmitter triggerListener:withData:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FEventEmitter observeEventType:withBlock:]", + "coverage": 0 + }, + { + "name": "__44-[FEventEmitter observeEventType:withBlock:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FEventEmitter addEventListener:forEventType:]", + "coverage": 0 + }, + { + "name": "-[FEventEmitter removeObserverForEventType:withHandle:]", + "coverage": 0 + }, + { + "name": "__55-[FEventEmitter removeObserverForEventType:withHandle:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FEventEmitter removeEventListenerWithHandle:forEventType:]", + "coverage": 0 + }, + { + "name": "-[FEventEmitter validateEventType:]", + "coverage": 0 + } + ] + }, + { + "name": "FNextPushId.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[FNextPushId get:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRTransactionResult.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[FIRTransactionResult successWithValue:]", + "coverage": 0 + }, + { + "name": "+[FIRTransactionResult abort]", + "coverage": 0 + } + ] + }, + { + "name": "FTupleNodePath.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FTupleNodePath initWithNode:andPath:]", + "coverage": 0 + } + ] + }, + { + "name": "FTupleObjectNode.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FTupleObjectNode initWithObject:andNode:]", + "coverage": 0 + } + ] + }, + { + "name": "FTupleUserCallback.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FTupleUserCallback initWithHandle:]", + "coverage": 0 + } + ] + }, + { + "name": "FTupleSetIdPath.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FTupleSetIdPath initWithSetId:andPath:]", + "coverage": 0 + } + ] + }, + { + "name": "FTupleTransaction.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FTupleTransaction setAbortStatus:reason:]", + "coverage": 0 + }, + { + "name": "-[FTupleTransaction abortError]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDatabaseQuery.m", + "coverage": 0.05921052631578947, + "type": "objc", + "functions": [ + { + "name": "+[FIRDatabaseQuery sharedQueue]", + "coverage": 1 + }, + { + "name": "__31+[FIRDatabaseQuery sharedQueue]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRDatabaseQuery initWithRepo:path:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery initWithRepo:path:params:orderByCalled:priorityMethodCalled:]", + "coverage": 0.7647058823529411 + }, + { + "name": "-[FIRDatabaseQuery querySpec]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery validateQueryEndpointsForParams:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery validateEqualToCall]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery validateNoPreviousOrderByCalled]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery validateIndexValueType:fromMethod:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryStartingAtValue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryStartingAtValue:childKey:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryStartingAtInternal:childKey:from:priorityMethod:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryEndingAtValue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryEndingAtValue:childKey:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryEndingAtInternal:childKey:from:priorityMethod:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryEqualToValue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryEqualToValue:childKey:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryEqualToInternal:childKey:from:priorityMethod:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery validateLimitRange:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryLimitedToFirst:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryLimitedToLast:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryOrderedByChild:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryOrderedByKey]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryOrderedByValue]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery queryOrderedByPriority]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeEventType:withBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeEventType:andPreviousSiblingKeyWithBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeEventType:withBlock:withCancelBlock:]", + "coverage": 0 + }, + { + "name": "__63-[FIRDatabaseQuery observeEventType:withBlock:withCancelBlock:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeEventType:andPreviousSiblingKeyWithBlock:withCancelBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeValueEventWithHandle:withBlock:cancelCallback:]", + "coverage": 0 + }, + { + "name": "__73-[FIRDatabaseQuery observeValueEventWithHandle:withBlock:cancelCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeChildEventWithHandle:withCallbacks:cancelCallback:]", + "coverage": 0 + }, + { + "name": "__77-[FIRDatabaseQuery observeChildEventWithHandle:withCallbacks:cancelCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery removeObserverWithHandle:]", + "coverage": 0 + }, + { + "name": "__45-[FIRDatabaseQuery removeObserverWithHandle:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery removeAllObservers]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery keepSynced:]", + "coverage": 0 + }, + { + "name": "__31-[FIRDatabaseQuery keepSynced:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeSingleEventOfType:withBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeSingleEventOfType:andPreviousSiblingKeyWithBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeSingleEventOfType:withBlock:withCancelBlock:]", + "coverage": 0 + }, + { + "name": "__71-[FIRDatabaseQuery observeSingleEventOfType:withBlock:withCancelBlock:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery observeSingleEventOfType:andPreviousSiblingKeyWithBlock:withCancelBlock:]", + "coverage": 0 + }, + { + "name": "__92-[FIRDatabaseQuery observeSingleEventOfType:andPreviousSiblingKeyWithBlock:withCancelBlock:]_block_invoke", + "coverage": 0 + }, + { + "name": "__92-[FIRDatabaseQuery observeSingleEventOfType:andPreviousSiblingKeyWithBlock:withCancelBlock:]_block_invoke.278", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery description]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseQuery ref]", + "coverage": 0 + } + ] + }, + { + "name": "FTree.m", + "coverage": 0.0625, + "type": "objc", + "functions": [ + { + "name": "-[FTree init]", + "coverage": 1 + }, + { + "name": "-[FTree initWithName:withParent:withNode:]", + "coverage": 0 + }, + { + "name": "-[FTree subTree:]", + "coverage": 0 + }, + { + "name": "-[FTree getValue]", + "coverage": 0 + }, + { + "name": "-[FTree setValue:]", + "coverage": 0 + }, + { + "name": "-[FTree clear]", + "coverage": 0 + }, + { + "name": "-[FTree hasChildren]", + "coverage": 0 + }, + { + "name": "-[FTree isEmpty]", + "coverage": 0 + }, + { + "name": "-[FTree forEachChild:]", + "coverage": 0 + }, + { + "name": "-[FTree forEachChildMutationSafe:]", + "coverage": 0 + }, + { + "name": "-[FTree forEachDescendant:]", + "coverage": 0 + }, + { + "name": "-[FTree forEachDescendant:includeSelf:childrenFirst:]", + "coverage": 0 + }, + { + "name": "__53-[FTree forEachDescendant:includeSelf:childrenFirst:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FTree forEachAncestor:]", + "coverage": 0 + }, + { + "name": "-[FTree forEachAncestor:includeSelf:]", + "coverage": 0 + }, + { + "name": "-[FTree forEachImmediateDescendantWithValue:]", + "coverage": 0 + }, + { + "name": "__45-[FTree forEachImmediateDescendantWithValue:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FTree valueExistsAtOrAbove:]", + "coverage": 0 + }, + { + "name": "-[FTree path]", + "coverage": 0 + }, + { + "name": "-[FTree updateParents]", + "coverage": 0 + }, + { + "name": "-[FTree updateChild:withNode:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDatabaseReference.m", + "coverage": 0.0912280701754386, + "type": "objc", + "functions": [ + { + "name": "-[FIRDatabaseReference initWithConfig:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference initWithRepo:path:]", + "coverage": 1 + }, + { + "name": "-[FIRDatabaseReference key]", + "coverage": 1 + }, + { + "name": "-[FIRDatabaseReference database]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference parent]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference URL]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference description]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference root]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference childByAppendingPath:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference child:]", + "coverage": 1 + }, + { + "name": "-[FIRDatabaseReference childByAutoId]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference setValue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference setValue:withCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference setValue:andPriority:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference setValue:andPriority:withCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference setValueInternal:andPriority:withCompletionBlock:from:]", + "coverage": 0 + }, + { + "name": "__78-[FIRDatabaseReference setValueInternal:andPriority:withCompletionBlock:from:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference removeValue]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference removeValueWithCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference setPriority:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference setPriority:withCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference setPriorityInternal:withCompletionBlock:from:]", + "coverage": 0 + }, + { + "name": "__69-[FIRDatabaseReference setPriorityInternal:withCompletionBlock:from:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference updateChildValues:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference updateChildValues:withCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference updateChildValuesInternal:withCompletionBlock:from:]", + "coverage": 0 + }, + { + "name": "__75-[FIRDatabaseReference updateChildValuesInternal:withCompletionBlock:from:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectSetValue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectSetValue:withCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectSetValue:andPriority:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectSetValue:andPriority:withCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectSetValueInternal:andPriority:withCompletionBlock:from:]", + "coverage": 0 + }, + { + "name": "__90-[FIRDatabaseReference onDisconnectSetValueInternal:andPriority:withCompletionBlock:from:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectRemoveValue]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectRemoveValueWithCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectUpdateChildValues:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectUpdateChildValues:withCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference onDisconnectUpdateChildValuesInternal:withCompletionBlock:from:]", + "coverage": 0 + }, + { + "name": "__87-[FIRDatabaseReference onDisconnectUpdateChildValuesInternal:withCompletionBlock:from:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference cancelDisconnectOperations]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference cancelDisconnectOperationsWithCompletionBlock:]", + "coverage": 0 + }, + { + "name": "__70-[FIRDatabaseReference cancelDisconnectOperationsWithCompletionBlock:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FIRDatabaseReference goOffline]", + "coverage": 0 + }, + { + "name": "+[FIRDatabaseReference goOnline]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference observeEventType:withBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference observeEventType:andPreviousSiblingKeyWithBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference observeEventType:withBlock:withCancelBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference observeEventType:andPreviousSiblingKeyWithBlock:withCancelBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference removeObserverWithHandle:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference removeAllObservers]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference keepSynced:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference observeSingleEventOfType:withBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference observeSingleEventOfType:andPreviousSiblingKeyWithBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference observeSingleEventOfType:withBlock:withCancelBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference observeSingleEventOfType:andPreviousSiblingKeyWithBlock:withCancelBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryLimitedToFirst:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryLimitedToLast:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryOrderedByChild:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryOrderedByKey]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryOrderedByPriority]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryStartingAtValue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryStartingAtValue:childKey:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryEndingAtValue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryEndingAtValue:childKey:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryEqualToValue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference queryEqualToValue:childKey:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference runTransactionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference runTransactionBlock:andCompletionBlock:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseReference runTransactionBlock:andCompletionBlock:withLocalEvents:]", + "coverage": 0 + }, + { + "name": "__79-[FIRDatabaseReference runTransactionBlock:andCompletionBlock:withLocalEvents:]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "FRepo.m", + "coverage": 0.14897260273972604, + "type": "objc", + "functions": [ + { + "name": "-[FRepo initWithRepoInfo:config:database:]", + "coverage": 1 + }, + { + "name": "__42-[FRepo initWithRepoInfo:config:database:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FRepo deferredInit]", + "coverage": 0.8089887640449438 + }, + { + "name": "__21-[FRepo deferredInit]_block_invoke", + "coverage": 0 + }, + { + "name": "__21-[FRepo deferredInit]_block_invoke.99", + "coverage": 0 + }, + { + "name": "__21-[FRepo deferredInit]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__21-[FRepo deferredInit]_block_invoke.128", + "coverage": 0 + }, + { + "name": "__21-[FRepo deferredInit]_block_invoke_2.138", + "coverage": 0 + }, + { + "name": "__21-[FRepo deferredInit]_block_invoke_3", + "coverage": 0 + }, + { + "name": "__21-[FRepo deferredInit]_block_invoke.149", + "coverage": 0 + }, + { + "name": "-[FRepo restoreWrites]", + "coverage": 1 + }, + { + "name": "__22-[FRepo restoreWrites]_block_invoke", + "coverage": 0 + }, + { + "name": "__22-[FRepo restoreWrites]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FRepo name]", + "coverage": 0 + }, + { + "name": "-[FRepo description]", + "coverage": 0 + }, + { + "name": "-[FRepo interrupt]", + "coverage": 0 + }, + { + "name": "-[FRepo resume]", + "coverage": 0 + }, + { + "name": "-[FRepo dispose]", + "coverage": 0 + }, + { + "name": "-[FRepo nextWriteId]", + "coverage": 0 + }, + { + "name": "-[FRepo serverTime]", + "coverage": 0 + }, + { + "name": "-[FRepo set:withNode:withCallback:]", + "coverage": 0 + }, + { + "name": "__35-[FRepo set:withNode:withCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo update:withNodes:withCallback:]", + "coverage": 0 + }, + { + "name": "__39-[FRepo update:withNodes:withCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "__39-[FRepo update:withNodes:withCallback:]_block_invoke.278", + "coverage": 0 + }, + { + "name": "-[FRepo onDisconnectCancel:withCallback:]", + "coverage": 0 + }, + { + "name": "__41-[FRepo onDisconnectCancel:withCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo onDisconnectSet:withNode:withCallback:]", + "coverage": 0 + }, + { + "name": "__47-[FRepo onDisconnectSet:withNode:withCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo onDisconnectUpdate:withNodes:withCallback:]", + "coverage": 0 + }, + { + "name": "__51-[FRepo onDisconnectUpdate:withNodes:withCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "__51-[FRepo onDisconnectUpdate:withNodes:withCallback:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FRepo purgeOutstandingWrites]", + "coverage": 0 + }, + { + "name": "-[FRepo addEventRegistration:forQuery:]", + "coverage": 0 + }, + { + "name": "-[FRepo removeEventRegistration:forQuery:]", + "coverage": 0 + }, + { + "name": "-[FRepo keepQuery:synced:]", + "coverage": 0 + }, + { + "name": "-[FRepo updateInfo:withValue:]", + "coverage": 0.7692307692307693 + }, + { + "name": "-[FRepo callOnComplete:withStatus:errorReason:andPath:]", + "coverage": 0 + }, + { + "name": "__55-[FRepo callOnComplete:withStatus:errorReason:andPath:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo ackWrite:rerunTransactionsAtPath:status:]", + "coverage": 0 + }, + { + "name": "-[FRepo warnIfWriteFailedAtPath:status:message:]", + "coverage": 0 + }, + { + "name": "-[FRepo onDataUpdate:forPath:message:isMerge:tagId:]", + "coverage": 0 + }, + { + "name": "-[FRepo onRangeMerge:forPath:tagId:]", + "coverage": 0 + }, + { + "name": "-[FRepo onConnect:]", + "coverage": 0 + }, + { + "name": "-[FRepo onDisconnect:]", + "coverage": 1 + }, + { + "name": "-[FRepo onServerInfoUpdate:updates:]", + "coverage": 0 + }, + { + "name": "-[FRepo setupNotifications]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FRepo didEnterBackground]", + "coverage": 0 + }, + { + "name": "__27-[FRepo didEnterBackground]_block_invoke", + "coverage": 0 + }, + { + "name": "__27-[FRepo didEnterBackground]_block_invoke.466", + "coverage": 0 + }, + { + "name": "-[FRepo runOnDisconnectEvents]", + "coverage": 1 + }, + { + "name": "__30-[FRepo runOnDisconnectEvents]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo dumpListens]", + "coverage": 0 + }, + { + "name": "-[FRepo initTransactions]", + "coverage": 1 + }, + { + "name": "-[FRepo startTransactionOnPath:update:onComplete:withLocalEvents:]", + "coverage": 0 + }, + { + "name": "__66-[FRepo startTransactionOnPath:update:onComplete:withLocalEvents:]_block_invoke", + "coverage": 0 + }, + { + "name": "__66-[FRepo startTransactionOnPath:update:onComplete:withLocalEvents:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__66-[FRepo startTransactionOnPath:update:onComplete:withLocalEvents:]_block_invoke.571", + "coverage": 0 + }, + { + "name": "-[FRepo latestStateAtPath:excludeWriteIds:]", + "coverage": 0 + }, + { + "name": "-[FRepo sendAllReadyTransactions]", + "coverage": 0 + }, + { + "name": "-[FRepo sendReadyTransactionsForTree:]", + "coverage": 0 + }, + { + "name": "__38-[FRepo sendReadyTransactionsForTree:]_block_invoke", + "coverage": 0 + }, + { + "name": "__38-[FRepo sendReadyTransactionsForTree:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FRepo sendTransactionQueue:atPath:]", + "coverage": 0 + }, + { + "name": "__37-[FRepo sendTransactionQueue:atPath:]_block_invoke", + "coverage": 0 + }, + { + "name": "__37-[FRepo sendTransactionQueue:atPath:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FRepo rerunTransactionsForPath:]", + "coverage": 0 + }, + { + "name": "-[FRepo rerunTransactionQueue:atPath:]", + "coverage": 0 + }, + { + "name": "__38-[FRepo rerunTransactionQueue:atPath:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo getAncestorTransactionNodeForPath:]", + "coverage": 0 + }, + { + "name": "-[FRepo buildTransactionQueueAtNode:]", + "coverage": 0 + }, + { + "name": "__37-[FRepo buildTransactionQueueAtNode:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo aggregateTransactionQueuesForNode:andQueue:]", + "coverage": 0 + }, + { + "name": "__52-[FRepo aggregateTransactionQueuesForNode:andQueue:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo pruneCompletedTransactionsBelowNode:]", + "coverage": 0 + }, + { + "name": "__45-[FRepo pruneCompletedTransactionsBelowNode:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FRepo abortTransactionsAtPath:error:]", + "coverage": 0 + }, + { + "name": "__39-[FRepo abortTransactionsAtPath:error:]_block_invoke", + "coverage": 0 + }, + { + "name": "__39-[FRepo abortTransactionsAtPath:error:]_block_invoke.715", + "coverage": 0 + }, + { + "name": "-[FRepo abortTransactionsAtNode:error:]", + "coverage": 0 + }, + { + "name": "__39-[FRepo abortTransactionsAtNode:error:]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "FCachePolicy.m", + "coverage": 0.17647058823529413, + "type": "objc", + "functions": [ + { + "name": "-[FLRUCachePolicy initWithMaxSize:]", + "coverage": 0 + }, + { + "name": "-[FLRUCachePolicy shouldPruneCacheWithSize:numberOfTrackedQueries:]", + "coverage": 0 + }, + { + "name": "-[FLRUCachePolicy shouldCheckCacheSize:]", + "coverage": 0 + }, + { + "name": "-[FLRUCachePolicy percentOfQueriesToPruneAtOnce]", + "coverage": 0 + }, + { + "name": "-[FLRUCachePolicy maxNumberOfQueriesToKeep]", + "coverage": 0 + }, + { + "name": "+[FNoCachePolicy noCachePolicy]", + "coverage": 1 + }, + { + "name": "-[FNoCachePolicy shouldPruneCacheWithSize:numberOfTrackedQueries:]", + "coverage": 0 + }, + { + "name": "-[FNoCachePolicy shouldCheckCacheSize:]", + "coverage": 1 + }, + { + "name": "-[FNoCachePolicy percentOfQueriesToPruneAtOnce]", + "coverage": 0 + }, + { + "name": "-[FNoCachePolicy maxNumberOfQueriesToKeep]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDatabase.m", + "coverage": 0.1986754966887417, + "type": "objc", + "functions": [ + { + "name": "+[FIRDatabase database]", + "coverage": 0 + }, + { + "name": "+[FIRDatabase databaseWithURL:]", + "coverage": 0 + }, + { + "name": "+[FIRDatabase databaseForApp:]", + "coverage": 0 + }, + { + "name": "+[FIRDatabase databaseForApp:URL:]", + "coverage": 0 + }, + { + "name": "+[FIRDatabase buildVersion]", + "coverage": 1 + }, + { + "name": "+[FIRDatabase createDatabaseForTests:config:]", + "coverage": 1 + }, + { + "name": "+[FIRDatabase sdkVersion]", + "coverage": 1 + }, + { + "name": "+[FIRDatabase setLoggingEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRDatabase initWithApp:repoInfo:config:]", + "coverage": 1 + }, + { + "name": "-[FIRDatabase reference]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase referenceWithPath:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase referenceFromURL:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase purgeOutstandingWrites]", + "coverage": 0 + }, + { + "name": "__37-[FIRDatabase purgeOutstandingWrites]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabase goOnline]", + "coverage": 0 + }, + { + "name": "__23-[FIRDatabase goOnline]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabase goOffline]", + "coverage": 0 + }, + { + "name": "__24-[FIRDatabase goOffline]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabase setPersistenceEnabled:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase persistenceEnabled]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase setPersistenceCacheSizeBytes:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase persistenceCacheSizeBytes]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase setCallbackQueue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase callbackQueue]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase assertUnfrozen:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabase ensureRepo]", + "coverage": 1 + } + ] + }, + { + "name": "FIRDatabaseComponent.m", + "coverage": 0.2, + "type": "objc", + "functions": [ + { + "name": "-[FIRDatabaseComponent initWithApp:]", + "coverage": 0 + }, + { + "name": "+[FIRDatabaseComponent load]", + "coverage": 1 + }, + { + "name": "+[FIRDatabaseComponent componentsToRegister]", + "coverage": 1 + }, + { + "name": "__44+[FIRDatabaseComponent componentsToRegister]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseComponent appWillBeDeleted:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseComponent databaseForApp:URL:]", + "coverage": 0 + } + ] + }, + { + "name": "FPersistentConnection.m", + "coverage": 0.23169398907103825, + "type": "objc", + "functions": [ + { + "name": "-[FPersistentConnection initWithRepoInfo:dispatchQueue:config:]", + "coverage": 1 + }, + { + "name": "-[FPersistentConnection dealloc]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection open]", + "coverage": 1 + }, + { + "name": "-[FPersistentConnection listen:tagId:hash:onComplete:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection putData:forPath:withHash:withCallback:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection mergeData:forPath:withCallback:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onDisconnectPutData:forPath:withCallback:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onDisconnectMergeData:forPath:withCallback:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onDisconnectCancelPath:withCallback:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection unlisten:tagId:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection refreshAuthToken:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection connected]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection canSendWrites]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onReady:atTime:sessionID:]", + "coverage": 0 + }, + { + "name": "__50-[FPersistentConnection onReady:atTime:sessionID:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onDataMessage:withMessage:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onDisconnect:withReason:]", + "coverage": 0.88 + }, + { + "name": "-[FPersistentConnection onKill:withReason:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection interruptForReason:]", + "coverage": 0.8 + }, + { + "name": "-[FPersistentConnection resumeForReason:]", + "coverage": 1 + }, + { + "name": "-[FPersistentConnection shouldReconnect]", + "coverage": 1 + }, + { + "name": "-[FPersistentConnection isInterruptedForReason:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection tryScheduleReconnect]", + "coverage": 1 + }, + { + "name": "__45-[FPersistentConnection tryScheduleReconnect]_block_invoke", + "coverage": 1 + }, + { + "name": "__45-[FPersistentConnection tryScheduleReconnect]_block_invoke_2", + "coverage": 0.5909090909090909 + }, + { + "name": "-[FPersistentConnection openNetworkConnectionWithToken:]", + "coverage": 1 + }, + { + "name": "reachabilityCallback", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection enteringForeground]", + "coverage": 0 + }, + { + "name": "__43-[FPersistentConnection enteringForeground]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection setupNotifications]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FPersistentConnection sendAuthAndRestoreStateAfterComplete:]", + "coverage": 0 + }, + { + "name": "__62-[FPersistentConnection sendAuthAndRestoreStateAfterComplete:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection sendUnauth]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onAuthRevokedWithStatus:andReason:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onListenRevoked:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection sendOnDisconnectAction:forPath:withData:andCallback:]", + "coverage": 0 + }, + { + "name": "__77-[FPersistentConnection sendOnDisconnectAction:forPath:withData:andCallback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection sendPut:]", + "coverage": 0 + }, + { + "name": "__33-[FPersistentConnection sendPut:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection sendUnlisten:queryParams:tagId:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection putInternal:forAction:forPath:withHash:withCallback:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection sendListen:]", + "coverage": 0 + }, + { + "name": "__36-[FPersistentConnection sendListen:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection warnOnListenWarningsForQuery:payload:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection getNextRequestNumber]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection sendAction:body:sensitive:callback:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection cancelSentTransactions]", + "coverage": 0.6666666666666666 + }, + { + "name": "__47-[FPersistentConnection cancelSentTransactions]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection onDataPushWithAction:andBody:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection restoreAuth]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection restoreState]", + "coverage": 0 + }, + { + "name": "__37-[FPersistentConnection restoreState]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection removeListen:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection removeAllListensAtPath:]", + "coverage": 0 + }, + { + "name": "__48-[FPersistentConnection removeAllListensAtPath:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection purgeOutstandingWrites]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection ackPuts]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection handleTimestamp:]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection sendStats:]", + "coverage": 0 + }, + { + "name": "__35-[FPersistentConnection sendStats:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection sendConnectStats]", + "coverage": 0 + }, + { + "name": "-[FPersistentConnection dumpListens]", + "coverage": 0 + } + ] + }, + { + "name": "FConnection.m", + "coverage": 0.2826086956521739, + "type": "objc", + "functions": [ + { + "name": "-[FConnection initWith:andDispatchQueue:lastSessionID:]", + "coverage": 1 + }, + { + "name": "-[FConnection open]", + "coverage": 1 + }, + { + "name": "-[FConnection closeWithReason:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FConnection close]", + "coverage": 1 + }, + { + "name": "-[FConnection sendRequest:sensitive:]", + "coverage": 0 + }, + { + "name": "-[FConnection sendData:sensitive:]", + "coverage": 0 + }, + { + "name": "-[FConnection onDisconnect:wasEverConnected:]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FConnection onMessage:withMessage:]", + "coverage": 0 + }, + { + "name": "-[FConnection onDataMessage:]", + "coverage": 0 + }, + { + "name": "-[FConnection onControl:]", + "coverage": 0 + }, + { + "name": "-[FConnection onConnectionShutdownWithReason:]", + "coverage": 0 + }, + { + "name": "-[FConnection onHandshake:]", + "coverage": 0 + }, + { + "name": "-[FConnection onConnection:readyAtTime:sessionID:]", + "coverage": 0 + }, + { + "name": "-[FConnection onReset:]", + "coverage": 0 + } + ] + }, + { + "name": "fbase64.c", + "coverage": 0.2891566265060241, + "type": "objc", + "functions": [ + { + "name": "f_b64_ntop", + "coverage": 0.9411764705882353 + }, + { + "name": "f_b64_pton", + "coverage": 0 + } + ] + }, + { + "name": "FImmutableSortedDictionary.m", + "coverage": 0.3103448275862069, + "type": "objc", + "functions": [ + { + "name": "+[FImmutableSortedDictionary dictionaryWithComparator:]", + "coverage": 1 + }, + { + "name": "+[FImmutableSortedDictionary fromDictionary:withComparator:]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedDictionary insertKey:withValue:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary removeKey:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary get:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary getPredecessorKey:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary isEmpty]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary count]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary minKey]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary maxKey]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary enumerateKeysAndObjectsReverse:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary contains:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary keyEnumerator]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary keyEnumeratorFrom:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary reverseKeyEnumerator]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary reverseKeyEnumeratorFrom:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary isEqual:]", + "coverage": 0.75 + }, + { + "name": "__38-[FImmutableSortedDictionary isEqual:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedDictionary hash]", + "coverage": 0 + }, + { + "name": "__34-[FImmutableSortedDictionary hash]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary description]", + "coverage": 0 + }, + { + "name": "__41-[FImmutableSortedDictionary description]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedDictionary setObject:forKey:]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedDictionary removeObjectForKey:]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedDictionary objectForKey:]", + "coverage": 1 + } + ] + }, + { + "name": "FRepoManager.m", + "coverage": 0.3157894736842105, + "type": "objc", + "functions": [ + { + "name": "+[FRepoManager configs]", + "coverage": 1 + }, + { + "name": "__23+[FRepoManager configs]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FRepoManager getRepo:config:]", + "coverage": 1 + }, + { + "name": "+[FRepoManager createRepo:config:database:]", + "coverage": 0.8571428571428571 + }, + { + "name": "+[FRepoManager interrupt:]", + "coverage": 0 + }, + { + "name": "__26+[FRepoManager interrupt:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FRepoManager interruptAll]", + "coverage": 0 + }, + { + "name": "__28+[FRepoManager interruptAll]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FRepoManager resume:]", + "coverage": 0 + }, + { + "name": "__23+[FRepoManager resume:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FRepoManager resumeAll]", + "coverage": 0 + }, + { + "name": "__25+[FRepoManager resumeAll]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FRepoManager disposeRepos:]", + "coverage": 0 + }, + { + "name": "__29+[FRepoManager disposeRepos:]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "FDataEvent.m", + "coverage": 0.3225806451612903, + "type": "objc", + "functions": [ + { + "name": "-[FDataEvent initWithEventType:eventRegistration:dataSnapshot:]", + "coverage": 0 + }, + { + "name": "-[FDataEvent initWithEventType:eventRegistration:dataSnapshot:prevName:]", + "coverage": 1 + }, + { + "name": "-[FDataEvent path]", + "coverage": 0 + }, + { + "name": "-[FDataEvent fireEventOnQueue:]", + "coverage": 0 + }, + { + "name": "-[FDataEvent isCancelEvent]", + "coverage": 0 + }, + { + "name": "-[FDataEvent description]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAppDelegate.m", + "coverage": 0.3333333333333333, + "type": "objc", + "functions": [ + { + "name": "-[FIRAppDelegate application:didFinishLaunchingWithOptions:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillResignActive:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidEnterBackground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationWillEnterForeground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidBecomeActive:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillTerminate:]", + "coverage": 0 + } + ] + }, + { + "name": "db.h", + "coverage": 0.3333333333333333, + "type": "objc", + "functions": [ + { + "name": "leveldb::Range::Range()", + "coverage": 0 + }, + { + "name": "leveldb::Range::Range(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::DB::DB()", + "coverage": 1 + } + ] + }, + { + "name": "FValidation.m", + "coverage": 0.33852140077821014, + "type": "objc", + "functions": [ + { + "name": "+[FValidation validateFrom:writablePath:]", + "coverage": 0 + }, + { + "name": "+[FValidation validateFrom:knownEventType:]", + "coverage": 0 + }, + { + "name": "+[FValidation isValidPathString:]", + "coverage": 1 + }, + { + "name": "__33+[FValidation isValidPathString:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FValidation validateFrom:validPathString:]", + "coverage": 0.6 + }, + { + "name": "+[FValidation validateFrom:validRootPathString:]", + "coverage": 0.8571428571428571 + }, + { + "name": "__48+[FValidation validateFrom:validRootPathString:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FValidation isValidKey:]", + "coverage": 1 + }, + { + "name": "__26+[FValidation isValidKey:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FValidation validateFrom:validKey:]", + "coverage": 0 + }, + { + "name": "+[FValidation validateFrom:validURL:]", + "coverage": 0 + }, + { + "name": "+[FValidation stringNonempty:]", + "coverage": 0 + }, + { + "name": "+[FValidation validateToken:]", + "coverage": 0 + }, + { + "name": "+[FValidation handleError:withUserCallback:]", + "coverage": 0 + }, + { + "name": "+[FValidation handleError:withSuccessCallback:]", + "coverage": 0 + }, + { + "name": "+[FValidation validateFrom:isValidLeafValue:withPath:]", + "coverage": 0.40816326530612246 + }, + { + "name": "+[FValidation parseAndValidateKey:fromFunction:path:]", + "coverage": 0.4 + }, + { + "name": "+[FValidation validateFrom:validDictionaryKey:withPath:]", + "coverage": 0.4 + }, + { + "name": "+[FValidation validateFrom:validUpdateDictionaryKey:withValue:]", + "coverage": 0 + }, + { + "name": "__63+[FValidation validateFrom:validUpdateDictionaryKey:withValue:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FValidation validateFrom:isValidPriorityValue:withPath:]", + "coverage": 1 + }, + { + "name": "+[FValidation validatePriorityValue:]", + "coverage": 0 + }, + { + "name": "+[FValidation validateFrom:isValidPriorityValue:withPath:throwError:]", + "coverage": 0.23076923076923078 + } + ] + }, + { + "name": "FEventRaiser.m", + "coverage": 0.3448275862068966, + "type": "objc", + "functions": [ + { + "name": "-[FEventRaiser init]", + "coverage": 0 + }, + { + "name": "-[FEventRaiser initWithQueue:]", + "coverage": 1 + }, + { + "name": "-[FEventRaiser raiseEvents:]", + "coverage": 0.6 + }, + { + "name": "-[FEventRaiser raiseCallback:]", + "coverage": 0 + }, + { + "name": "-[FEventRaiser raiseCallbacks:]", + "coverage": 0 + }, + { + "name": "+[FEventRaiser raiseCallbacks:queue:]", + "coverage": 0 + } + ] + }, + { + "name": "FWebSocketConnection.m", + "coverage": 0.35944700460829493, + "type": "objc", + "functions": [ + { + "name": "-[FWebSocketConnection initWith:andQueue:lastSessionID:]", + "coverage": 1 + }, + { + "name": "-[FWebSocketConnection userAgent]", + "coverage": 0.8709677419354839 + }, + { + "name": "-[FWebSocketConnection buffering]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection open]", + "coverage": 1 + }, + { + "name": "__28-[FWebSocketConnection open]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection close]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection start]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection send:]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection nop:]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection handleNewFrameCount:]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection extractFrameCount:]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection appendFrame:]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection handleIncomingFrame:]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection webSocket:didReceiveMessage:]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection webSocketDidOpen:]", + "coverage": 0 + }, + { + "name": "__41-[FWebSocketConnection webSocketDidOpen:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection webSocket:didFailWithError:]", + "coverage": 1 + }, + { + "name": "-[FWebSocketConnection webSocket:didCloseWithCode:reason:wasClean:]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection closeIfNeverConnected]", + "coverage": 0 + }, + { + "name": "-[FWebSocketConnection shutdown]", + "coverage": 1 + }, + { + "name": "-[FWebSocketConnection onClosed]", + "coverage": 0.8 + }, + { + "name": "-[FWebSocketConnection resetKeepAlive]", + "coverage": 0 + } + ] + }, + { + "name": "FSRWebSocket.m", + "coverage": 0.39594843462246776, + "type": "objc", + "functions": [ + { + "name": "newSHA1String", + "coverage": 0 + }, + { + "name": "-[NSData(FSRWebSocket) stringBySHA1ThenBase64Encoding]", + "coverage": 0 + }, + { + "name": "-[NSString(FSRWebSocket) stringBySHA1ThenBase64Encoding]", + "coverage": 0 + }, + { + "name": "+[FSRWebSocket initialize]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket initWithURLRequest:protocols:queue:andUserAgent:]", + "coverage": 0.8928571428571429 + }, + { + "name": "-[FSRWebSocket initWithURLRequest:protocols:]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket initWithURLRequest:queue:andUserAgent:]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket initWithURLRequest:]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket initWithURL:]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket initWithURL:protocols:]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _SR_commonInit]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket assertOnWorkQueue]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket dealloc]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket setReadyState:]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket open]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _performDelegateBlock:]", + "coverage": 0.875 + }, + { + "name": "-[FSRWebSocket setDelegateDispatchQueue:]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _checkHandshake:]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _HTTPHeadersDidFinish]", + "coverage": 0.23684210526315788 + }, + { + "name": "__37-[FSRWebSocket _HTTPHeadersDidFinish]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _readHTTPHeader]", + "coverage": 1 + }, + { + "name": "__31-[FSRWebSocket _readHTTPHeader]_block_invoke", + "coverage": 0.8 + }, + { + "name": "-[FSRWebSocket didConnect]", + "coverage": 0.9512195121951219 + }, + { + "name": "__26-[FSRWebSocket didConnect]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _initializeStreams]", + "coverage": 0.6818181818181818 + }, + { + "name": "-[FSRWebSocket _connect]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket scheduleInRunLoop:forMode:]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket unscheduleFromRunLoop:forMode:]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket close]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket closeWithCode:reason:]", + "coverage": 0 + }, + { + "name": "__37-[FSRWebSocket closeWithCode:reason:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _closeWithProtocolError:]", + "coverage": 0 + }, + { + "name": "__40-[FSRWebSocket _closeWithProtocolError:]_block_invoke", + "coverage": 0 + }, + { + "name": "__40-[FSRWebSocket _closeWithProtocolError:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _failWithError:]", + "coverage": 1 + }, + { + "name": "__31-[FSRWebSocket _failWithError:]_block_invoke", + "coverage": 1 + }, + { + "name": "__31-[FSRWebSocket _failWithError:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _writeData:]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FSRWebSocket send:]", + "coverage": 0 + }, + { + "name": "__21-[FSRWebSocket send:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket handlePing:]", + "coverage": 0 + }, + { + "name": "__27-[FSRWebSocket handlePing:]_block_invoke", + "coverage": 0 + }, + { + "name": "__27-[FSRWebSocket handlePing:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket handlePong]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _handleMessage:]", + "coverage": 0 + }, + { + "name": "__31-[FSRWebSocket _handleMessage:]_block_invoke", + "coverage": 0 + }, + { + "name": "closeCodeIsValid", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket handleCloseWithData:]", + "coverage": 0 + }, + { + "name": "__36-[FSRWebSocket handleCloseWithData:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _disconnect]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _handleFrameWithData:opCode:]", + "coverage": 0 + }, + { + "name": "__44-[FSRWebSocket _handleFrameWithData:opCode:]_block_invoke", + "coverage": 0 + }, + { + "name": "__44-[FSRWebSocket _handleFrameWithData:opCode:]_block_invoke.320", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _handleFrameHeader:curData:]", + "coverage": 0 + }, + { + "name": "__43-[FSRWebSocket _handleFrameHeader:curData:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _readFrameContinue]", + "coverage": 0 + }, + { + "name": "__34-[FSRWebSocket _readFrameContinue]_block_invoke", + "coverage": 0 + }, + { + "name": "__34-[FSRWebSocket _readFrameContinue]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _readFrameNew]", + "coverage": 0 + }, + { + "name": "__29-[FSRWebSocket _readFrameNew]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _pumpWriting]", + "coverage": 0.7209302325581395 + }, + { + "name": "__28-[FSRWebSocket _pumpWriting]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _addConsumerWithScanner:callback:]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _addConsumerWithDataLength:callback:readToCurrentFrame:unmaskBytes:]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _addConsumerWithScanner:callback:dataLength:]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _scheduleCleanup]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _cleanupSelfReference:]", + "coverage": 1 + }, + { + "name": "__38-[FSRWebSocket _cleanupSelfReference:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _readUntilHeaderCompleteWithCallback:]", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _readUntilBytes:length:callback:]", + "coverage": 1 + }, + { + "name": "__48-[FSRWebSocket _readUntilBytes:length:callback:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket _innerPumpScanner]", + "coverage": 0.4368932038834951 + }, + { + "name": "__33-[FSRWebSocket _innerPumpScanner]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket _pumpScanner]", + "coverage": 0.8666666666666667 + }, + { + "name": "-[FSRWebSocket _sendFrameWithOpcode:data:]", + "coverage": 0 + }, + { + "name": "-[FSRWebSocket stream:handleEvent:]", + "coverage": 0.4482758620689655 + }, + { + "name": "__35-[FSRWebSocket stream:handleEvent:]_block_invoke", + "coverage": 0 + }, + { + "name": "__35-[FSRWebSocket stream:handleEvent:]_block_invoke.457", + "coverage": 1 + }, + { + "name": "-[FSRWebSocket safeHandleEvent:stream:]", + "coverage": 0.5955056179775281 + }, + { + "name": "__39-[FSRWebSocket safeHandleEvent:stream:]_block_invoke", + "coverage": 0 + }, + { + "name": "__39-[FSRWebSocket safeHandleEvent:stream:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FSRIOConsumer setupWithScanner:handler:bytesNeeded:readToCurrentFrame:unmaskBytes:]", + "coverage": 1 + }, + { + "name": "-[FSRIOConsumerPool initWithBufferCapacity:]", + "coverage": 1 + }, + { + "name": "-[FSRIOConsumerPool init]", + "coverage": 1 + }, + { + "name": "-[FSRIOConsumerPool consumerWithScanner:handler:bytesNeeded:readToCurrentFrame:unmaskBytes:]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FSRIOConsumerPool returnConsumer:]", + "coverage": 0 + }, + { + "name": "-[NSURLRequest(FCertificateAdditions) FSR_SSLPinnedCertificates]", + "coverage": 0 + }, + { + "name": "-[NSMutableURLRequest(FCertificateAdditions) FSR_SSLPinnedCertificates]", + "coverage": 0 + }, + { + "name": "-[NSMutableURLRequest(FCertificateAdditions) setFSR_SSLPinnedCertificates:]", + "coverage": 0 + }, + { + "name": "-[NSURL(FSRWebSocket) SR_origin]", + "coverage": 0.8666666666666667 + }, + { + "name": "SRFastLog", + "coverage": 1 + }, + { + "name": "validate_dispatch_data_partial_string", + "coverage": 0 + }, + { + "name": "+[NSRunLoop(FSRWebSocket) FSR_networkRunLoop]", + "coverage": 1 + }, + { + "name": "__45+[NSRunLoop(FSRWebSocket) FSR_networkRunLoop]_block_invoke", + "coverage": 1 + }, + { + "name": "-[_FSRRunLoopThread dealloc]", + "coverage": 0 + }, + { + "name": "-[_FSRRunLoopThread init]", + "coverage": 1 + }, + { + "name": "-[_FSRRunLoopThread main]", + "coverage": 1 + }, + { + "name": "-[_FSRRunLoopThread runLoop]", + "coverage": 1 + } + ] + }, + { + "name": "FStringUtilities.m", + "coverage": 0.41935483870967744, + "type": "objc", + "functions": [ + { + "name": "+[FStringUtilities base64EncodedSha1:]", + "coverage": 1 + }, + { + "name": "+[FStringUtilities urlDecoded:]", + "coverage": 0 + }, + { + "name": "+[FStringUtilities urlEncoded:]", + "coverage": 0 + }, + { + "name": "+[FStringUtilities sanitizedForUserAgent:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRViewController.m", + "coverage": 0.5, + "type": "objc", + "functions": [ + { + "name": "-[FIRViewController viewDidLoad]", + "coverage": 1 + }, + { + "name": "-[FIRViewController didReceiveMemoryWarning]", + "coverage": 0 + } + ] + }, + { + "name": "FValueIndex.m", + "coverage": 0.5166666666666667, + "type": "objc", + "functions": [ + { + "name": "-[FValueIndex compareKey:andNode:toOtherKey:andNode:]", + "coverage": 0.875 + }, + { + "name": "-[FValueIndex compareKey:andNode:toOtherKey:andNode:reverse:]", + "coverage": 0 + }, + { + "name": "-[FValueIndex compareNamedNode:toNamedNode:]", + "coverage": 1 + }, + { + "name": "-[FValueIndex isDefinedOn:]", + "coverage": 0 + }, + { + "name": "-[FValueIndex indexedValueChangedBetween:and:]", + "coverage": 0 + }, + { + "name": "-[FValueIndex minPost]", + "coverage": 0 + }, + { + "name": "-[FValueIndex maxPost]", + "coverage": 0 + }, + { + "name": "-[FValueIndex makePost:name:]", + "coverage": 0 + }, + { + "name": "-[FValueIndex queryDefinition]", + "coverage": 1 + }, + { + "name": "-[FValueIndex description]", + "coverage": 0 + }, + { + "name": "-[FValueIndex copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[FValueIndex isEqual:]", + "coverage": 1 + }, + { + "name": "-[FValueIndex hash]", + "coverage": 1 + }, + { + "name": "+[FValueIndex valueIndex]", + "coverage": 1 + }, + { + "name": "__25+[FValueIndex valueIndex]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FImmutableSortedSet.m", + "coverage": 0.5421686746987951, + "type": "objc", + "functions": [ + { + "name": "+[FImmutableSortedSet setWithKeysFromDictionary:comparator:]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedSet initWithDictionary:]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedSet contains:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedSet addObject:]", + "coverage": 0.75 + }, + { + "name": "-[FImmutableSortedSet removeObject:]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedSet containsObject:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedSet firstObject]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedSet lastObject]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedSet predecessorEntry:]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedSet count]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedSet isEmpty]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedSet enumerateObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[FImmutableSortedSet enumerateObjectsReverse:usingBlock:]", + "coverage": 1 + }, + { + "name": "__58-[FImmutableSortedSet enumerateObjectsReverse:usingBlock:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedSet objectEnumerator]", + "coverage": 1 + }, + { + "name": "-[FImmutableSortedSet description]", + "coverage": 0 + }, + { + "name": "__34-[FImmutableSortedSet description]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "FWriteRecord.m", + "coverage": 0.5526315789473685, + "type": "objc", + "functions": [ + { + "name": "-[FWriteRecord initWithPath:overwrite:writeId:visible:]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FWriteRecord initWithPath:merge:writeId:]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FWriteRecord overwrite]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FWriteRecord compoundWrite]", + "coverage": 0 + }, + { + "name": "-[FWriteRecord isMerge]", + "coverage": 0 + }, + { + "name": "-[FWriteRecord isOverwrite]", + "coverage": 1 + }, + { + "name": "-[FWriteRecord description]", + "coverage": 0 + }, + { + "name": "-[FWriteRecord isEqual:]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FWriteRecord hash]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDatabaseConfig.m", + "coverage": 0.6, + "type": "objc", + "functions": [ + { + "name": "-[FIRDatabaseConfig init]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseConfig initWithSessionIdentifier:authTokenProvider:]", + "coverage": 1 + }, + { + "name": "-[FIRDatabaseConfig assertUnfrozen]", + "coverage": 0.6 + }, + { + "name": "-[FIRDatabaseConfig setAuthTokenProvider:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseConfig setPersistenceEnabled:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseConfig setPersistenceCacheSizeBytes:]", + "coverage": 1 + }, + { + "name": "-[FIRDatabaseConfig setCallbackQueue:]", + "coverage": 0 + }, + { + "name": "-[FIRDatabaseConfig freeze]", + "coverage": 1 + } + ] + }, + { + "name": "FPersistenceManager.m", + "coverage": 0.6131386861313869, + "type": "objc", + "functions": [ + { + "name": "-[FPersistenceManager initWithStorageEngine:cachePolicy:]", + "coverage": 1 + }, + { + "name": "-[FPersistenceManager close]", + "coverage": 0 + }, + { + "name": "-[FPersistenceManager saveUserOverwrite:atPath:writeId:]", + "coverage": 0 + }, + { + "name": "-[FPersistenceManager saveUserMerge:atPath:writeId:]", + "coverage": 0 + }, + { + "name": "-[FPersistenceManager removeUserWrite:]", + "coverage": 0 + }, + { + "name": "-[FPersistenceManager removeAllUserWrites]", + "coverage": 0 + }, + { + "name": "-[FPersistenceManager userWrites]", + "coverage": 0 + }, + { + "name": "-[FPersistenceManager serverCacheForQuery:]", + "coverage": 0.9629629629629629 + }, + { + "name": "-[FPersistenceManager updateServerCacheWithNode:forQuery:]", + "coverage": 1 + }, + { + "name": "-[FPersistenceManager updateServerCacheWithMerge:atPath:]", + "coverage": 0 + }, + { + "name": "-[FPersistenceManager applyUserMerge:toServerCacheAtPath:]", + "coverage": 1 + }, + { + "name": "__58-[FPersistenceManager applyUserMerge:toServerCacheAtPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FPersistenceManager applyUserWrite:toServerCacheAtPath:]", + "coverage": 1 + }, + { + "name": "-[FPersistenceManager setQueryComplete:]", + "coverage": 1 + }, + { + "name": "-[FPersistenceManager setQueryActive:]", + "coverage": 1 + }, + { + "name": "-[FPersistenceManager setQueryInactive:]", + "coverage": 0 + }, + { + "name": "-[FPersistenceManager doPruneCheckAfterServerUpdate]", + "coverage": 0.17391304347826086 + }, + { + "name": "-[FPersistenceManager setTrackedQueryKeys:forQuery:]", + "coverage": 1 + }, + { + "name": "-[FPersistenceManager updateTrackedQueryKeysWithAddedKeys:removedKeys:forQuery:]", + "coverage": 0 + } + ] + }, + { + "name": "FMaxNode.m", + "coverage": 0.6176470588235294, + "type": "objc", + "functions": [ + { + "name": "-[FMaxNode init]", + "coverage": 1 + }, + { + "name": "+[FMaxNode maxNode]", + "coverage": 1 + }, + { + "name": "__19+[FMaxNode maxNode]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FMaxNode compare:]", + "coverage": 0 + }, + { + "name": "-[FMaxNode isEqual:]", + "coverage": 0 + }, + { + "name": "-[FMaxNode getImmediateChild:]", + "coverage": 0 + }, + { + "name": "-[FMaxNode isEmpty]", + "coverage": 1 + } + ] + }, + { + "name": "status.h", + "coverage": 0.631578947368421, + "type": "objc", + "functions": [ + { + "name": "leveldb::Status::Status()", + "coverage": 1 + }, + { + "name": "leveldb::Status::~Status()", + "coverage": 1 + }, + { + "name": "leveldb::Status::OK()", + "coverage": 1 + }, + { + "name": "leveldb::Status::NotFound(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Status::Corruption(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::Status::NotSupported(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Status::InvalidArgument(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Status::IOError(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::Status::ok() const", + "coverage": 1 + }, + { + "name": "leveldb::Status::IsNotFound() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::IsCorruption() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::IsIOError() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::IsNotSupportedError() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::IsInvalidArgument() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::code() const", + "coverage": 1 + }, + { + "name": "leveldb::Status::Status(leveldb::Status const&)", + "coverage": 1 + }, + { + "name": "leveldb::Status::operator=(leveldb::Status const&)", + "coverage": 1 + } + ] + }, + { + "name": "APLevelDB.mm", + "coverage": 0.6482084690553745, + "type": "cpp", + "functions": [ + { + "name": "+[APLevelDB levelDBWithPath:error:]", + "coverage": 1 + }, + { + "name": "-[APLevelDB initWithPath:error:]", + "coverage": 1 + }, + { + "name": "-[APLevelDB close]", + "coverage": 1 + }, + { + "name": "-[APLevelDB dealloc]", + "coverage": 1 + }, + { + "name": "+[APLevelDB defaultCreateOptions]", + "coverage": 1 + }, + { + "name": "-[APLevelDB setData:forKey:]", + "coverage": 1 + }, + { + "name": "-[APLevelDB setString:forKey:]", + "coverage": 0 + }, + { + "name": "-[APLevelDB dataForKey:]", + "coverage": 0 + }, + { + "name": "-[APLevelDB stringForKey:]", + "coverage": 0 + }, + { + "name": "-[APLevelDB removeKey:]", + "coverage": 1 + }, + { + "name": "-[APLevelDB allKeys]", + "coverage": 0 + }, + { + "name": "__20-[APLevelDB allKeys]_block_invoke", + "coverage": 0 + }, + { + "name": "-[APLevelDB enumerateKeysAndValuesAsStrings:]", + "coverage": 0 + }, + { + "name": "-[APLevelDB enumerateKeysWithPrefix:asStrings:]", + "coverage": 0.9523809523809523 + }, + { + "name": "-[APLevelDB enumerateKeys:]", + "coverage": 1 + }, + { + "name": "-[APLevelDB enumerateKeysWithPrefix:usingBlock:]", + "coverage": 0.95 + }, + { + "name": "-[APLevelDB enumerateKeysAndValuesAsData:]", + "coverage": 1 + }, + { + "name": "-[APLevelDB enumerateKeysWithPrefix:asData:]", + "coverage": 0.9523809523809523 + }, + { + "name": "-[APLevelDB exactSizeFrom:to:]", + "coverage": 0 + }, + { + "name": "-[APLevelDB approximateSizeFrom:to:]", + "coverage": 0 + }, + { + "name": "-[APLevelDB objectForKeyedSubscript:]", + "coverage": 0 + }, + { + "name": "-[APLevelDB setObject:forKeyedSubscript:]", + "coverage": 0 + }, + { + "name": "-[APLevelDB beginWriteBatch]", + "coverage": 1 + }, + { + "name": "-[APLevelDB commitWriteBatch:]", + "coverage": 0.9 + }, + { + "name": "+[APLevelDBIterator iteratorWithLevelDB:]", + "coverage": 1 + }, + { + "name": "-[APLevelDBIterator initWithLevelDB:]", + "coverage": 1 + }, + { + "name": "-[APLevelDBIterator init]", + "coverage": 0 + }, + { + "name": "-[APLevelDBIterator dealloc]", + "coverage": 1 + }, + { + "name": "-[APLevelDBIterator seekToKey:]", + "coverage": 1 + }, + { + "name": "-[APLevelDBIterator seekToFirst]", + "coverage": 0 + }, + { + "name": "-[APLevelDBIterator seekToLast]", + "coverage": 0 + }, + { + "name": "-[APLevelDBIterator nextKey]", + "coverage": 1 + }, + { + "name": "-[APLevelDBIterator key]", + "coverage": 1 + }, + { + "name": "-[APLevelDBIterator valueAsString]", + "coverage": 0 + }, + { + "name": "-[APLevelDBIterator valueAsData]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[APLevelDBWriteBatch initWithLevelDB:]", + "coverage": 1 + }, + { + "name": "-[APLevelDBWriteBatch setData:forKey:]", + "coverage": 1 + }, + { + "name": "-[APLevelDBWriteBatch setString:forKey:]", + "coverage": 1 + }, + { + "name": "-[APLevelDBWriteBatch removeKey:]", + "coverage": 1 + }, + { + "name": "-[APLevelDBWriteBatch clear]", + "coverage": 0 + }, + { + "name": "-[APLevelDBWriteBatch commit]", + "coverage": 1 + } + ] + }, + { + "name": "FSnapshotUtilities.m", + "coverage": 0.6577946768060836, + "type": "objc", + "functions": [ + { + "name": "+[FSnapshotUtilities nodeFrom:]", + "coverage": 1 + }, + { + "name": "+[FSnapshotUtilities nodeFrom:priority:]", + "coverage": 1 + }, + { + "name": "+[FSnapshotUtilities nodeFrom:withValidationFrom:]", + "coverage": 1 + }, + { + "name": "+[FSnapshotUtilities nodeFrom:priority:withValidationFrom:]", + "coverage": 1 + }, + { + "name": "+[FSnapshotUtilities nodeFrom:priority:withValidationFrom:atDepth:path:]", + "coverage": 1 + }, + { + "name": "+[FSnapshotUtilities internalNodeFrom:priority:withValidationFrom:atDepth:path:]", + "coverage": 0.6052631578947368 + }, + { + "name": "+[FSnapshotUtilities compoundWriteFromDictionary:withValidationFrom:]", + "coverage": 0 + }, + { + "name": "__69+[FSnapshotUtilities compoundWriteFromDictionary:withValidationFrom:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FSnapshotUtilities validatePriorityNode:]", + "coverage": 0.8888888888888888 + }, + { + "name": "+[FSnapshotUtilities appendHashRepresentationForLeafNode:toString:hashVersion:]", + "coverage": 0.9354838709677419 + }, + { + "name": "+[FSnapshotUtilities appendHashV2RepresentationForString:toString:]", + "coverage": 1 + }, + { + "name": "+[FSnapshotUtilities estimateLeafNodeSize:]", + "coverage": 0.6086956521739131 + }, + { + "name": "+[FSnapshotUtilities estimateSerializedNodeSize:]", + "coverage": 1 + }, + { + "name": "__49+[FSnapshotUtilities estimateSerializedNodeSize:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FKeyIndex.m", + "coverage": 0.6984126984126984, + "type": "objc", + "functions": [ + { + "name": "-[FKeyIndex init]", + "coverage": 1 + }, + { + "name": "-[FKeyIndex compareKey:andNode:toOtherKey:andNode:]", + "coverage": 1 + }, + { + "name": "-[FKeyIndex compareKey:andNode:toOtherKey:andNode:reverse:]", + "coverage": 0 + }, + { + "name": "-[FKeyIndex compareNamedNode:toNamedNode:]", + "coverage": 0 + }, + { + "name": "-[FKeyIndex isDefinedOn:]", + "coverage": 1 + }, + { + "name": "-[FKeyIndex indexedValueChangedBetween:and:]", + "coverage": 1 + }, + { + "name": "-[FKeyIndex minPost]", + "coverage": 0 + }, + { + "name": "-[FKeyIndex makePost:name:]", + "coverage": 1 + }, + { + "name": "-[FKeyIndex queryDefinition]", + "coverage": 1 + }, + { + "name": "-[FKeyIndex description]", + "coverage": 0 + }, + { + "name": "-[FKeyIndex copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[FKeyIndex isEqual:]", + "coverage": 1 + }, + { + "name": "-[FKeyIndex hash]", + "coverage": 1 + }, + { + "name": "+[FKeyIndex keyIndex]", + "coverage": 1 + }, + { + "name": "__21+[FKeyIndex keyIndex]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FChildChangeAccumulator.m", + "coverage": 0.7, + "type": "objc", + "functions": [ + { + "name": "-[FChildChangeAccumulator init]", + "coverage": 1 + }, + { + "name": "-[FChildChangeAccumulator trackChildChange:]", + "coverage": 0.625 + }, + { + "name": "-[FChildChangeAccumulator changes]", + "coverage": 1 + } + ] + }, + { + "name": "FNamedNode.m", + "coverage": 0.7068965517241379, + "type": "objc", + "functions": [ + { + "name": "+[FNamedNode nodeWithName:node:]", + "coverage": 1 + }, + { + "name": "-[FNamedNode initWithName:andNode:]", + "coverage": 1 + }, + { + "name": "-[FNamedNode copy]", + "coverage": 0 + }, + { + "name": "-[FNamedNode copyWithZone:]", + "coverage": 1 + }, + { + "name": "+[FNamedNode min]", + "coverage": 1 + }, + { + "name": "__17+[FNamedNode min]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FNamedNode max]", + "coverage": 0 + }, + { + "name": "__17+[FNamedNode max]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FNamedNode description]", + "coverage": 0 + }, + { + "name": "-[FNamedNode isEqual:]", + "coverage": 1 + }, + { + "name": "-[FNamedNode hash]", + "coverage": 1 + } + ] + }, + { + "name": "FUtilities.m", + "coverage": 0.7151515151515152, + "type": "objc", + "functions": [ + { + "name": "FFIsLoggingEnabled", + "coverage": 1 + }, + { + "name": "firebaseJobsTroll", + "coverage": 0 + }, + { + "name": "-[FUtilities init]", + "coverage": 1 + }, + { + "name": "+[FUtilities setLoggingEnabled:]", + "coverage": 1 + }, + { + "name": "+[FUtilities getLoggingEnabled]", + "coverage": 0 + }, + { + "name": "+[FUtilities singleton]", + "coverage": 1 + }, + { + "name": "__23+[FUtilities singleton]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FUtilities splitString:intoMaxSize:]", + "coverage": 0 + }, + { + "name": "+[FUtilities LUIDGenerator]", + "coverage": 1 + }, + { + "name": "+[FUtilities decodePath:]", + "coverage": 0.8 + }, + { + "name": "+[FUtilities parseUrl:]", + "coverage": 0.8727272727272727 + }, + { + "name": "+[FUtilities getJavascriptType:]", + "coverage": 0.7727272727272727 + }, + { + "name": "+[FUtilities errorForStatus:andReason:]", + "coverage": 0 + }, + { + "name": "__39+[FUtilities errorForStatus:andReason:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FUtilities intForString:]", + "coverage": 1 + }, + { + "name": "+[FUtilities ieee754StringForNumber:]", + "coverage": 1 + }, + { + "name": "tryParseStringToInt", + "coverage": 1 + }, + { + "name": "+[FUtilities maxName]", + "coverage": 1 + }, + { + "name": "__21+[FUtilities maxName]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FUtilities minName]", + "coverage": 1 + }, + { + "name": "__21+[FUtilities minName]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FUtilities compareKey:toKey:]", + "coverage": 0.9705882352941176 + }, + { + "name": "+[FUtilities keyComparator]", + "coverage": 1 + }, + { + "name": "__27+[FUtilities keyComparator]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FUtilities stringComparator]", + "coverage": 1 + }, + { + "name": "__30+[FUtilities stringComparator]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FUtilities randomDouble]", + "coverage": 1 + } + ] + }, + { + "name": "FAuthTokenProvider.m", + "coverage": 0.734375, + "type": "objc", + "functions": [ + { + "name": "-[FAuthStateListenerWrapper initWithListener:auth:]", + "coverage": 1 + }, + { + "name": "-[FAuthStateListenerWrapper authStateDidChangeNotification:]", + "coverage": 0 + }, + { + "name": "__60-[FAuthStateListenerWrapper authStateDidChangeNotification:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FAuthStateListenerWrapper dealloc]", + "coverage": 0 + }, + { + "name": "-[FIRFirebaseAuthTokenProvider initWithAuth:]", + "coverage": 1 + }, + { + "name": "-[FIRFirebaseAuthTokenProvider fetchTokenForcingRefresh:withCallback:]", + "coverage": 0.8461538461538461 + }, + { + "name": "__70-[FIRFirebaseAuthTokenProvider fetchTokenForcingRefresh:withCallback:]_block_invoke", + "coverage": 1 + }, + { + "name": "__70-[FIRFirebaseAuthTokenProvider fetchTokenForcingRefresh:withCallback:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRFirebaseAuthTokenProvider listenForTokenChanges:]", + "coverage": 1 + }, + { + "name": "+[FAuthTokenProvider authTokenProviderWithAuth:]", + "coverage": 1 + } + ] + }, + { + "name": "FServerValues.m", + "coverage": 0.7435897435897436, + "type": "objc", + "functions": [ + { + "name": "+[FServerValues generateServerValues:]", + "coverage": 1 + }, + { + "name": "+[FServerValues resolveDeferredValue:withServerValues:]", + "coverage": 0.2857142857142857 + }, + { + "name": "+[FServerValues resolveDeferredValueCompoundWrite:withServerValues:]", + "coverage": 1 + }, + { + "name": "__68+[FServerValues resolveDeferredValueCompoundWrite:withServerValues:]_block_invoke", + "coverage": 0.7142857142857143 + }, + { + "name": "+[FServerValues resolveDeferredValueTree:withServerValues:]", + "coverage": 1 + }, + { + "name": "__59+[FServerValues resolveDeferredValueTree:withServerValues:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FServerValues resolveDeferredValueSnapshot:withServerValues:]", + "coverage": 0.8846153846153846 + }, + { + "name": "__63+[FServerValues resolveDeferredValueSnapshot:withServerValues:]_block_invoke", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FLLRBEmptyNode.m", + "coverage": 0.75, + "type": "objc", + "functions": [ + { + "name": "-[FLLRBEmptyNode description]", + "coverage": 0 + }, + { + "name": "+[FLLRBEmptyNode emptyNode]", + "coverage": 1 + }, + { + "name": "__27+[FLLRBEmptyNode emptyNode]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode copyWith:withValue:withColor:withLeft:withRight:]", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode insertKey:forValue:withComparator:]", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode remove:withComparator:]", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode count]", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode isEmpty]", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode inorderTraversal:]", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode reverseTraversal:]", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode min]", + "coverage": 0 + }, + { + "name": "-[FLLRBEmptyNode minKey]", + "coverage": 0 + }, + { + "name": "-[FLLRBEmptyNode maxKey]", + "coverage": 0 + }, + { + "name": "-[FLLRBEmptyNode isRed]", + "coverage": 1 + }, + { + "name": "-[FLLRBEmptyNode check]", + "coverage": 1 + } + ] + }, + { + "name": "FSyncTree.m", + "coverage": 0.7587719298245614, + "type": "objc", + "functions": [ + { + "name": "-[FListenContainer initWithView:onComplete:]", + "coverage": 1 + }, + { + "name": "-[FListenContainer serverCache]", + "coverage": 0 + }, + { + "name": "-[FListenContainer compoundHash]", + "coverage": 0 + }, + { + "name": "-[FListenContainer simpleHash]", + "coverage": 0 + }, + { + "name": "-[FListenContainer includeCompoundHash]", + "coverage": 0 + }, + { + "name": "-[FSyncTree initWithListenProvider:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree initWithPersistenceManager:listenProvider:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyUserOverwriteAtPath:newData:writeId:isVisible:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyUserMergeAtPath:changedChildren:writeId:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree ackUserWriteWithWriteId:revert:persist:clock:]", + "coverage": 1 + }, + { + "name": "__58-[FSyncTree ackUserWriteWithWriteId:revert:persist:clock:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyServerOverwriteAtPath:newData:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyServerMergeAtPath:changedChildren:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyServerRangeMergeAtPath:updates:]", + "coverage": 0 + }, + { + "name": "-[FSyncTree applyListenCompleteAtPath:]", + "coverage": 0 + }, + { + "name": "-[FSyncTree applyTaggedListenCompleteAtPath:tagId:]", + "coverage": 0 + }, + { + "name": "-[FSyncTree applyTaggedOperation:atPath:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyTaggedQueryOverwriteAtPath:newData:tagId:]", + "coverage": 0.7857142857142857 + }, + { + "name": "-[FSyncTree applyTaggedQueryMergeAtPath:changedChildren:tagId:]", + "coverage": 0.7857142857142857 + }, + { + "name": "-[FSyncTree applyTaggedServerRangeMergeAtPath:updates:tagId:]", + "coverage": 0 + }, + { + "name": "-[FSyncTree addEventRegistration:forQuery:]", + "coverage": 0.9791666666666666 + }, + { + "name": "__43-[FSyncTree addEventRegistration:forQuery:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSyncTree serverCacheForQuery:]", + "coverage": 0.975 + }, + { + "name": "__33-[FSyncTree serverCacheForQuery:]_block_invoke", + "coverage": 1 + }, + { + "name": "__33-[FSyncTree serverCacheForQuery:]_block_invoke.292", + "coverage": 1 + }, + { + "name": "__33-[FSyncTree serverCacheForQuery:]_block_invoke.303", + "coverage": 0 + }, + { + "name": "-[FSyncTree removeEventRegistration:forQuery:cancelError:]", + "coverage": 0.8831168831168831 + }, + { + "name": "__58-[FSyncTree removeEventRegistration:forQuery:cancelError:]_block_invoke", + "coverage": 1 + }, + { + "name": "__58-[FSyncTree removeEventRegistration:forQuery:cancelError:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__58-[FSyncTree removeEventRegistration:forQuery:cancelError:]_block_invoke.334", + "coverage": 0 + }, + { + "name": "__58-[FSyncTree removeEventRegistration:forQuery:cancelError:]_block_invoke_2.344", + "coverage": 1 + }, + { + "name": "__58-[FSyncTree removeEventRegistration:forQuery:cancelError:]_block_invoke.366", + "coverage": 0 + }, + { + "name": "-[FSyncTree keepQuery:synced:]", + "coverage": 0 + }, + { + "name": "-[FSyncTree removeAllWrites]", + "coverage": 0 + }, + { + "name": "-[FSyncTree calcCompleteEventCacheAtPath:excludeWriteIds:]", + "coverage": 0 + }, + { + "name": "__58-[FSyncTree calcCompleteEventCacheAtPath:excludeWriteIds:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSyncTree collectDistinctViewsForSubTree:]", + "coverage": 1 + }, + { + "name": "__44-[FSyncTree collectDistinctViewsForSubTree:]_block_invoke", + "coverage": 0.875 + }, + { + "name": "__44-[FSyncTree collectDistinctViewsForSubTree:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FSyncTree removeTags:]", + "coverage": 1 + }, + { + "name": "__24-[FSyncTree removeTags:]_block_invoke", + "coverage": 0.375 + }, + { + "name": "-[FSyncTree queryForListening:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree setupListenerOnQuery:view:]", + "coverage": 1 + }, + { + "name": "__39-[FSyncTree setupListenerOnQuery:view:]_block_invoke", + "coverage": 1 + }, + { + "name": "__39-[FSyncTree setupListenerOnQuery:view:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FSyncTree createListenerForView:]", + "coverage": 1 + }, + { + "name": "__35-[FSyncTree createListenerForView:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FSyncTree queryForTag:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree tagForQuery:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyOperationToSyncPoints:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyOperationHelper:syncPointTree:serverCache:writesCache:]", + "coverage": 1 + }, + { + "name": "-[FSyncTree applyOperationDescendantsHelper:syncPointTree:serverCache:writesCache:]", + "coverage": 1 + }, + { + "name": "__83-[FSyncTree applyOperationDescendantsHelper:syncPointTree:serverCache:writesCache:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FWriteTree.m", + "coverage": 0.7731958762886598, + "type": "objc", + "functions": [ + { + "name": "-[FWriteTree init]", + "coverage": 1 + }, + { + "name": "-[FWriteTree childWritesForPath:]", + "coverage": 1 + }, + { + "name": "-[FWriteTree addOverwriteAtPath:newData:writeId:isVisible:]", + "coverage": 1 + }, + { + "name": "-[FWriteTree addMergeAtPath:changedChildren:writeId:]", + "coverage": 1 + }, + { + "name": "-[FWriteTree writeForId:]", + "coverage": 1 + }, + { + "name": "__25-[FWriteTree writeForId:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FWriteTree removeWriteId:]", + "coverage": 1 + }, + { + "name": "__28-[FWriteTree removeWriteId:]_block_invoke", + "coverage": 1 + }, + { + "name": "__28-[FWriteTree removeWriteId:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FWriteTree removeAllWrites]", + "coverage": 0 + }, + { + "name": "-[FWriteTree completeWriteDataAtPath:]", + "coverage": 0 + }, + { + "name": "-[FWriteTree calculateCompleteEventCacheAtPath:completeServerCache:excludeWriteIds:includeHiddenWrites:]", + "coverage": 0.46153846153846156 + }, + { + "name": "__104-[FWriteTree calculateCompleteEventCacheAtPath:completeServerCache:excludeWriteIds:includeHiddenWrites:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FWriteTree calculateCompleteEventChildrenAtPath:completeServerChildren:]", + "coverage": 0.7142857142857143 + }, + { + "name": "__74-[FWriteTree calculateCompleteEventChildrenAtPath:completeServerChildren:]_block_invoke", + "coverage": 0 + }, + { + "name": "__74-[FWriteTree calculateCompleteEventChildrenAtPath:completeServerChildren:]_block_invoke.124", + "coverage": 1 + }, + { + "name": "-[FWriteTree calculateEventCacheAfterServerOverwriteAtPath:childPath:existingEventSnap:existingServerSnap:]", + "coverage": 0.88 + }, + { + "name": "-[FWriteTree calculateCompleteChildAtPath:childKey:cache:]", + "coverage": 1 + }, + { + "name": "-[FWriteTree shadowingWriteAtPath:]", + "coverage": 1 + }, + { + "name": "-[FWriteTree calculateNextNodeAfterPost:atPath:completeServerData:reverse:index:]", + "coverage": 0.9285714285714286 + }, + { + "name": "__81-[FWriteTree calculateNextNodeAfterPost:atPath:completeServerData:reverse:index:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FWriteTree record:containsPath:]", + "coverage": 0.4166666666666667 + }, + { + "name": "__34-[FWriteTree record:containsPath:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FWriteTree resetTree]", + "coverage": 0.7777777777777778 + }, + { + "name": "+[FWriteTree defaultFilter]", + "coverage": 1 + }, + { + "name": "__27+[FWriteTree defaultFilter]_block_invoke", + "coverage": 1 + }, + { + "name": "__27+[FWriteTree defaultFilter]_block_invoke_2", + "coverage": 1 + }, + { + "name": "+[FWriteTree layerTreeFromWrites:filter:treeRoot:]", + "coverage": 1 + }, + { + "name": "__50+[FWriteTree layerTreeFromWrites:filter:treeRoot:]_block_invoke", + "coverage": 0.3684210526315789 + } + ] + }, + { + "name": "FIRMutableData.m", + "coverage": 0.7831325301204819, + "type": "objc", + "functions": [ + { + "name": "-[FIRMutableData initWithNode:]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData initWithPrefixPath:andSnapshotHolder:]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData childDataByAppendingPath:]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData parent]", + "coverage": 0 + }, + { + "name": "-[FIRMutableData setValue:]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData setPriority:]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData value]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData priority]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData hasChildren]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData hasChildAtPath:]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData childrenCount]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData key]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData nodeValue]", + "coverage": 1 + }, + { + "name": "-[FIRMutableData children]", + "coverage": 1 + }, + { + "name": "__26-[FIRMutableData children]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRMutableData isEqualToData:]", + "coverage": 0 + }, + { + "name": "-[FIRMutableData description]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDependency.m", + "coverage": 0.7857142857142857, + "type": "objc", + "functions": [ + { + "name": "+[FIRDependency dependencyWithProtocol:]", + "coverage": 0 + }, + { + "name": "+[FIRDependency dependencyWithProtocol:isRequired:]", + "coverage": 1 + }, + { + "name": "-[FIRDependency initWithProtocol:isRequired:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRBundleUtil.m", + "coverage": 0.7872340425531915, + "type": "objc", + "functions": [ + { + "name": "+[FIRBundleUtil relevantBundles]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil optionsDictionaryPathWithResourceName:andFileType:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil relevantURLSchemes]", + "coverage": 0 + }, + { + "name": "+[FIRBundleUtil hasBundleIdentifierPrefix:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil bundleIdentifierByRemovingLastPartFrom:]", + "coverage": 1 + } + ] + }, + { + "name": "FRepoInfo.m", + "coverage": 0.7931034482758621, + "type": "objc", + "functions": [ + { + "name": "-[FRepoInfo initWithHost:isSecure:withNamespace:]", + "coverage": 0.9473684210526315 + }, + { + "name": "-[FRepoInfo description]", + "coverage": 1 + }, + { + "name": "-[FRepoInfo setInternalHost:]", + "coverage": 0 + }, + { + "name": "-[FRepoInfo clearInternalHostCache]", + "coverage": 1 + }, + { + "name": "-[FRepoInfo isDemoHost]", + "coverage": 0 + }, + { + "name": "-[FRepoInfo isCustomHost]", + "coverage": 0 + }, + { + "name": "-[FRepoInfo connectionURL]", + "coverage": 1 + }, + { + "name": "-[FRepoInfo connectionURLWithLastSessionID:]", + "coverage": 1 + }, + { + "name": "-[FRepoInfo copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FRepoInfo hash]", + "coverage": 1 + }, + { + "name": "-[FRepoInfo isEqual:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAuthInteropFake.m", + "coverage": 0.8, + "type": "objc", + "functions": [ + { + "name": "-[FIRAuthInteropFake initWithToken:userID:error:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthInteropFake getTokenForcingRefresh:withCallback:]", + "coverage": 1 + }, + { + "name": "-[FIRAuthInteropFake getUserID]", + "coverage": 0 + } + ] + }, + { + "name": "FTrackedQuery.m", + "coverage": 0.8070175438596491, + "type": "objc", + "functions": [ + { + "name": "-[FTrackedQuery initWithId:query:lastUse:isActive:isComplete:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQuery initWithId:query:lastUse:isActive:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQuery updateLastUse:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQuery setComplete]", + "coverage": 1 + }, + { + "name": "-[FTrackedQuery setActiveState:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQuery isEqual:]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FTrackedQuery hash]", + "coverage": 0 + } + ] + }, + { + "name": "FLevelDBStorageEngine.m", + "coverage": 0.8074935400516796, + "type": "objc", + "functions": [ + { + "name": "writeRecordKey", + "coverage": 1 + }, + { + "name": "serverCacheKey", + "coverage": 1 + }, + { + "name": "trackedQueryKey", + "coverage": 1 + }, + { + "name": "trackedQueryKeysKeyPrefix", + "coverage": 1 + }, + { + "name": "trackedQueryKeysKey", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine initWithPath:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine runMigration]", + "coverage": 0.7222222222222222 + }, + { + "name": "-[FLevelDBStorageEngine runLegacyMigration:]", + "coverage": 0 + }, + { + "name": "__44-[FLevelDBStorageEngine runLegacyMigration:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FLevelDBStorageEngine openDatabases]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine purgeDatabase:]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FLevelDBStorageEngine purgeEverything]", + "coverage": 1 + }, + { + "name": "__40-[FLevelDBStorageEngine purgeEverything]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine close]", + "coverage": 1 + }, + { + "name": "+[FLevelDBStorageEngine firebaseDir]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine createDB:]", + "coverage": 0.8636363636363636 + }, + { + "name": "-[FLevelDBStorageEngine saveUserOverwrite:atPath:writeId:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine saveUserMerge:atPath:writeId:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine removeUserWrite:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine removeAllUserWrites]", + "coverage": 0.9333333333333333 + }, + { + "name": "__44-[FLevelDBStorageEngine removeAllUserWrites]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine userWrites]", + "coverage": 1 + }, + { + "name": "__35-[FLevelDBStorageEngine userWrites]_block_invoke", + "coverage": 0.7 + }, + { + "name": "__35-[FLevelDBStorageEngine userWrites]_block_invoke.287", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FLevelDBStorageEngine serverCacheAtPath:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine serverCacheForKeys:atPath:]", + "coverage": 0 + }, + { + "name": "__51-[FLevelDBStorageEngine serverCacheForKeys:atPath:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FLevelDBStorageEngine updateServerCache:atPath:merge:]", + "coverage": 0.72 + }, + { + "name": "__56-[FLevelDBStorageEngine updateServerCache:atPath:merge:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FLevelDBStorageEngine updateServerCacheWithMerge:atPath:]", + "coverage": 0.9444444444444444 + }, + { + "name": "__59-[FLevelDBStorageEngine updateServerCacheWithMerge:atPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine saveNodeInternal:atPath:batch:counter:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine serverCacheEstimatedSizeInBytes]", + "coverage": 0 + }, + { + "name": "-[FLevelDBStorageEngine pruneCache:atPath:]", + "coverage": 0.9629629629629629 + }, + { + "name": "__43-[FLevelDBStorageEngine pruneCache:atPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine loadTrackedQueries]", + "coverage": 1 + }, + { + "name": "__43-[FLevelDBStorageEngine loadTrackedQueries]_block_invoke", + "coverage": 0.7096774193548387 + }, + { + "name": "-[FLevelDBStorageEngine removeTrackedQuery:]", + "coverage": 0.95 + }, + { + "name": "__44-[FLevelDBStorageEngine removeTrackedQuery:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine saveTrackedQuery:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine setTrackedQueryKeys:forQueryId:]", + "coverage": 0.972972972972973 + }, + { + "name": "__56-[FLevelDBStorageEngine setTrackedQueryKeys:forQueryId:]_block_invoke", + "coverage": 1 + }, + { + "name": "__56-[FLevelDBStorageEngine setTrackedQueryKeys:forQueryId:]_block_invoke.460", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine updateTrackedQueryKeysWithAddedKeys:removedKeys:forQueryId:]", + "coverage": 0.9375 + }, + { + "name": "__84-[FLevelDBStorageEngine updateTrackedQueryKeysWithAddedKeys:removedKeys:forQueryId:]_block_invoke", + "coverage": 1 + }, + { + "name": "__84-[FLevelDBStorageEngine updateTrackedQueryKeysWithAddedKeys:removedKeys:forQueryId:]_block_invoke.477", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine trackedQueryKeysForQuery:]", + "coverage": 1 + }, + { + "name": "__50-[FLevelDBStorageEngine trackedQueryKeysForQuery:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine removeAllLeafNodesOnPath:batch:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine removeAllWithPrefix:batch:database:]", + "coverage": 1 + }, + { + "name": "__60-[FLevelDBStorageEngine removeAllWithPrefix:batch:database:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine internalSetNestedData:forKey:withBatch:counter:]", + "coverage": 1 + }, + { + "name": "__72-[FLevelDBStorageEngine internalSetNestedData:forKey:withBatch:counter:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine internalNestedDataForPath:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine internalNestedDataFromIterator:andKeyPrefix:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine serializePrimitive:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine fixDoubleParsing:]", + "coverage": 1 + }, + { + "name": "-[FLevelDBStorageEngine deserializePrimitive:]", + "coverage": 0.8235294117647058 + }, + { + "name": "+[FLevelDBStorageEngine ensureDir:markAsDoNotBackup:]", + "coverage": 0.7619047619047619 + } + ] + }, + { + "name": "FIRConfiguration.m", + "coverage": 0.8076923076923077, + "type": "objc", + "functions": [ + { + "name": "+[FIRConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__34+[FIRConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration init]", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration setLoggerLevel:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRLogger.m", + "coverage": 0.8118811881188119, + "type": "objc", + "functions": [ + { + "name": "FIRLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__FIRLoggerInitializeASL_block_invoke", + "coverage": 0.9333333333333333 + }, + { + "name": "FIRSetAnalyticsDebugMode", + "coverage": 0 + }, + { + "name": "FIRSetLoggerLevel", + "coverage": 0 + }, + { + "name": "FIRResetLogger", + "coverage": 1 + }, + { + "name": "FIRSetLoggerUserDefaults", + "coverage": 1 + }, + { + "name": "FIRIsLoggableLevel", + "coverage": 0 + }, + { + "name": "FIRLogBasic", + "coverage": 1 + }, + { + "name": "Definition at 158:95", + "coverage": 1 + }, + { + "name": "+[FIRLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + }, + { + "name": "FLeafNode.m", + "coverage": 0.8435754189944135, + "type": "objc", + "functions": [ + { + "name": "-[FLeafNode initWithValue:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode initWithValue:withPriority:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode isLeafNode]", + "coverage": 1 + }, + { + "name": "-[FLeafNode getPriority]", + "coverage": 1 + }, + { + "name": "-[FLeafNode updatePriority:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode getImmediateChild:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode getChild:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode hasChild:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode predecessorChildKey:]", + "coverage": 0 + }, + { + "name": "-[FLeafNode updateImmediateChild:withNewChild:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode updateChild:withNewChild:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode val]", + "coverage": 1 + }, + { + "name": "-[FLeafNode valForExport:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FLeafNode isEqual:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode hash]", + "coverage": 1 + }, + { + "name": "-[FLeafNode withIndex:]", + "coverage": 0 + }, + { + "name": "-[FLeafNode isIndexed:]", + "coverage": 0 + }, + { + "name": "-[FLeafNode isEmpty]", + "coverage": 1 + }, + { + "name": "-[FLeafNode numChildren]", + "coverage": 1 + }, + { + "name": "-[FLeafNode enumerateChildrenUsingBlock:]", + "coverage": 1 + }, + { + "name": "-[FLeafNode enumerateChildrenReverse:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[FLeafNode childEnumerator]", + "coverage": 0 + }, + { + "name": "-[FLeafNode dataHash]", + "coverage": 1 + }, + { + "name": "-[FLeafNode compare:]", + "coverage": 1 + }, + { + "name": "+[FLeafNode valueTypeOrder]", + "coverage": 1 + }, + { + "name": "__27+[FLeafNode valueTypeOrder]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLeafNode compareToLeafNode:]", + "coverage": 0.9 + }, + { + "name": "-[FLeafNode description]", + "coverage": 0 + }, + { + "name": "-[FLeafNode forEachChildDo:]", + "coverage": 0 + } + ] + }, + { + "name": "FImmutableTree.m", + "coverage": 0.8474114441416893, + "type": "objc", + "functions": [ + { + "name": "-[FImmutableTree initWithValue:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree initWithValue:children:]", + "coverage": 1 + }, + { + "name": "+[FImmutableTree emptyChildren]", + "coverage": 1 + }, + { + "name": "__31+[FImmutableTree emptyChildren]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FImmutableTree empty]", + "coverage": 1 + }, + { + "name": "__23+[FImmutableTree empty]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree isEmpty]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree findRootMostMatchingPath:predicate:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree findRootMostValueAndPath:]", + "coverage": 1 + }, + { + "name": "__43-[FImmutableTree findRootMostValueAndPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree rootMostValueOnPath:]", + "coverage": 1 + }, + { + "name": "__38-[FImmutableTree rootMostValueOnPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree rootMostValueOnPath:matching:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree leafMostValueOnPath:]", + "coverage": 1 + }, + { + "name": "__38-[FImmutableTree leafMostValueOnPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree leafMostValueOnPath:matching:]", + "coverage": 1 + }, + { + "name": "__47-[FImmutableTree leafMostValueOnPath:matching:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree containsValueMatching:]", + "coverage": 1 + }, + { + "name": "__40-[FImmutableTree containsValueMatching:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree subtreeAtPath:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree setValue:atPath:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree removeValueAtPath:]", + "coverage": 0.7857142857142857 + }, + { + "name": "-[FImmutableTree valueAtPath:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree setTree:atPath:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree foldWithBlock:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree foldWithPathSoFar:withBlock:]", + "coverage": 1 + }, + { + "name": "__46-[FImmutableTree foldWithPathSoFar:withBlock:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree findOnPath:andApplyBlock:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree findOnPath:pathSoFar:andApplyBlock:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FImmutableTree forEachOnPath:whileBlock:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree forEachOnPath:pathSoFar:whileBlock:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree forEachOnPath:performBlock:]", + "coverage": 0 + }, + { + "name": "-[FImmutableTree forEachOnPath:pathSoFar:performBlock:]", + "coverage": 0 + }, + { + "name": "-[FImmutableTree forEach:]", + "coverage": 1 + }, + { + "name": "-[FImmutableTree forEachPathSoFar:withBlock:]", + "coverage": 1 + }, + { + "name": "__45-[FImmutableTree forEachPathSoFar:withBlock:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree forEachChild:]", + "coverage": 1 + }, + { + "name": "__31-[FImmutableTree forEachChild:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FImmutableTree isEqual:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FImmutableTree hash]", + "coverage": 0 + }, + { + "name": "-[FImmutableTree description]", + "coverage": 0 + }, + { + "name": "__29-[FImmutableTree description]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FImmutableTree debugDescription]", + "coverage": 0 + } + ] + }, + { + "name": "FPriorityIndex.m", + "coverage": 0.8507462686567164, + "type": "objc", + "functions": [ + { + "name": "-[FPriorityIndex compareKey:andNode:toOtherKey:andNode:]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex compareKey:andNode:toOtherKey:andNode:reverse:]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex compareNamedNode:toNamedNode:]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex isDefinedOn:]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex indexedValueChangedBetween:and:]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex minPost]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex maxPost]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex makePost:name:]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex queryDefinition]", + "coverage": 0 + }, + { + "name": "-[FPriorityIndex description]", + "coverage": 0 + }, + { + "name": "-[FPriorityIndex copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[FPriorityIndex isEqual:]", + "coverage": 1 + }, + { + "name": "-[FPriorityIndex hash]", + "coverage": 1 + }, + { + "name": "+[FPriorityIndex priorityIndex]", + "coverage": 1 + }, + { + "name": "__31+[FPriorityIndex priorityIndex]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "NSData+SRB64Additions.m", + "coverage": 0.8571428571428571, + "type": "objc", + "functions": [ + { + "name": "+[FSRUtilities base64EncodedStringFromData:]", + "coverage": 0.8571428571428571 + } + ] + }, + { + "name": "FLLRBValueNode.m", + "coverage": 0.8601036269430051, + "type": "objc", + "functions": [ + { + "name": "-[FLLRBValueNode description]", + "coverage": 0 + }, + { + "name": "-[FLLRBValueNode initWithKey:withValue:withColor:withLeft:withRight:]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode copyWith:withValue:withColor:withLeft:withRight:]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode count]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode isEmpty]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode inorderTraversal:]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode reverseTraversal:]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode min]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode minKey]", + "coverage": 0 + }, + { + "name": "-[FLLRBValueNode maxKey]", + "coverage": 0 + }, + { + "name": "-[FLLRBValueNode insertKey:forValue:withComparator:]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode removeMin]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode fixUp]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode moveRedLeft]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode moveRedRight]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode rotateLeft]", + "coverage": 0.75 + }, + { + "name": "-[FLLRBValueNode rotateRight]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode colorFlip]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode remove:withComparator:]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode isRed]", + "coverage": 1 + }, + { + "name": "-[FLLRBValueNode checkMaxDepth]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FLLRBValueNode check]", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FView.m", + "coverage": 0.8642857142857143, + "type": "objc", + "functions": [ + { + "name": "-[FViewOperationResult initWithChanges:events:]", + "coverage": 1 + }, + { + "name": "-[FView initWithQuery:initialViewCache:]", + "coverage": 1 + }, + { + "name": "-[FView serverCache]", + "coverage": 0 + }, + { + "name": "-[FView eventCache]", + "coverage": 1 + }, + { + "name": "-[FView completeServerCacheFor:]", + "coverage": 1 + }, + { + "name": "-[FView isEmpty]", + "coverage": 1 + }, + { + "name": "-[FView addEventRegistration:]", + "coverage": 1 + }, + { + "name": "-[FView removeEventRegistration:cancelError:]", + "coverage": 0.5357142857142857 + }, + { + "name": "-[FView applyOperation:writesCache:serverCache:]", + "coverage": 1 + }, + { + "name": "-[FView initialEvents:]", + "coverage": 1 + }, + { + "name": "__23-[FView initialEvents:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FView generateEventsForChanges:eventCache:registration:]", + "coverage": 1 + }, + { + "name": "-[FView description]", + "coverage": 0 + } + ] + }, + { + "name": "FPathIndex.m", + "coverage": 0.8714285714285714, + "type": "objc", + "functions": [ + { + "name": "-[FPathIndex initWithPath:]", + "coverage": 0.8 + }, + { + "name": "-[FPathIndex compareKey:andNode:toOtherKey:andNode:]", + "coverage": 1 + }, + { + "name": "-[FPathIndex compareKey:andNode:toOtherKey:andNode:reverse:]", + "coverage": 1 + }, + { + "name": "-[FPathIndex compareNamedNode:toNamedNode:]", + "coverage": 1 + }, + { + "name": "-[FPathIndex isDefinedOn:]", + "coverage": 1 + }, + { + "name": "-[FPathIndex indexedValueChangedBetween:and:]", + "coverage": 1 + }, + { + "name": "-[FPathIndex minPost]", + "coverage": 1 + }, + { + "name": "-[FPathIndex maxPost]", + "coverage": 1 + }, + { + "name": "-[FPathIndex makePost:name:]", + "coverage": 1 + }, + { + "name": "-[FPathIndex queryDefinition]", + "coverage": 1 + }, + { + "name": "-[FPathIndex description]", + "coverage": 0 + }, + { + "name": "-[FPathIndex copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[FPathIndex isEqual:]", + "coverage": 1 + }, + { + "name": "-[FPathIndex hash]", + "coverage": 1 + } + ] + }, + { + "name": "FQuerySpec.m", + "coverage": 0.8723404255319149, + "type": "objc", + "functions": [ + { + "name": "-[FQuerySpec initWithPath:params:]", + "coverage": 1 + }, + { + "name": "+[FQuerySpec defaultQueryAtPath:]", + "coverage": 1 + }, + { + "name": "-[FQuerySpec copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FQuerySpec index]", + "coverage": 1 + }, + { + "name": "-[FQuerySpec isDefault]", + "coverage": 1 + }, + { + "name": "-[FQuerySpec loadsAllData]", + "coverage": 1 + }, + { + "name": "-[FQuerySpec isEqual:]", + "coverage": 0.6470588235294118 + }, + { + "name": "-[FQuerySpec hash]", + "coverage": 1 + }, + { + "name": "-[FQuerySpec description]", + "coverage": 1 + } + ] + }, + { + "name": "FOverwrite.m", + "coverage": 0.875, + "type": "objc", + "functions": [ + { + "name": "-[FOverwrite initWithSource:path:snap:]", + "coverage": 1 + }, + { + "name": "-[FOverwrite operationForChild:]", + "coverage": 1 + }, + { + "name": "-[FOverwrite description]", + "coverage": 0 + } + ] + }, + { + "name": "FIRRetryHelper.m", + "coverage": 0.8809523809523809, + "type": "objc", + "functions": [ + { + "name": "-[FIRRetryHelperTask initWithBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRRetryHelperTask isCanceled]", + "coverage": 1 + }, + { + "name": "-[FIRRetryHelperTask cancel]", + "coverage": 0 + }, + { + "name": "-[FIRRetryHelperTask execute]", + "coverage": 1 + }, + { + "name": "-[FIRRetryHelper initWithDispatchQueue:minRetryDelayAfterFailure:maxRetryDelay:retryExponent:jitterFactor:]", + "coverage": 1 + }, + { + "name": "-[FIRRetryHelper retry:]", + "coverage": 0.8823529411764706 + }, + { + "name": "__24-[FIRRetryHelper retry:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRRetryHelper signalSuccess]", + "coverage": 1 + }, + { + "name": "-[FIRRetryHelper cancel]", + "coverage": 0.7 + } + ] + }, + { + "name": "FIRApp.m", + "coverage": 0.8848, + "type": "objc", + "functions": [ + { + "name": "+[FIRApp configure]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithOptions:]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithName:options:]", + "coverage": 0.9333333333333333 + }, + { + "name": "+[FIRApp defaultApp]", + "coverage": 1 + }, + { + "name": "+[FIRApp appNamed:]", + "coverage": 1 + }, + { + "name": "+[FIRApp allApps]", + "coverage": 1 + }, + { + "name": "+[FIRApp resetApps]", + "coverage": 1 + }, + { + "name": "-[FIRApp deleteApp:]", + "coverage": 0.7916666666666666 + }, + { + "name": "+[FIRApp addAppToAppDictionary:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRApp initInstanceWithName:options:]", + "coverage": 1 + }, + { + "name": "-[FIRApp configureCore]", + "coverage": 0.7962962962962963 + }, + { + "name": "-[FIRApp options]", + "coverage": 1 + }, + { + "name": "-[FIRApp setDataCollectionDefaultEnabled:]", + "coverage": 0.9354838709677419 + }, + { + "name": "-[FIRApp isDataCollectionDefaultEnabled]", + "coverage": 1 + }, + { + "name": "+[FIRApp sendNotificationsToSDKs:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForMissingOptions]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForSubspecConfigurationFailureWithDomain:errorCode:service:reason:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForInvalidAppID]", + "coverage": 0 + }, + { + "name": "+[FIRApp isDefaultAppConfigured]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerLibrary:withVersion:]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerInternalLibrary:withName:withVersion:]", + "coverage": 1 + }, + { + "name": "__55+[FIRApp registerInternalLibrary:withName:withVersion:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRApp firebaseUserAgent]", + "coverage": 1 + }, + { + "name": "-[FIRApp checkExpectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp isAppIDValid]", + "coverage": 1 + }, + { + "name": "+[FIRApp validateAppID:]", + "coverage": 0.9473684210526315 + }, + { + "name": "+[FIRApp actualBundleID]", + "coverage": 0 + }, + { + "name": "+[FIRApp validateAppIDFormat:withVersion:]", + "coverage": 0.8450704225352113 + }, + { + "name": "+[FIRApp validateAppIDFingerprint:withVersion:]", + "coverage": 0.8620689655172413 + }, + { + "name": "-[FIRApp expectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp clearDataCollectionSwitchFromUserDefaults]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromUserDefaultsForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromPlist]", + "coverage": 1 + }, + { + "name": "__43+[FIRApp readDataCollectionSwitchFromPlist]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRApp sendLogsWithServiceName:version:error:]", + "coverage": 0.2631578947368421 + } + ] + }, + { + "name": "FChange.m", + "coverage": 0.8888888888888888, + "type": "objc", + "functions": [ + { + "name": "-[FChange initWithType:indexedNode:]", + "coverage": 1 + }, + { + "name": "-[FChange initWithType:indexedNode:childKey:]", + "coverage": 1 + }, + { + "name": "-[FChange initWithType:indexedNode:childKey:oldIndexedNode:]", + "coverage": 1 + }, + { + "name": "-[FChange changeWithPrevKey:]", + "coverage": 1 + }, + { + "name": "-[FChange description]", + "coverage": 0 + } + ] + }, + { + "name": "FAckUserWrite.m", + "coverage": 0.8888888888888888, + "type": "objc", + "functions": [ + { + "name": "-[FAckUserWrite initWithPath:affectedTree:revert:]", + "coverage": 1 + }, + { + "name": "-[FAckUserWrite operationForChild:]", + "coverage": 1 + }, + { + "name": "-[FAckUserWrite description]", + "coverage": 0 + } + ] + }, + { + "name": "FOperationSource.m", + "coverage": 0.8974358974358975, + "type": "objc", + "functions": [ + { + "name": "-[FOperationSource initWithFromUser:fromServer:queryParams:tagged:]", + "coverage": 1 + }, + { + "name": "+[FOperationSource userInstance]", + "coverage": 1 + }, + { + "name": "__32+[FOperationSource userInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FOperationSource serverInstance]", + "coverage": 1 + }, + { + "name": "__34+[FOperationSource serverInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FOperationSource forServerTaggedQuery:]", + "coverage": 1 + }, + { + "name": "-[FOperationSource description]", + "coverage": 0 + } + ] + }, + { + "name": "FMerge.m", + "coverage": 0.9, + "type": "objc", + "functions": [ + { + "name": "-[FMerge initWithSource:path:children:]", + "coverage": 1 + }, + { + "name": "-[FMerge operationForChild:]", + "coverage": 1 + }, + { + "name": "-[FMerge description]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDataSnapshot.m", + "coverage": 0.9016393442622951, + "type": "objc", + "functions": [ + { + "name": "-[FIRDataSnapshot initWithRef:indexedNode:]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot value]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot valueInExportFormat]", + "coverage": 0 + }, + { + "name": "-[FIRDataSnapshot childSnapshotForPath:]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot hasChild:]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot priority]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot hasChildren]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot exists]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot key]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot childrenCount]", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot children]", + "coverage": 1 + }, + { + "name": "__27-[FIRDataSnapshot children]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRDataSnapshot description]", + "coverage": 0 + } + ] + }, + { + "name": "FPruneForest.m", + "coverage": 0.9041095890410958, + "type": "objc", + "functions": [ + { + "name": "kFPrunePredicate_block_invoke", + "coverage": 1 + }, + { + "name": "kFKeepPredicate_block_invoke_2", + "coverage": 1 + }, + { + "name": "+[FPruneForest pruneTree]", + "coverage": 1 + }, + { + "name": "__25+[FPruneForest pruneTree]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FPruneForest keepTree]", + "coverage": 1 + }, + { + "name": "__24+[FPruneForest keepTree]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FPruneForest initWithForest:]", + "coverage": 1 + }, + { + "name": "+[FPruneForest empty]", + "coverage": 1 + }, + { + "name": "__21+[FPruneForest empty]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FPruneForest prunesAnything]", + "coverage": 1 + }, + { + "name": "-[FPruneForest shouldPruneUnkeptDescendantsAtPath:]", + "coverage": 1 + }, + { + "name": "-[FPruneForest shouldKeepPath:]", + "coverage": 0 + }, + { + "name": "-[FPruneForest affectsPath:]", + "coverage": 1 + }, + { + "name": "-[FPruneForest child:]", + "coverage": 0.8666666666666667 + }, + { + "name": "-[FPruneForest childAtPath:]", + "coverage": 1 + }, + { + "name": "-[FPruneForest prunePath:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FPruneForest keepPath:]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FPruneForest keepAll:atPath:]", + "coverage": 0.75 + }, + { + "name": "-[FPruneForest pruneAll:atPath:]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FPruneForest setPruneValue:forAll:atPath:]", + "coverage": 1 + }, + { + "name": "__44-[FPruneForest setPruneValue:forAll:atPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FPruneForest enumarateKeptNodesUsingBlock:]", + "coverage": 1 + }, + { + "name": "__45-[FPruneForest enumarateKeptNodesUsingBlock:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FChildrenNode.m", + "coverage": 0.9064171122994652, + "type": "objc", + "functions": [ + { + "name": "-[FChildrenNode init]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode initWithChildren:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode initWithPriority:children:]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FChildrenNode description]", + "coverage": 0 + }, + { + "name": "-[FChildrenNode isLeafNode]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode getPriority]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode updatePriority:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode getImmediateChild:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode getChild:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode hasChild:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode updateImmediateChild:withNewChild:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode updateChild:withNewChild:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode isEmpty]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode numChildren]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode val]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode valForExport:]", + "coverage": 0.7627118644067796 + }, + { + "name": "__30-[FChildrenNode valForExport:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FChildrenNode dataHash]", + "coverage": 1 + }, + { + "name": "__25-[FChildrenNode dataHash]_block_invoke", + "coverage": 1 + }, + { + "name": "__25-[FChildrenNode dataHash]_block_invoke.123", + "coverage": 1 + }, + { + "name": "__25-[FChildrenNode dataHash]_block_invoke.130", + "coverage": 1 + }, + { + "name": "__25-[FChildrenNode dataHash]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__25-[FChildrenNode dataHash]_block_invoke.158", + "coverage": 1 + }, + { + "name": "-[FChildrenNode compare:]", + "coverage": 0.5294117647058824 + }, + { + "name": "-[FChildrenNode isEqual:]", + "coverage": 0.9655172413793104 + }, + { + "name": "__25-[FChildrenNode isEqual:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FChildrenNode hash]", + "coverage": 1 + }, + { + "name": "__21-[FChildrenNode hash]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FChildrenNode enumerateChildrenAndPriorityUsingBlock:]", + "coverage": 1 + }, + { + "name": "__56-[FChildrenNode enumerateChildrenAndPriorityUsingBlock:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FChildrenNode enumerateChildrenUsingBlock:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode enumerateChildrenReverse:usingBlock:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode childEnumerator]", + "coverage": 1 + }, + { + "name": "__32-[FChildrenNode childEnumerator]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FChildrenNode predecessorChildKey:]", + "coverage": 1 + }, + { + "name": "-[FChildrenNode childrenGetter:]", + "coverage": 0 + }, + { + "name": "-[FChildrenNode firstChild]", + "coverage": 0.75 + }, + { + "name": "-[FChildrenNode lastChild]", + "coverage": 0.75 + } + ] + }, + { + "name": "FCompoundWrite.m", + "coverage": 0.908675799086758, + "type": "objc", + "functions": [ + { + "name": "-[FCompoundWrite initWithWriteTree:]", + "coverage": 1 + }, + { + "name": "+[FCompoundWrite compoundWriteWithValueDictionary:]", + "coverage": 1 + }, + { + "name": "__51+[FCompoundWrite compoundWriteWithValueDictionary:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FCompoundWrite compoundWriteWithNodeDictionary:]", + "coverage": 0 + }, + { + "name": "__50+[FCompoundWrite compoundWriteWithNodeDictionary:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FCompoundWrite emptyWrite]", + "coverage": 1 + }, + { + "name": "__28+[FCompoundWrite emptyWrite]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite addWrite:atPath:]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite addWrite:atKey:]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite addCompoundWrite:atPath:]", + "coverage": 1 + }, + { + "name": "__42-[FCompoundWrite addCompoundWrite:atPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite removeWriteAtPath:]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite hasCompleteWriteAtPath:]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite completeNodeAtPath:]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite completeChildren]", + "coverage": 1 + }, + { + "name": "__34-[FCompoundWrite completeChildren]_block_invoke", + "coverage": 1 + }, + { + "name": "__34-[FCompoundWrite completeChildren]_block_invoke.73", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite childCompoundWrites]", + "coverage": 1 + }, + { + "name": "__37-[FCompoundWrite childCompoundWrites]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite childCompoundWriteAtPath:]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite applySubtreeWrite:atPath:toNode:]", + "coverage": 1 + }, + { + "name": "__50-[FCompoundWrite applySubtreeWrite:atPath:toNode:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite enumerateWrites:]", + "coverage": 1 + }, + { + "name": "__34-[FCompoundWrite enumerateWrites:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite applyToNode:]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite isEmpty]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite rootWrite]", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite isEqual:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FCompoundWrite hash]", + "coverage": 0 + }, + { + "name": "-[FCompoundWrite valForExport:]", + "coverage": 1 + }, + { + "name": "__31-[FCompoundWrite valForExport:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FCompoundWrite description]", + "coverage": 0 + } + ] + }, + { + "name": "FIndex.m", + "coverage": 0.9090909090909091, + "type": "objc", + "functions": [ + { + "name": "+[FIndex indexFromQueryDefinition:]", + "coverage": 0.9090909090909091 + } + ] + }, + { + "name": "FTrackedQueryManager.m", + "coverage": 0.9108635097493036, + "type": "objc", + "functions": [ + { + "name": "-[FTrackedQueryManager initWithStorageEngine:clock:]", + "coverage": 1 + }, + { + "name": "__52-[FTrackedQueryManager initWithStorageEngine:clock:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FTrackedQueryManager assertValidTrackedQuery:]", + "coverage": 1 + }, + { + "name": "+[FTrackedQueryManager normalizeQuery:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager findTrackedQuery:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager removeTrackedQuery:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager setQueryActive:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager setQueryInactive:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager setQueryActive:forQuery:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager setQueryComplete:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FTrackedQueryManager setQueriesCompleteAtPath:]", + "coverage": 1 + }, + { + "name": "__49-[FTrackedQueryManager setQueriesCompleteAtPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "__49-[FTrackedQueryManager setQueriesCompleteAtPath:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager isQueryComplete:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager hasActiveDefaultQueryAtPath:]", + "coverage": 1 + }, + { + "name": "__52-[FTrackedQueryManager hasActiveDefaultQueryAtPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager ensureCompleteTrackedQueryAtPath:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager isIncludedInDefaultCompleteQuery:]", + "coverage": 1 + }, + { + "name": "__57-[FTrackedQueryManager isIncludedInDefaultCompleteQuery:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager cacheTrackedQuery:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager numberOfQueriesToPrune:prunableCount:]", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager pruneOldQueries:]", + "coverage": 1 + }, + { + "name": "__40-[FTrackedQueryManager pruneOldQueries:]_block_invoke", + "coverage": 1 + }, + { + "name": "__40-[FTrackedQueryManager pruneOldQueries:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__40-[FTrackedQueryManager pruneOldQueries:]_block_invoke.149", + "coverage": 0.8888888888888888 + }, + { + "name": "__40-[FTrackedQueryManager pruneOldQueries:]_block_invoke.168", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager numberOfPrunableQueries]", + "coverage": 0 + }, + { + "name": "__47-[FTrackedQueryManager numberOfPrunableQueries]_block_invoke", + "coverage": 0 + }, + { + "name": "__47-[FTrackedQueryManager numberOfPrunableQueries]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FTrackedQueryManager filteredQueryIdsAtPath:]", + "coverage": 1 + }, + { + "name": "__47-[FTrackedQueryManager filteredQueryIdsAtPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager knownCompleteChildrenAtPath:]", + "coverage": 1 + }, + { + "name": "__52-[FTrackedQueryManager knownCompleteChildrenAtPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "__52-[FTrackedQueryManager knownCompleteChildrenAtPath:]_block_invoke.206", + "coverage": 1 + }, + { + "name": "-[FTrackedQueryManager verifyCache]", + "coverage": 0.9130434782608695 + }, + { + "name": "__35-[FTrackedQueryManager verifyCache]_block_invoke", + "coverage": 1 + }, + { + "name": "__35-[FTrackedQueryManager verifyCache]_block_invoke.220", + "coverage": 0.7777777777777778 + } + ] + }, + { + "name": "FRangedFilter.m", + "coverage": 0.9130434782608695, + "type": "objc", + "functions": [ + { + "name": "-[FRangedFilter initWithQueryParams:]", + "coverage": 1 + }, + { + "name": "+[FRangedFilter startPostFromQueryParams:]", + "coverage": 1 + }, + { + "name": "+[FRangedFilter endPostFromQueryParams:]", + "coverage": 1 + }, + { + "name": "-[FRangedFilter matchesKey:andNode:]", + "coverage": 1 + }, + { + "name": "-[FRangedFilter updateChildIn:forChildKey:newChild:affectedPath:fromSource:accumulator:]", + "coverage": 1 + }, + { + "name": "-[FRangedFilter updateFullNode:withNewNode:accumulator:]", + "coverage": 0.875 + }, + { + "name": "__56-[FRangedFilter updateFullNode:withNewNode:accumulator:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FRangedFilter updatePriority:forNode:]", + "coverage": 0 + }, + { + "name": "-[FRangedFilter filtersNodes]", + "coverage": 1 + } + ] + }, + { + "name": "FSyncPoint.m", + "coverage": 0.915, + "type": "objc", + "functions": [ + { + "name": "-[FSyncPoint initWithPersistenceManager:]", + "coverage": 1 + }, + { + "name": "-[FSyncPoint isEmpty]", + "coverage": 1 + }, + { + "name": "-[FSyncPoint applyOperation:toView:writesCache:serverCache:]", + "coverage": 1 + }, + { + "name": "__60-[FSyncPoint applyOperation:toView:writesCache:serverCache:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSyncPoint applyOperation:writesCache:serverCache:]", + "coverage": 1 + }, + { + "name": "__53-[FSyncPoint applyOperation:writesCache:serverCache:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSyncPoint addEventRegistration:forNonExistingViewForQuery:writesCache:serverCache:]", + "coverage": 1 + }, + { + "name": "__86-[FSyncPoint addEventRegistration:forNonExistingViewForQuery:writesCache:serverCache:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSyncPoint addEventRegistration:forExistingViewForQuery:]", + "coverage": 1 + }, + { + "name": "-[FSyncPoint removeEventRegistration:forQuery:cancelError:]", + "coverage": 0.6341463414634146 + }, + { + "name": "__59-[FSyncPoint removeEventRegistration:forQuery:cancelError:]_block_invoke", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FSyncPoint queryViews]", + "coverage": 1 + }, + { + "name": "__24-[FSyncPoint queryViews]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSyncPoint completeServerCacheAtPath:]", + "coverage": 1 + }, + { + "name": "__40-[FSyncPoint completeServerCacheAtPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSyncPoint viewForQuery:]", + "coverage": 1 + }, + { + "name": "-[FSyncPoint viewExistsForQuery:]", + "coverage": 1 + }, + { + "name": "-[FSyncPoint hasCompleteView]", + "coverage": 1 + }, + { + "name": "-[FSyncPoint completeView]", + "coverage": 1 + }, + { + "name": "__26-[FSyncPoint completeView]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FViewProcessor.m", + "coverage": 0.923728813559322, + "type": "objc", + "functions": [ + { + "name": "+[FNoCompleteChildSource instance]", + "coverage": 1 + }, + { + "name": "__34+[FNoCompleteChildSource instance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FNoCompleteChildSource completeChild:]", + "coverage": 0 + }, + { + "name": "-[FNoCompleteChildSource childByIndex:afterChild:isReverse:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeCompleteChildSource initWithWrites:viewCache:serverCache:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeCompleteChildSource completeChild:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeCompleteChildSource childByIndex:afterChild:isReverse:]", + "coverage": 1 + }, + { + "name": "-[FViewProcessor initWithFilter:]", + "coverage": 1 + }, + { + "name": "-[FViewProcessor applyOperationOn:operation:writesCache:completeCache:]", + "coverage": 0.875 + }, + { + "name": "-[FViewProcessor maybeAddValueFromOldViewCache:newViewCache:changes:]", + "coverage": 1 + }, + { + "name": "-[FViewProcessor generateEventCacheAfterServerEvent:path:writesCache:source:accumulator:]", + "coverage": 0.918918918918919 + }, + { + "name": "-[FViewProcessor applyServerOverwriteTo:changePath:snap:writesCache:completeCache:filterServerNode:accumulator:]", + "coverage": 0.8367346938775511 + }, + { + "name": "-[FViewProcessor applyUserOverwriteTo:changePath:changedSnap:writesCache:completeCache:accumulator:]", + "coverage": 0.9464285714285714 + }, + { + "name": "+[FViewProcessor cache:hasChild:]", + "coverage": 1 + }, + { + "name": "-[FViewProcessor applyUserMergeTo:path:changedChildren:writesCache:completeCache:accumulator:]", + "coverage": 1 + }, + { + "name": "__94-[FViewProcessor applyUserMergeTo:path:changedChildren:writesCache:completeCache:accumulator:]_block_invoke", + "coverage": 1 + }, + { + "name": "__94-[FViewProcessor applyUserMergeTo:path:changedChildren:writesCache:completeCache:accumulator:]_block_invoke.268", + "coverage": 1 + }, + { + "name": "-[FViewProcessor applyServerMergeTo:path:changedChildren:writesCache:completeCache:filterServerNode:accumulator:]", + "coverage": 1 + }, + { + "name": "__113-[FViewProcessor applyServerMergeTo:path:changedChildren:writesCache:completeCache:filterServerNode:accumulator:]_block_invoke", + "coverage": 1 + }, + { + "name": "__113-[FViewProcessor applyServerMergeTo:path:changedChildren:writesCache:completeCache:filterServerNode:accumulator:]_block_invoke.292", + "coverage": 1 + }, + { + "name": "-[FViewProcessor ackUserWriteOn:ackPath:affectedTree:writesCache:completeCache:accumulator:]", + "coverage": 0.9111111111111111 + }, + { + "name": "__92-[FViewProcessor ackUserWriteOn:ackPath:affectedTree:writesCache:completeCache:accumulator:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FViewProcessor revertUserWriteOn:path:writesCache:completeCache:accumulator:]", + "coverage": 0.9298245614035088 + }, + { + "name": "-[FViewProcessor listenCompleteOldCache:path:writesCache:serverCache:accumulator:]", + "coverage": 0 + } + ] + }, + { + "name": "FIROptions.m", + "coverage": 0.9489795918367347, + "type": "objc", + "functions": [ + { + "name": "+[FIROptions defaultOptions]", + "coverage": 0.8461538461538461 + }, + { + "name": "+[FIROptions initialize]", + "coverage": 1 + }, + { + "name": "+[FIROptions defaultOptionsDictionary]", + "coverage": 1 + }, + { + "name": "+[FIROptions plistFilePathWithName:]", + "coverage": 1 + }, + { + "name": "+[FIROptions resetDefaultOptions]", + "coverage": 1 + }, + { + "name": "-[FIROptions initInternalWithOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithContentsOfFile:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithGoogleAppID:GCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions APIKey]", + "coverage": 1 + }, + { + "name": "-[FIROptions checkEditingLocked]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAPIKey:]", + "coverage": 1 + }, + { + "name": "-[FIROptions clientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions trackingID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setTrackingID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions GCMSenderID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions projectID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setProjectID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions androidClientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAndroidClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions googleAppID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGoogleAppID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions libraryVersionID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setLibraryVersionID:]", + "coverage": 0 + }, + { + "name": "-[FIROptions databaseURL]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDatabaseURL:]", + "coverage": 1 + }, + { + "name": "-[FIROptions storageBucket]", + "coverage": 1 + }, + { + "name": "-[FIROptions setStorageBucket:]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDeepLinkURLScheme:]", + "coverage": 1 + }, + { + "name": "-[FIROptions bundleID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionaryWithInfoDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIROptions isMeasurementEnabled]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIROptions isAnalyticsCollectionExpicitlySet]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionEnabled]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionDeactivated]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsEnabled]", + "coverage": 0 + }, + { + "name": "-[FIROptions isSignInEnabled]", + "coverage": 0 + } + ] + }, + { + "name": "FLimitedFilter.m", + "coverage": 0.9504950495049505, + "type": "objc", + "functions": [ + { + "name": "-[FLimitedFilter initWithQueryParams:]", + "coverage": 1 + }, + { + "name": "-[FLimitedFilter updateChildIn:forChildKey:newChild:affectedPath:fromSource:accumulator:]", + "coverage": 1 + }, + { + "name": "-[FLimitedFilter fullLimitUpdateNode:forChildKey:newChild:fromSource:accumulator:]", + "coverage": 0.9368421052631579 + }, + { + "name": "-[FLimitedFilter updateFullNode:withNewNode:accumulator:]", + "coverage": 1 + }, + { + "name": "__57-[FLimitedFilter updateFullNode:withNewNode:accumulator:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FLimitedFilter updatePriority:forNode:]", + "coverage": 0 + }, + { + "name": "-[FLimitedFilter filtersNodes]", + "coverage": 1 + }, + { + "name": "-[FLimitedFilter indexedFilter]", + "coverage": 1 + } + ] + }, + { + "name": "FEventGenerator.m", + "coverage": 0.9534883720930233, + "type": "objc", + "functions": [ + { + "name": "-[FEventGenerator initWithQuery:]", + "coverage": 1 + }, + { + "name": "-[FEventGenerator generateEventsForChanges:eventCache:eventRegistrations:]", + "coverage": 1 + }, + { + "name": "-[FEventGenerator generateEvents:forType:changes:eventCache:eventRegistrations:]", + "coverage": 1 + }, + { + "name": "__80-[FEventGenerator generateEvents:forType:changes:eventCache:eventRegistrations:]_block_invoke", + "coverage": 0.6363636363636364 + }, + { + "name": "-[FEventGenerator generateEventForChange:registration:eventCache:]", + "coverage": 1 + } + ] + }, + { + "name": "FRangeMerge.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "-[FRangeMerge initWithStart:end:updates:]", + "coverage": 1 + }, + { + "name": "-[FRangeMerge applyToNode:]", + "coverage": 1 + }, + { + "name": "-[FRangeMerge updateRangeInNode:node:updates:]", + "coverage": 1 + }, + { + "name": "__46-[FRangeMerge updateRangeInNode:node:updates:]_block_invoke", + "coverage": 1 + }, + { + "name": "__46-[FRangeMerge updateRangeInNode:node:updates:]_block_invoke.41", + "coverage": 1 + }, + { + "name": "__46-[FRangeMerge updateRangeInNode:node:updates:]_block_invoke.45", + "coverage": 1 + }, + { + "name": "-[FRangeMerge description]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAnalyticsConfiguration.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "+[FIRAnalyticsConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__43+[FIRAnalyticsConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration postNotificationName:value:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAnalyticsConfiguration setMinimumSessionInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setSessionTimeoutInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:persistSetting:]", + "coverage": 1 + } + ] + }, + { + "name": "FTreeSortedDictionary.m", + "coverage": 0.96415770609319, + "type": "objc", + "functions": [ + { + "name": "-[FTreeSortedDictionary initWithComparator:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary initWithComparator:withRoot:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary insertKey:withValue:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary removeKey:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary get:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary getPredecessorKey:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary isEmpty]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary count]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary minKey]", + "coverage": 0 + }, + { + "name": "-[FTreeSortedDictionary maxKey]", + "coverage": 0 + }, + { + "name": "-[FTreeSortedDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary enumerateKeysAndObjectsReverse:usingBlock:]", + "coverage": 1 + }, + { + "name": "__67-[FTreeSortedDictionary enumerateKeysAndObjectsReverse:usingBlock:]_block_invoke", + "coverage": 1 + }, + { + "name": "__67-[FTreeSortedDictionary enumerateKeysAndObjectsReverse:usingBlock:]_block_invoke.60", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary contains:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary keyEnumerator]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary keyEnumeratorFrom:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary reverseKeyEnumerator]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionary reverseKeyEnumeratorFrom:]", + "coverage": 1 + }, + { + "name": "log_base2", + "coverage": 1 + }, + { + "name": "base1_2List_next", + "coverage": 1 + }, + { + "name": "bit_mask", + "coverage": 1 + }, + { + "name": "base1_2List_new", + "coverage": 1 + }, + { + "name": "base1_2List_free", + "coverage": 1 + }, + { + "name": "+[FTreeSortedDictionary buildBalancedTree:dictionary:subArrayStartIndex:length:]", + "coverage": 1 + }, + { + "name": "+[FTreeSortedDictionary rootFrom12List:keyList:dictionary:]", + "coverage": 1 + }, + { + "name": "__59+[FTreeSortedDictionary rootFrom12List:keyList:dictionary:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FTreeSortedDictionary fromDictionary:withComparator:]", + "coverage": 0.9411764705882353 + }, + { + "name": "__55+[FTreeSortedDictionary fromDictionary:withComparator:]_block_invoke", + "coverage": 1 + }, + { + "name": "__55+[FTreeSortedDictionary fromDictionary:withComparator:]_block_invoke.99", + "coverage": 0.7142857142857143 + } + ] + }, + { + "name": "FPath.m", + "coverage": 0.9644444444444444, + "type": "objc", + "functions": [ + { + "name": "+[FPath relativePathFrom:to:]", + "coverage": 0.8181818181818182 + }, + { + "name": "+[FPath pathWithString:]", + "coverage": 1 + }, + { + "name": "-[FPath initWith:]", + "coverage": 1 + }, + { + "name": "-[FPath initWithPieces:andPieceNum:]", + "coverage": 1 + }, + { + "name": "-[FPath copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[FPath description]", + "coverage": 1 + }, + { + "name": "-[FPath getFront]", + "coverage": 1 + }, + { + "name": "-[FPath length]", + "coverage": 1 + }, + { + "name": "-[FPath popFront]", + "coverage": 1 + }, + { + "name": "-[FPath getBack]", + "coverage": 1 + }, + { + "name": "-[FPath toString]", + "coverage": 1 + }, + { + "name": "-[FPath toStringWithTrailingSlash]", + "coverage": 1 + }, + { + "name": "-[FPath toStringWithTrailingSlash:]", + "coverage": 1 + }, + { + "name": "-[FPath wireFormat]", + "coverage": 1 + }, + { + "name": "-[FPath parent]", + "coverage": 1 + }, + { + "name": "-[FPath child:]", + "coverage": 1 + }, + { + "name": "-[FPath childFromString:]", + "coverage": 1 + }, + { + "name": "-[FPath isEmpty]", + "coverage": 1 + }, + { + "name": "+[FPath empty]", + "coverage": 1 + }, + { + "name": "__14+[FPath empty]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FPath contains:]", + "coverage": 1 + }, + { + "name": "-[FPath enumerateComponentsUsingBlock:]", + "coverage": 1 + }, + { + "name": "-[FPath compare:]", + "coverage": 1 + }, + { + "name": "-[FPath isEqual:]", + "coverage": 0.8888888888888888 + }, + { + "name": "-[FPath hash]", + "coverage": 1 + } + ] + }, + { + "name": "FQueryParams.m", + "coverage": 0.9703703703703703, + "type": "objc", + "functions": [ + { + "name": "+[FQueryParams defaultInstance]", + "coverage": 1 + }, + { + "name": "__31+[FQueryParams defaultInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FQueryParams init]", + "coverage": 1 + }, + { + "name": "-[FQueryParams indexStartValue]", + "coverage": 1 + }, + { + "name": "-[FQueryParams indexStartKey]", + "coverage": 1 + }, + { + "name": "-[FQueryParams indexEndValue]", + "coverage": 1 + }, + { + "name": "-[FQueryParams indexEndKey]", + "coverage": 1 + }, + { + "name": "-[FQueryParams hasAnchoredLimit]", + "coverage": 0 + }, + { + "name": "-[FQueryParams limit]", + "coverage": 1 + }, + { + "name": "-[FQueryParams hasStart]", + "coverage": 1 + }, + { + "name": "-[FQueryParams hasEnd]", + "coverage": 1 + }, + { + "name": "-[FQueryParams copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams mutableCopy]", + "coverage": 1 + }, + { + "name": "-[FQueryParams limitTo:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams limitToFirst:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams limitToLast:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams startAt:childKey:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams startAt:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams endAt:childKey:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams endAt:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams orderBy:]", + "coverage": 1 + }, + { + "name": "-[FQueryParams wireProtocolParams]", + "coverage": 0.9772727272727273 + }, + { + "name": "+[FQueryParams fromQueryObject:]", + "coverage": 0.95 + }, + { + "name": "-[FQueryParams isViewFromLeft]", + "coverage": 1 + }, + { + "name": "-[FQueryParams nodeFilter]", + "coverage": 1 + }, + { + "name": "-[FQueryParams isValid]", + "coverage": 1 + }, + { + "name": "-[FQueryParams loadsAllData]", + "coverage": 1 + }, + { + "name": "-[FQueryParams isDefault]", + "coverage": 1 + }, + { + "name": "-[FQueryParams description]", + "coverage": 1 + }, + { + "name": "-[FQueryParams isEqual:]", + "coverage": 0.8947368421052632 + }, + { + "name": "-[FQueryParams hash]", + "coverage": 1 + } + ] + }, + { + "name": "FSparseSnapshotTree.m", + "coverage": 0.9736842105263158, + "type": "objc", + "functions": [ + { + "name": "-[FSparseSnapshotTree init]", + "coverage": 1 + }, + { + "name": "-[FSparseSnapshotTree findPath:]", + "coverage": 1 + }, + { + "name": "-[FSparseSnapshotTree rememberData:onPath:]", + "coverage": 1 + }, + { + "name": "-[FSparseSnapshotTree forgetPath:]", + "coverage": 0.9318181818181818 + }, + { + "name": "__34-[FSparseSnapshotTree forgetPath:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSparseSnapshotTree forEachTreeAtPath:do:]", + "coverage": 1 + }, + { + "name": "__44-[FSparseSnapshotTree forEachTreeAtPath:do:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FSparseSnapshotTree forEachChild:]", + "coverage": 1 + } + ] + }, + { + "name": "FIndexedNode.m", + "coverage": 0.9736842105263158, + "type": "objc", + "functions": [ + { + "name": "+[FIndexedNode fallbackIndex]", + "coverage": 1 + }, + { + "name": "__29+[FIndexedNode fallbackIndex]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIndexedNode indexedNodeWithNode:]", + "coverage": 1 + }, + { + "name": "+[FIndexedNode indexedNodeWithNode:index:]", + "coverage": 1 + }, + { + "name": "-[FIndexedNode initWithNode:index:]", + "coverage": 1 + }, + { + "name": "-[FIndexedNode initWithNode:index:indexed:]", + "coverage": 1 + }, + { + "name": "-[FIndexedNode ensureIndexed]", + "coverage": 1 + }, + { + "name": "__29-[FIndexedNode ensureIndexed]_block_invoke", + "coverage": 1 + }, + { + "name": "__29-[FIndexedNode ensureIndexed]_block_invoke.35", + "coverage": 1 + }, + { + "name": "__29-[FIndexedNode ensureIndexed]_block_invoke.47", + "coverage": 1 + }, + { + "name": "-[FIndexedNode hasIndex:]", + "coverage": 1 + }, + { + "name": "-[FIndexedNode updateChild:withNewChild:]", + "coverage": 1 + }, + { + "name": "-[FIndexedNode updatePriority:]", + "coverage": 1 + }, + { + "name": "-[FIndexedNode firstChild]", + "coverage": 0.9166666666666666 + }, + { + "name": "-[FIndexedNode lastChild]", + "coverage": 0.9166666666666666 + }, + { + "name": "-[FIndexedNode predecessorForChildKey:childNode:index:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIndexedNode enumerateChildrenReverse:usingBlock:]", + "coverage": 1 + }, + { + "name": "__52-[FIndexedNode enumerateChildrenReverse:usingBlock:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIndexedNode childEnumerator]", + "coverage": 1 + } + ] + }, + { + "name": "FIndexedFilter.m", + "coverage": 0.9758064516129032, + "type": "objc", + "functions": [ + { + "name": "-[FIndexedFilter initWithIndex:]", + "coverage": 1 + }, + { + "name": "-[FIndexedFilter updateChildIn:forChildKey:newChild:affectedPath:fromSource:accumulator:]", + "coverage": 0.9375 + }, + { + "name": "-[FIndexedFilter updateFullNode:withNewNode:accumulator:]", + "coverage": 1 + }, + { + "name": "__57-[FIndexedFilter updateFullNode:withNewNode:accumulator:]_block_invoke", + "coverage": 1 + }, + { + "name": "__57-[FIndexedFilter updateFullNode:withNewNode:accumulator:]_block_invoke.52", + "coverage": 1 + }, + { + "name": "-[FIndexedFilter updatePriority:forNode:]", + "coverage": 1 + }, + { + "name": "-[FIndexedFilter filtersNodes]", + "coverage": 1 + }, + { + "name": "-[FIndexedFilter indexedFilter]", + "coverage": 1 + } + ] + }, + { + "name": "FArraySortedDictionary.m", + "coverage": 0.9759615384615384, + "type": "objc", + "functions": [ + { + "name": "-[FArraySortedDictionaryEnumerator initWithKeys:startPos:isReverse:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionaryEnumerator nextObject]", + "coverage": 1 + }, + { + "name": "+[FArraySortedDictionary fromDictionary:withComparator:]", + "coverage": 1 + }, + { + "name": "__56+[FArraySortedDictionary fromDictionary:withComparator:]_block_invoke", + "coverage": 1 + }, + { + "name": "__56+[FArraySortedDictionary fromDictionary:withComparator:]_block_invoke.43", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FArraySortedDictionary initWithComparator:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary initWithComparator:keys:values:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary findInsertPositionForKey:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary findKey:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary insertKey:withValue:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary removeKey:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary get:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary getPredecessorKey:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary isEmpty]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary count]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary minKey]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary maxKey]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary enumerateKeysAndObjectsReverse:usingBlock:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary contains:]", + "coverage": 0 + }, + { + "name": "-[FArraySortedDictionary keyEnumerator]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary keyEnumeratorFrom:]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary reverseKeyEnumerator]", + "coverage": 1 + }, + { + "name": "-[FArraySortedDictionary reverseKeyEnumeratorFrom:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentContainer.m", + "coverage": 0.9818181818181818, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:]", + "coverage": 1 + }, + { + "name": "__55+[FIRComponentContainer registerAsComponentRegistrant:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:inSet:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:registrants:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer populateComponentsFromRegisteredClasses:forApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer instantiateInstanceForProtocol:withBlock:]", + "coverage": 0.9285714285714286 + }, + { + "name": "-[FIRComponentContainer instanceForProtocol:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer removeAllCachedInstances]", + "coverage": 1 + } + ] + }, + { + "name": "FCompoundHash.m", + "coverage": 0.9883040935672515, + "type": "objc", + "functions": [ + { + "name": "-[FCompoundHashBuilder initWithSplitStrategy:]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder isBuildingRange]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder currentHashLength]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder currentPath]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder currentPathWithDepth:]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder enumerateCurrentPathToDepth:withBlock:]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder appendKey:toString:]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder ensureRange]", + "coverage": 1 + }, + { + "name": "__35-[FCompoundHashBuilder ensureRange]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder processLeaf:]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder startChild:]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder endChild]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder finishHashing]", + "coverage": 1 + }, + { + "name": "-[FCompoundHashBuilder endRange]", + "coverage": 1 + }, + { + "name": "-[FCompoundHash initWithPosts:hashes:]", + "coverage": 0.8181818181818182 + }, + { + "name": "+[FCompoundHash simpleSizeSplitStrategyForNode:]", + "coverage": 1 + }, + { + "name": "__48+[FCompoundHash simpleSizeSplitStrategyForNode:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FCompoundHash fromNode:]", + "coverage": 1 + }, + { + "name": "+[FCompoundHash fromNode:splitStrategy:]", + "coverage": 1 + }, + { + "name": "+[FCompoundHash processNode:builder:]", + "coverage": 1 + }, + { + "name": "__37+[FCompoundHash processNode:builder:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "main.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "main", + "coverage": 1 + } + ] + }, + { + "name": "FTransformedEnumerator.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FTransformedEnumerator initWithEnumerator:andTransform:]", + "coverage": 1 + }, + { + "name": "-[FTransformedEnumerator nextObject]", + "coverage": 1 + } + ] + }, + { + "name": "FClock.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FSystemClock currentTime]", + "coverage": 1 + }, + { + "name": "+[FSystemClock clock]", + "coverage": 1 + }, + { + "name": "__21+[FSystemClock clock]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FOffsetClock currentTime]", + "coverage": 1 + }, + { + "name": "-[FOffsetClock initWithClock:offset:]", + "coverage": 1 + } + ] + }, + { + "name": "FEmptyNode.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FEmptyNode emptyNode]", + "coverage": 1 + }, + { + "name": "__23+[FEmptyNode emptyNode]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FViewCache.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FViewCache initWithEventCache:serverCache:]", + "coverage": 1 + }, + { + "name": "-[FViewCache updateEventSnap:isComplete:isFiltered:]", + "coverage": 1 + }, + { + "name": "-[FViewCache updateServerSnap:isComplete:isFiltered:]", + "coverage": 1 + }, + { + "name": "-[FViewCache completeEventSnap]", + "coverage": 1 + }, + { + "name": "-[FViewCache completeServerSnap]", + "coverage": 1 + } + ] + }, + { + "name": "FAtomicNumber.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FAtomicNumber init]", + "coverage": 1 + }, + { + "name": "-[FAtomicNumber getAndIncrement]", + "coverage": 1 + } + ] + }, + { + "name": "FCacheNode.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FCacheNode initWithIndexedNode:isFullyInitialized:isFiltered:]", + "coverage": 1 + }, + { + "name": "-[FCacheNode isCompleteForPath:]", + "coverage": 1 + }, + { + "name": "-[FCacheNode isCompleteForChild:]", + "coverage": 1 + }, + { + "name": "-[FCacheNode node]", + "coverage": 1 + } + ] + }, + { + "name": "FTreeNode.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FTreeNode init]", + "coverage": 1 + } + ] + }, + { + "name": "FWriteTreeRef.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FWriteTreeRef initWithPath:writeTree:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeRef calculateCompleteEventCacheWithCompleteServerCache:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeRef calculateCompleteEventChildrenWithCompleteServerChildren:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeRef calculateEventCacheAfterServerOverwriteWithChildPath:existingEventSnap:existingServerSnap:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeRef shadowingWriteAtPath:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeRef calculateNextNodeAfterPost:completeServerData:reverse:index:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeRef calculateCompleteChild:cache:]", + "coverage": 1 + }, + { + "name": "-[FWriteTreeRef childWriteTreeRef:]", + "coverage": 1 + } + ] + }, + { + "name": "FSnapshotHolder.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FSnapshotHolder init]", + "coverage": 1 + }, + { + "name": "-[FSnapshotHolder getNode:]", + "coverage": 1 + }, + { + "name": "-[FSnapshotHolder updateSnapshot:withNewSnapshot:]", + "coverage": 1 + } + ] + }, + { + "name": "FTuplePathValue.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FTuplePathValue initWithPath:value:]", + "coverage": 1 + } + ] + }, + { + "name": "FTupleRemovedQueriesEvents.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FTupleRemovedQueriesEvents initWithRemovedQueries:cancelEvents:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentType.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentType instanceForProtocol:inContainer:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponent.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponent componentWithProtocol:creationBlock:]", + "coverage": 1 + }, + { + "name": "+[FIRComponent componentWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRComponent initWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAppAssociationRegistration.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRAppAssociationRegistration registeredObjectWithHost:key:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FTreeSortedDictionaryEnumerator.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FTreeSortedDictionaryEnumerator initWithImmutableSortedDictionary:startKey:isReverse:]", + "coverage": 1 + }, + { + "name": "-[FTreeSortedDictionaryEnumerator nextObject]", + "coverage": 1 + } + ] + }, + { + "name": "slice.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::Slice::Slice()", + "coverage": 1 + }, + { + "name": "leveldb::Slice::Slice(char const*, unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::Slice(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::Slice(char const*)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::data() const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::size() const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::empty() const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::operator[](unsigned long) const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::clear()", + "coverage": 1 + }, + { + "name": "leveldb::Slice::remove_prefix(unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::ToString() const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::starts_with(leveldb::Slice const&) const", + "coverage": 1 + }, + { + "name": "leveldb::operator==(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::operator!=(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::compare(leveldb::Slice const&) const", + "coverage": 1 + } + ] + }, + { + "name": "options.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::ReadOptions::ReadOptions()", + "coverage": 1 + }, + { + "name": "leveldb::WriteOptions::WriteOptions()", + "coverage": 1 + } + ] + }, + { + "name": "FViewProcessorResult.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FViewProcessorResult initWithViewCache:changes:]", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "DynamicLinks_Example_iOS.app", + "coverage": 0.7375878853434289, + "files": [ + { + "name": "GINArgument.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[GINArgument argumentWithObject:]", + "coverage": 0 + }, + { + "name": "+[GINArgument argumentWithInteger:]", + "coverage": 0 + }, + { + "name": "+[GINArgument setNextArgumentInList:atIndex:inInvocation:]", + "coverage": 0 + }, + { + "name": "-[GINArgument setArgumentInInvocation:atIndex:]", + "coverage": 0 + } + ] + }, + { + "name": "GINInvocation.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[GINInvocation objectByPerformingSelector:onTarget:numberOfArguments:]", + "coverage": 0 + }, + { + "name": "+[GINInvocation doubleByPerformingSelector:onTarget:numberOfArguments:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDLDefaultRetrievalProcessV2.m", + "coverage": 0.11191335740072202, + "type": "objc", + "functions": [ + { + "name": "-[FIRDLDefaultRetrievalProcessV2 initWithNetworkingService:clientID:URLScheme:APIKey:FDLSDKVersion:delegate:]", + "coverage": 1 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 retrievePendingDynamicLink]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 isCompleted]", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 javaScriptExecutor:completedExecutionWithResult:]", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 javaScriptExecutor:failedWithError:]", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 retrievePendingDynamicLinkInternal]", + "coverage": 0 + }, + { + "name": "__68-[FIRDLDefaultRetrievalProcessV2 retrievePendingDynamicLinkInternal]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 foundResultsWithDynamicLinks]", + "coverage": 0 + }, + { + "name": "__62-[FIRDLDefaultRetrievalProcessV2 foundResultsWithDynamicLinks]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 resultsWithErrors]", + "coverage": 0 + }, + { + "name": "__51-[FIRDLDefaultRetrievalProcessV2 resultsWithErrors]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 results]", + "coverage": 0 + }, + { + "name": "__41-[FIRDLDefaultRetrievalProcessV2 results]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 resultWithUniqueMatchedDynamicLink]", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 handleRequestResultsUpdated]", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 markCompleted]", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 uniqueMatchLinkToCheck]", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 clearUsedUniqueMatchLinkToCheckFromClipboard]", + "coverage": 0 + }, + { + "name": "-[FIRDLDefaultRetrievalProcessV2 fetchLocaleFromWebView]", + "coverage": 0.8 + } + ] + }, + { + "name": "FIRInstanceIDTokenDeleteOperation.m", + "coverage": 0.12643678160919541, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenDeleteOperation initWithAuthorizedEntity:scope:checkinPreferences:keyPair:action:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenDeleteOperation performTokenOperation]", + "coverage": 0 + }, + { + "name": "__58-[FIRInstanceIDTokenDeleteOperation performTokenOperation]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenDeleteOperation handleResponseWithData:response:error:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDLJavaScriptExecutor.m", + "coverage": 0.1736111111111111, + "type": "objc", + "functions": [ + { + "name": "FIRDLTypeofFingerprintJSMethodNameString", + "coverage": 0 + }, + { + "name": "__FIRDLTypeofFingerprintJSMethodNameString_block_invoke", + "coverage": 0 + }, + { + "name": "GINFingerprintJSMethodString", + "coverage": 0 + }, + { + "name": "__GINFingerprintJSMethodString_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDLJavaScriptExecutor initWithDelegate:script:]", + "coverage": 1 + }, + { + "name": "-[FIRDLJavaScriptExecutor start]", + "coverage": 0.75 + }, + { + "name": "-[FIRDLJavaScriptExecutor handleExecutionResult:]", + "coverage": 0 + }, + { + "name": "-[FIRDLJavaScriptExecutor handleExecutionError:]", + "coverage": 0 + }, + { + "name": "-[FIRDLJavaScriptExecutor cleanup]", + "coverage": 0 + }, + { + "name": "-[FIRDLJavaScriptExecutor webView:didFinishNavigation:]", + "coverage": 0 + }, + { + "name": "__55-[FIRDLJavaScriptExecutor webView:didFinishNavigation:]_block_invoke", + "coverage": 0 + }, + { + "name": "__55-[FIRDLJavaScriptExecutor webView:didFinishNavigation:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRDLJavaScriptExecutor webView:didFailNavigation:withError:]", + "coverage": 0 + }, + { + "name": "-[FIRDLJavaScriptExecutor webViewDidFinishLoad:]", + "coverage": 0 + }, + { + "name": "-[FIRDLJavaScriptExecutor webView:didFailLoadWithError:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAppDelegate.m", + "coverage": 0.2413793103448276, + "type": "objc", + "functions": [ + { + "name": "-[FIRAppDelegate application:didFinishLaunchingWithOptions:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillResignActive:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidEnterBackground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationWillEnterForeground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidBecomeActive:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillTerminate:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDynamicLinkNetworking.m", + "coverage": 0.30670103092783507, + "type": "objc", + "functions": [ + { + "name": "FIRURLParameterString", + "coverage": 0.6666666666666666 + }, + { + "name": "FIRDynamicLinkAPIKeyParameter", + "coverage": 1 + }, + { + "name": "FIRMakeHTTPRequest", + "coverage": 1 + }, + { + "name": "__FIRMakeHTTPRequest_block_invoke", + "coverage": 0 + }, + { + "name": "FIRDataWithDictionary", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkNetworking initWithAPIKey:clientID:URLScheme:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkNetworking resolveShortLink:FDLSDKVersion:completion:]", + "coverage": 0.9387755102040817 + }, + { + "name": "__70-[FIRDynamicLinkNetworking resolveShortLink:FDLSDKVersion:completion:]_block_invoke", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRDynamicLinkNetworking retrievePendingDynamicLinkWithIOSVersion:resolutionHeight:resolutionWidth:locale:localeRaw:localeFromWebView:timezone:modelName:FDLSDKVersion:appInstallationDate:uniqueMatchVisualStyle:retrievalProcessType:uniqueMatchLinkToCheck:handler:]", + "coverage": 0 + }, + { + "name": "__265-[FIRDynamicLinkNetworking retrievePendingDynamicLinkWithIOSVersion:resolutionHeight:resolutionWidth:locale:localeRaw:localeFromWebView:timezone:modelName:FDLSDKVersion:appInstallationDate:uniqueMatchVisualStyle:retrievalProcessType:uniqueMatchLinkToCheck:handler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinkNetworking convertInvitation:handler:]", + "coverage": 0 + }, + { + "name": "__54-[FIRDynamicLinkNetworking convertInvitation:handler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__54-[FIRDynamicLinkNetworking convertInvitation:handler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinkNetworking sendRequestWithBaseURLString:requestBody:endpointPath:parserBlock:completion:]", + "coverage": 0 + }, + { + "name": "__105-[FIRDynamicLinkNetworking sendRequestWithBaseURLString:requestBody:endpointPath:parserBlock:completion:]_block_invoke", + "coverage": 0 + }, + { + "name": "__105-[FIRDynamicLinkNetworking sendRequestWithBaseURLString:requestBody:endpointPath:parserBlock:completion:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__105-[FIRDynamicLinkNetworking sendRequestWithBaseURLString:requestBody:endpointPath:parserBlock:completion:]_block_invoke.201", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinkNetworking executeOnePlatformRequest:forURL:completionHandler:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceID+Private.m", + "coverage": 0.3333333333333333, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceID(Private) cachedCheckinPreferences]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID(Private) fetchCheckinInfoWithHandler:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID(Private) appInstanceID:]", + "coverage": 1 + } + ] + }, + { + "name": "NSError+FIRInstanceID.m", + "coverage": 0.375, + "type": "objc", + "functions": [ + { + "name": "-[NSError(FIRInstanceID) instanceIDErrorCode]", + "coverage": 0 + }, + { + "name": "+[NSError(FIRInstanceID) errorWithFIRInstanceIDErrorCode:]", + "coverage": 1 + }, + { + "name": "+[NSError(FIRInstanceID) errorWithFIRInstanceIDErrorCode:userInfo:]", + "coverage": 1 + }, + { + "name": "+[NSError(FIRInstanceID) FIRInstanceIDErrorMissingCheckin]", + "coverage": 0 + } + ] + }, + { + "name": "FIRViewController.m", + "coverage": 0.5, + "type": "objc", + "functions": [ + { + "name": "-[FIRViewController viewDidLoad]", + "coverage": 1 + }, + { + "name": "-[FIRViewController didReceiveMemoryWarning]", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDStringEncoding.m", + "coverage": 0.5308641975308642, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDStringEncoding rfc4648Base64WebsafeStringEncoding]", + "coverage": 1 + }, + { + "name": "lcm", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStringEncoding stringEncodingWithString:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding initWithString:]", + "coverage": 0.6896551724137931 + }, + { + "name": "-[FIRInstanceIDStringEncoding description]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDStringEncoding doPad]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDStringEncoding setDoPad:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding setPaddingChar:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding encode:]", + "coverage": 0.8444444444444444 + }, + { + "name": "-[FIRInstanceIDStringEncoding decode:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDynamicLinkComponentsKeyProvider.m", + "coverage": 0.6, + "type": "objc", + "functions": [ + { + "name": "+[FIRDynamicLinkComponentsKeyProvider APIKey]", + "coverage": 0.6 + } + ] + }, + { + "name": "FIRDynamicLinks.m", + "coverage": 0.6314878892733564, + "type": "objc", + "functions": [ + { + "name": "+[FIRDynamicLinks load]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinks componentsToRegister]", + "coverage": 1 + }, + { + "name": "__39+[FIRDynamicLinks componentsToRegister]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinks configureWithApp:]", + "coverage": 0.6 + }, + { + "name": "-[FIRDynamicLinks configureDynamicLinks:]", + "coverage": 0.391304347826087 + }, + { + "name": "-[FIRDynamicLinks initWithAnalytics:]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinks dynamicLinks]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinks setUpWithLaunchOptions:apiKey:clientID:urlScheme:userDefaults:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinks checkForPendingDynamicLinkUsingExperimentalRetrievalProcess]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks checkForPendingDynamicLink]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinks sharedInstance]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks checkForPendingDeepLink]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks deepLinkFromCustomSchemeURL:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks deepLinkFromUniversalLinkURL:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks shouldHandleDeepLinkFromCustomSchemeURL:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks shouldHandleDynamicLinkFromCustomSchemeURL:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks dynamicLinkFromCustomSchemeURL:]", + "coverage": 0.9444444444444444 + }, + { + "name": "-[FIRDynamicLinks dynamicLinkFromUniversalLinkURL:]", + "coverage": 1 + }, + { + "name": "__51-[FIRDynamicLinks dynamicLinkFromUniversalLinkURL:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinks handleUniversalLink:completion:]", + "coverage": 1 + }, + { + "name": "__50-[FIRDynamicLinks handleUniversalLink:completion:]_block_invoke", + "coverage": 0.8181818181818182 + }, + { + "name": "__50-[FIRDynamicLinks handleUniversalLink:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinks resolveShortLink:completion:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinks matchesShortLinkFormat:]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinks isAutomaticRetrievalEnabled]", + "coverage": 0.75 + }, + { + "name": "-[FIRDynamicLinks dynamicLinkNetworking]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinks canParseCustomSchemeURL:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinks canParseUniversalLinkURL:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinks handleIncomingCustomSchemeDeepLink:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks passRetrievedDynamicLinkToApplication:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks handlePendingDynamicLinkRetrievalFailureWithErrorCode:errorDescription:underlyingError:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLinks retrievalProcess:completedWithResult:]", + "coverage": 0 + }, + { + "name": "+[FIRDynamicLinks genericDiagnosticInformation]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinks diagnosticAnalyzeEntitlements]", + "coverage": 0 + }, + { + "name": "+[FIRDynamicLinks performDiagnosticsIncludingHeaderFooter:detectedErrors:]", + "coverage": 0.7671232876712328 + }, + { + "name": "+[FIRDynamicLinks performDiagnosticsWithCompletion:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDTokenManager.m", + "coverage": 0.7067039106145251, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenManager init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager configureTokenOperations]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke.52", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke_2.53", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager cachedTokenInfoWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]", + "coverage": 0.9166666666666666 + }, + { + "name": "__83-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__83-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke.86", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteAllTokensLocallyWithHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager stopAllTokenOperations]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager store:didDeleteFCMScopedTokensForCheckin:]", + "coverage": 0 + }, + { + "name": "__70-[FIRInstanceIDTokenManager store:didDeleteFCMScopedTokensForCheckin:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager createFetchOperationWithAuthorizedEntity:scope:options:keyPair:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager createDeleteOperationWithAuthorizedEntity:scope:checkinPreferences:keyPair:action:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager checkForTokenRefreshPolicy]", + "coverage": 0.8125 + }, + { + "name": "-[FIRInstanceIDTokenManager updateTokensToAPNSDeviceToken:isSandbox:]", + "coverage": 0.9333333333333333 + } + ] + }, + { + "name": "FIRInstanceIDLogger.m", + "coverage": 0.72, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDLogger formatMessageCode:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncDebug:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncInfo:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncNotice:messageCode:msg:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDLogger logFuncWarning:messageCode:msg:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDLogger logFuncError:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "FIRInstanceIDSharedLogger", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDSharedLogger_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceID.m", + "coverage": 0.7266414141414141, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDResult copyWithZone:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID instanceID]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIRInstanceID initPrivately]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID instanceIDForTests]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID token]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID instanceIDWithHandler:]", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceID instanceIDWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceID instanceIDWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID cachedTokenIfAvailable]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setDefaultFCMToken:]", + "coverage": 0.8666666666666667 + }, + { + "name": "-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]", + "coverage": 0.8584905660377359 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke.163", + "coverage": 0.9302325581395349 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke_2.171", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]", + "coverage": 0.84375 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke", + "coverage": 0.8 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke.195", + "coverage": 0.8695652173913043 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke_2.196", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID asyncLoadKeyPairWithHandler:]", + "coverage": 1 + }, + { + "name": "__45-[FIRInstanceID asyncLoadKeyPairWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__45-[FIRInstanceID asyncLoadKeyPairWithHandler:]_block_invoke_2", + "coverage": 0.7222222222222222 + }, + { + "name": "-[FIRInstanceID getIDWithHandler:]", + "coverage": 0.7837837837837838 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke.223", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteIDWithHandler:]", + "coverage": 0.8333333333333334 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke", + "coverage": 0.5555555555555556 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke.240", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke_2.241", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke.250", + "coverage": 0.8 + }, + { + "name": "-[FIRInstanceID notifyIdentityReset]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteIdentityWithHandler:]", + "coverage": 1 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke", + "coverage": 0.9137931034482759 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_4", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID load]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID componentsToRegister]", + "coverage": 1 + }, + { + "name": "__37+[FIRInstanceID componentsToRegister]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID configureWithApp:]", + "coverage": 0.4444444444444444 + }, + { + "name": "-[FIRInstanceID configureInstanceIDWithOptions:app:]", + "coverage": 0.7222222222222222 + }, + { + "name": "+[FIRInstanceID configureErrorWithReason:]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID exitWithReason:forFirebaseApp:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID didCompleteConfigure]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID isFCMAutoInitEnabled]", + "coverage": 0.8709677419354839 + }, + { + "name": "-[FIRInstanceID start]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FIRInstanceID setupTokenManager]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setupKeyPairManager]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setupNotificationListeners]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID maxRetryCountForDefaultToken]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID minIntervalForDefaultTokenRetry]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID maxRetryIntervalForDefaultTokenInSeconds]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID retryIntervalToFetchDefaultToken]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIRInstanceID fetchDefaultToken]", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID fetchDefaultToken]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID defaultTokenWithHandler:]", + "coverage": 0.89 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke", + "coverage": 0.8133333333333334 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke.406", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID notifyAPNSTokenIsSet:]", + "coverage": 0 + }, + { + "name": "__38-[FIRInstanceID notifyAPNSTokenIsSet:]_block_invoke", + "coverage": 0 + }, + { + "name": "__38-[FIRInstanceID notifyAPNSTokenIsSet:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID isSandboxApp]", + "coverage": 1 + }, + { + "name": "__29-[FIRInstanceID isSandboxApp]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID isProductionApp]", + "coverage": 0.22556390977443608 + }, + { + "name": "-[FIRInstanceID logAPNSConfigurationError:]", + "coverage": 0.875 + } + ] + }, + { + "name": "FDLUtilities.m", + "coverage": 0.7358490566037735, + "type": "objc", + "functions": [ + { + "name": "FIRDLCookieRetrievalURL", + "coverage": 1 + }, + { + "name": "FIRDLURLQueryStringFromDictionary", + "coverage": 1 + }, + { + "name": "__FIRDLURLQueryStringFromDictionary_block_invoke", + "coverage": 1 + }, + { + "name": "FIRDLDictionaryFromQuery", + "coverage": 1 + }, + { + "name": "FIRDLDeepLinkURLWithInviteID", + "coverage": 0.926829268292683 + }, + { + "name": "FIRDLOSVersionSupported", + "coverage": 1 + }, + { + "name": "FIRDLAppInstallationDate", + "coverage": 0 + }, + { + "name": "FIRDLDeviceModelName", + "coverage": 0 + }, + { + "name": "__FIRDLDeviceModelName_block_invoke", + "coverage": 0 + }, + { + "name": "FIRDLDeviceLocale", + "coverage": 1 + }, + { + "name": "FIRDLDeviceLocaleRaw", + "coverage": 1 + }, + { + "name": "FIRDLDeviceTimezone", + "coverage": 1 + }, + { + "name": "FIRDLIsURLForWhiteListedCustomDomain", + "coverage": 1 + }, + { + "name": "FIRDLCanParseUniversalLinkURL", + "coverage": 1 + }, + { + "name": "FIRDLMatchesShortLinkFormat", + "coverage": 1 + }, + { + "name": "FIRDLMatchTypeStringFromServerString", + "coverage": 0 + }, + { + "name": "__FIRDLMatchTypeStringFromServerString_block_invoke", + "coverage": 0 + }, + { + "name": "FIRDLAddToAllowListForCustomDomainsArray", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDTokenFetchOperation.m", + "coverage": 0.75, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenFetchOperation initWithAuthorizedEntity:scope:options:checkinPreferences:keyPair:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation performTokenOperation]", + "coverage": 0.9206349206349206 + }, + { + "name": "__57-[FIRInstanceIDTokenFetchOperation performTokenOperation]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation handleResponseWithData:response:error:]", + "coverage": 0.5454545454545454 + }, + { + "name": "__74-[FIRInstanceIDTokenFetchOperation handleResponseWithData:response:error:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation parseFetchTokenResponse:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDVersionUtilities.m", + "coverage": 0.775, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDParseCurrentLibraryVersion", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDParseCurrentLibraryVersion_block_invoke", + "coverage": 0.9285714285714286 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionMajor", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionMinor", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionPatch", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionIsBeta", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDURLQueryItem.m", + "coverage": 0.78125, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDURLQueryItem queryItemWithName:value:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDURLQueryItem initWithName:value:]", + "coverage": 1 + }, + { + "name": "FIRInstanceIDQueryFromQueryItems", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FIRInstanceIDBackupExcludedPlist.m", + "coverage": 0.7816901408450704, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDBackupExcludedPlist initWithFileName:subDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist writeDictionary:error:]", + "coverage": 0.696969696969697 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist deleteFile:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist contentAsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist moveToApplicationSupportSubDirectory:]", + "coverage": 0.5428571428571428 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist doesFileExist]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist plistDirectory]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist plistPathInDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist pathWithName:inDirectory:]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist doesFileExistInDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist supportedDirectory]", + "coverage": 1 + } + ] + }, + { + "name": "FIRDependency.m", + "coverage": 0.7857142857142857, + "type": "objc", + "functions": [ + { + "name": "+[FIRDependency dependencyWithProtocol:]", + "coverage": 0 + }, + { + "name": "+[FIRDependency dependencyWithProtocol:isRequired:]", + "coverage": 1 + }, + { + "name": "-[FIRDependency initWithProtocol:isRequired:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRBundleUtil.m", + "coverage": 0.7872340425531915, + "type": "objc", + "functions": [ + { + "name": "+[FIRBundleUtil relevantBundles]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil optionsDictionaryPathWithResourceName:andFileType:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil relevantURLSchemes]", + "coverage": 0 + }, + { + "name": "+[FIRBundleUtil hasBundleIdentifierPrefix:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil bundleIdentifierByRemovingLastPartFrom:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinStore.m", + "coverage": 0.7874396135265701, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinStore initWithCheckinPlistFileName:subDirectoryName:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore initWithCheckinPlist:keychain:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore hasCheckinPlist]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore bundleIdentifierForKeychainAccount]", + "coverage": 1 + }, + { + "name": "__63-[FIRInstanceIDCheckinStore bundleIdentifierForKeychainAccount]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore saveCheckinPreferences:handler:]", + "coverage": 0.5573770491803278 + }, + { + "name": "__60-[FIRInstanceIDCheckinStore saveCheckinPreferences:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore removeCheckinPreferencesWithHandler:]", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceIDCheckinStore removeCheckinPreferencesWithHandler:]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRInstanceIDCheckinStore cachedCheckinPreferences]", + "coverage": 0.7073170731707317 + }, + { + "name": "-[FIRInstanceIDCheckinStore migrateCheckinItemIfNeeded]", + "coverage": 1 + } + ] + }, + { + "name": "FIRDynamicLink.m", + "coverage": 0.8, + "type": "objc", + "functions": [ + { + "name": "-[FIRDynamicLink description]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLink initWithParametersDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLink setUrl:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLink setMinimumAppVersion:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLink setInviteId:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLink setWeakMatchEndpoint:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLink setMatchType:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLink setMatchMessage:]", + "coverage": 0 + }, + { + "name": "-[FIRDynamicLink setParametersDictionaryValue:forKey:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLink matchConfidence]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLink stringWithMatchType:]", + "coverage": 0.8333333333333334 + }, + { + "name": "+[FIRDynamicLink matchTypeWithString:]", + "coverage": 1 + }, + { + "name": "__38+[FIRDynamicLink matchTypeWithString:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FDLLogging.m", + "coverage": 0.8064516129032258, + "type": "objc", + "functions": [ + { + "name": "FDLMessageCodeForLogIdentifier", + "coverage": 1 + }, + { + "name": "FDLLog", + "coverage": 0.7692307692307693 + } + ] + }, + { + "name": "FIRConfiguration.m", + "coverage": 0.8076923076923077, + "type": "objc", + "functions": [ + { + "name": "+[FIRConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__34+[FIRConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration init]", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration setLoggerLevel:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDLRetrievalProcessResult.m", + "coverage": 0.8076923076923077, + "type": "objc", + "functions": [ + { + "name": "-[FIRDLRetrievalProcessResult initWithDynamicLink:error:message:matchSource:]", + "coverage": 1 + }, + { + "name": "-[FIRDLRetrievalProcessResult URLWithCustomURLScheme:]", + "coverage": 0.7058823529411765 + } + ] + }, + { + "name": "FIRLogger.m", + "coverage": 0.8118811881188119, + "type": "objc", + "functions": [ + { + "name": "FIRLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__FIRLoggerInitializeASL_block_invoke", + "coverage": 0.9333333333333333 + }, + { + "name": "FIRSetAnalyticsDebugMode", + "coverage": 0 + }, + { + "name": "FIRSetLoggerLevel", + "coverage": 0 + }, + { + "name": "FIRResetLogger", + "coverage": 1 + }, + { + "name": "FIRSetLoggerUserDefaults", + "coverage": 1 + }, + { + "name": "FIRIsLoggableLevel", + "coverage": 0 + }, + { + "name": "FIRLogBasic", + "coverage": 1 + }, + { + "name": "Definition at 158:95", + "coverage": 1 + }, + { + "name": "+[FIRLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDTokenOperation.m", + "coverage": 0.8323353293413174, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDTokenOperation sharedURLSession]", + "coverage": 0 + }, + { + "name": "__47+[FIRInstanceIDTokenOperation sharedURLSession]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenOperation initWithAction:forAuthorizedEntity:scope:options:checkinPreferences:keyPair:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation addCompletionHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation isAsynchronous]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenOperation isExecuting]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation setExecuting:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation isFinished]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation setFinished:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation start]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRInstanceIDTokenOperation finishWithResult:token:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation cancel]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation performTokenOperation]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceIDTokenOperation requestWithAuthHeader:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenOperation standardQueryItemsWithDeviceID:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation queryItemsWithKeyPair:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenOperation HTTPAuthHeaderFromCheckin:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPairStore.m", + "coverage": 0.8351449275362319, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDKeyDataWithTag", + "coverage": 0.8333333333333334 + }, + { + "name": "FIRInstanceIDCachedKeyRefWithTag", + "coverage": 0.7777777777777778 + }, + { + "name": "FIRInstanceIDHasMigratedKeyPair", + "coverage": 1 + }, + { + "name": "FIRInstanceIDLegacyPublicTagWithSubtype", + "coverage": 1 + }, + { + "name": "FIRInstanceIDLegacyPrivateTagWithSubtype", + "coverage": 1 + }, + { + "name": "FIRInstanceIDPublicTagWithSubtype", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDPublicTagWithSubtype_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDPrivateTagWithSubtype", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDPrivateTagWithSubtype_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCreationTimeKeyWithSubtype", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore invalidateKeyPairsIfNeeded]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore hasCachedKeyPairs]", + "coverage": 0.29411764705882354 + }, + { + "name": "-[FIRInstanceIDKeyPairStore appIdentityWithError:]", + "coverage": 0.6 + }, + { + "name": "-[FIRInstanceIDKeyPairStore loadKeyPairWithError:]", + "coverage": 0.78125 + }, + { + "name": "-[FIRInstanceIDKeyPairStore generateAndSaveKeyWithSubtype:creationTime:error:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRInstanceIDKeyPairStore validCachedKeyPairWithSubtype:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore cachedKeyPairWithSubtype:error:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDKeyPairStore keyPairForPrivateKeyTag:publicKeyTag:error:]", + "coverage": 0.8387096774193549 + }, + { + "name": "-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]", + "coverage": 0.8771929824561403 + }, + { + "name": "__68-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]_block_invoke", + "coverage": 0.8636363636363636 + }, + { + "name": "__68-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]_block_invoke_2", + "coverage": 0.6428571428571429 + }, + { + "name": "-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]", + "coverage": 1 + }, + { + "name": "__58-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]_block_invoke", + "coverage": 0.7619047619047619 + }, + { + "name": "__58-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore deleteSavedKeyPairWithSubtype:handler:]", + "coverage": 0.9375 + }, + { + "name": "__67-[FIRInstanceIDKeyPairStore deleteSavedKeyPairWithSubtype:handler:]_block_invoke", + "coverage": 0.76 + }, + { + "name": "+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]", + "coverage": 1 + }, + { + "name": "__75+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]_block_invoke", + "coverage": 0.7619047619047619 + }, + { + "name": "__75+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]_block_invoke_2", + "coverage": 0.5454545454545454 + }, + { + "name": "-[FIRInstanceIDKeyPairStore removeKeyPairCreationTimePlistWithError:]", + "coverage": 0.5 + }, + { + "name": "+[FIRInstanceIDKeyPairStore keyStoreFileName]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDUtilities.m", + "coverage": 0.8547008547008547, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDRegisterServer", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentTimestampInSeconds", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentTimestampInMilliseconds", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentAppVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAppIdentifier", + "coverage": 1 + }, + { + "name": "FIRInstanceIDFirebaseAppID", + "coverage": 1 + }, + { + "name": "FIRInstanceIDDeviceModel", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDDeviceModel_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDOperatingSystemVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDHasLocaleChanged", + "coverage": 1 + }, + { + "name": "FIRInstanceIDIsValidGCMScope", + "coverage": 1 + }, + { + "name": "FIRInstanceIDStringForAPNSDeviceToken", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAPNSTupleStringForTokenAndServerType", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentGCMVersion", + "coverage": 0.6 + }, + { + "name": "FIRInstanceIDCurrentLocale", + "coverage": 0.4583333333333333 + } + ] + }, + { + "name": "FDLURLComponents.m", + "coverage": 0.8563922942206655, + "type": "objc", + "functions": [ + { + "name": "FDLSafelyAddKeyValuePairToDictionary", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkGoogleAnalyticsParameters parameters]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkGoogleAnalyticsParameters parametersWithSource:medium:campaign:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters init]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters initWithSource:medium:campaign:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters setSource:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters source]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters setMedium:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters medium]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters setCampaign:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters campaign]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters setTerm:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters term]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters setContent:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters content]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkGoogleAnalyticsParameters dictionaryRepresentation]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkIOSParameters parametersWithBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters initWithBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters bundleID]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters setAppStoreID:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters appStoreID]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters setFallbackURL:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters fallbackURL]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters setCustomScheme:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters customScheme]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters setMinimumAppVersion:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters minimumAppVersion]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters setIPadBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters iPadBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters setIPadFallbackURL:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters iPadFallbackURL]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkIOSParameters dictionaryRepresentation]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkItunesConnectAnalyticsParameters parameters]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkItunesConnectAnalyticsParameters init]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkItunesConnectAnalyticsParameters setAffiliateToken:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkItunesConnectAnalyticsParameters affiliateToken]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkItunesConnectAnalyticsParameters setCampaignToken:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkItunesConnectAnalyticsParameters campaignToken]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkItunesConnectAnalyticsParameters setProviderToken:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkItunesConnectAnalyticsParameters providerToken]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkItunesConnectAnalyticsParameters dictionaryRepresentation]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkAndroidParameters parametersWithPackageName:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkAndroidParameters initWithPackageName:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkAndroidParameters packageName]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkAndroidParameters setMinimumVersion:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkAndroidParameters minimumVersion]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkAndroidParameters setFallbackURL:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkAndroidParameters fallbackURL]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkAndroidParameters dictionaryRepresentation]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkSocialMetaTagParameters parameters]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkSocialMetaTagParameters init]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkSocialMetaTagParameters setTitle:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkSocialMetaTagParameters title]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkSocialMetaTagParameters setDescriptionText:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkSocialMetaTagParameters descriptionText]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkSocialMetaTagParameters setImageURL:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkSocialMetaTagParameters imageURL]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkSocialMetaTagParameters dictionaryRepresentation]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkNavigationInfoParameters parameters]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkNavigationInfoParameters init]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkNavigationInfoParameters isForcedRedirectEnabled]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkNavigationInfoParameters setForcedRedirectEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkNavigationInfoParameters dictionaryRepresentation]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkOtherPlatformParameters parameters]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkOtherPlatformParameters init]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkOtherPlatformParameters fallbackUrl]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkOtherPlatformParameters setFallbackUrl:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkOtherPlatformParameters dictionaryRepresentation]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkComponentsOptions options]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkComponentsOptions init]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkComponents componentsWithLink:domain:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkComponents initWithLink:domain:]", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkComponents componentsWithLink:domainURIPrefix:]", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkComponents initWithLink:domainURIPrefix:]", + "coverage": 0.8 + }, + { + "name": "+[FIRDynamicLinkComponents shortenURL:options:completion:]", + "coverage": 0.9333333333333333 + }, + { + "name": "__58+[FIRDynamicLinkComponents shortenURL:options:completion:]_block_invoke", + "coverage": 0.3088235294117647 + }, + { + "name": "__58+[FIRDynamicLinkComponents shortenURL:options:completion:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRDynamicLinkComponents shortenWithCompletion:]", + "coverage": 0.3333333333333333 + }, + { + "name": "-[FIRDynamicLinkComponents url]", + "coverage": 1 + }, + { + "name": "__31-[FIRDynamicLinkComponents url]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRDynamicLinkComponents sendHTTPRequest:completion:]", + "coverage": 0 + }, + { + "name": "__55+[FIRDynamicLinkComponents sendHTTPRequest:completion:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FIRDynamicLinkComponents shorteningRequestForLongURL:options:]", + "coverage": 0.9512195121951219 + } + ] + }, + { + "name": "FIRInstanceIDStore.m", + "coverage": 0.8579234972677595, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDStore initWithDelegate:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore initWithCheckinStore:tokenStore:delegate:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore hasSubDirectory:]", + "coverage": 0.8181818181818182 + }, + { + "name": "+[FIRInstanceIDStore supportedDirectory]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore pathForSupportSubDirectory:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore createSubDirectory:]", + "coverage": 0.6 + }, + { + "name": "+[FIRInstanceIDStore removeSubDirectory:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore resetCredentialsIfNeeded]", + "coverage": 1 + }, + { + "name": "__46-[FIRInstanceIDStore resetCredentialsIfNeeded]_block_invoke", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRInstanceIDStore tokenInfoWithAuthorizedEntity:scope:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRInstanceIDStore cachedTokenInfos]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore saveTokenInfo:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore removeCachedTokenWithAuthorizedEntity:scope:]", + "coverage": 0.4444444444444444 + }, + { + "name": "-[FIRInstanceIDStore removeAllCachedTokensWithHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore saveCheckinPreferences:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore cachedCheckinPreferences]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore removeCheckinPreferencesWithHandler:]", + "coverage": 1 + }, + { + "name": "__58-[FIRInstanceIDStore removeCheckinPreferencesWithHandler:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIRApp.m", + "coverage": 0.8848, + "type": "objc", + "functions": [ + { + "name": "+[FIRApp configure]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithOptions:]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithName:options:]", + "coverage": 0.9333333333333333 + }, + { + "name": "+[FIRApp defaultApp]", + "coverage": 1 + }, + { + "name": "+[FIRApp appNamed:]", + "coverage": 1 + }, + { + "name": "+[FIRApp allApps]", + "coverage": 1 + }, + { + "name": "+[FIRApp resetApps]", + "coverage": 1 + }, + { + "name": "-[FIRApp deleteApp:]", + "coverage": 0.7916666666666666 + }, + { + "name": "+[FIRApp addAppToAppDictionary:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRApp initInstanceWithName:options:]", + "coverage": 1 + }, + { + "name": "-[FIRApp configureCore]", + "coverage": 0.7962962962962963 + }, + { + "name": "-[FIRApp options]", + "coverage": 1 + }, + { + "name": "-[FIRApp setDataCollectionDefaultEnabled:]", + "coverage": 0.9354838709677419 + }, + { + "name": "-[FIRApp isDataCollectionDefaultEnabled]", + "coverage": 1 + }, + { + "name": "+[FIRApp sendNotificationsToSDKs:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForMissingOptions]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForSubspecConfigurationFailureWithDomain:errorCode:service:reason:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForInvalidAppID]", + "coverage": 0 + }, + { + "name": "+[FIRApp isDefaultAppConfigured]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerLibrary:withVersion:]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerInternalLibrary:withName:withVersion:]", + "coverage": 1 + }, + { + "name": "__55+[FIRApp registerInternalLibrary:withName:withVersion:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRApp firebaseUserAgent]", + "coverage": 1 + }, + { + "name": "-[FIRApp checkExpectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp isAppIDValid]", + "coverage": 1 + }, + { + "name": "+[FIRApp validateAppID:]", + "coverage": 0.9473684210526315 + }, + { + "name": "+[FIRApp actualBundleID]", + "coverage": 0 + }, + { + "name": "+[FIRApp validateAppIDFormat:withVersion:]", + "coverage": 0.8450704225352113 + }, + { + "name": "+[FIRApp validateAppIDFingerprint:withVersion:]", + "coverage": 0.8620689655172413 + }, + { + "name": "-[FIRApp expectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp clearDataCollectionSwitchFromUserDefaults]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromUserDefaultsForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromPlist]", + "coverage": 1 + }, + { + "name": "__43+[FIRApp readDataCollectionSwitchFromPlist]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRApp sendLogsWithServiceName:version:error:]", + "coverage": 0.2631578947368421 + } + ] + }, + { + "name": "FIRInstanceIDTokenInfo.m", + "coverage": 0.889763779527559, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenInfo initWithAuthorizedEntity:scope:token:appVersion:firebaseAppID:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenInfo isFresh]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenInfo initWithCoder:]", + "coverage": 0.78125 + }, + { + "name": "-[FIRInstanceIDTokenInfo encodeWithCoder:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinPreferences.m", + "coverage": 0.8913043478260869, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinPreferences checkinPlistContents]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences hasCheckinInfo]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences hasValidCheckinInfo]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences setHasPreCachedAuthCredentials:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinService.m", + "coverage": 0.896414342629482, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinService init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService checkinWithExistingCheckin:completion:]", + "coverage": 0.9649122807017544 + }, + { + "name": "__69-[FIRInstanceIDCheckinService checkinWithExistingCheckin:completion:]_block_invoke", + "coverage": 0.7105263157894737 + }, + { + "name": "-[FIRInstanceIDCheckinService stopFetching]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService checkinParametersWithExistingCheckin:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinService setCheckinTestBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDAPNSInfo.m", + "coverage": 0.9090909090909091, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAPNSInfo initWithDeviceToken:isSandbox:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo initWithTokenOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo initWithCoder:]", + "coverage": 0.75 + }, + { + "name": "-[FIRInstanceIDAPNSInfo encodeWithCoder:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo isEqualToAPNSInfo:]", + "coverage": 0.7142857142857143 + } + ] + }, + { + "name": "FIRInstanceIDAuthService.m", + "coverage": 0.9166666666666666, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAuthService initWithCheckinService:store:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService initWithStore:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService scheduleCheckin:]", + "coverage": 0.7 + }, + { + "name": "-[FIRInstanceIDAuthService startCheckinTimerWithDuration:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService clearScheduledCheckinTimer]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService onScheduledCheckinTimerFired:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService performScheduledCheckin]", + "coverage": 1 + }, + { + "name": "__51-[FIRInstanceIDAuthService performScheduledCheckin]_block_invoke", + "coverage": 1 + }, + { + "name": "__51-[FIRInstanceIDAuthService performScheduledCheckin]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService calculateNextCheckinRetryIntervalInSeconds]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRInstanceIDAuthService hasValidCheckinInfo]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService checkinPreferences]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService stopCheckinRequest]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService resetCheckinWithHandler:]", + "coverage": 0 + }, + { + "name": "__52-[FIRInstanceIDAuthService resetCheckinWithHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDAuthService notifyCheckinHandlersWithCheckin:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService cachedCheckinMatchesCheckin:]", + "coverage": 0.5714285714285714 + } + ] + }, + { + "name": "FIRInstanceIDKeychain.m", + "coverage": 0.9227053140096618, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDKeychain sharedInstance]", + "coverage": 1 + }, + { + "name": "__39+[FIRInstanceIDKeychain sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain itemWithQuery:]", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceIDKeychain itemWithQuery:]_block_invoke", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRInstanceIDKeychain removeItemWithQuery:handler:]", + "coverage": 1 + }, + { + "name": "__53-[FIRInstanceIDKeychain removeItemWithQuery:handler:]_block_invoke", + "coverage": 0.8260869565217391 + }, + { + "name": "__53-[FIRInstanceIDKeychain removeItemWithQuery:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain addItemWithQuery:handler:]", + "coverage": 1 + }, + { + "name": "__50-[FIRInstanceIDKeychain addItemWithQuery:handler:]_block_invoke", + "coverage": 0.6470588235294118 + }, + { + "name": "__50-[FIRInstanceIDKeychain addItemWithQuery:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain generateKeyPairWithPrivateTag:publicTag:]", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceIDKeychain generateKeyPairWithPrivateTag:publicTag:]_block_invoke", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FIRInstanceIDAuthKeyChain.m", + "coverage": 0.9405405405405406, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAuthKeychain initWithIdentifier:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDAuthKeychain keychainQueryForService:account:generic:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain keychainQueryForService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain itemsMatchingService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain dataForService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain removeItemsMatchingService:account:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain setData:forService:accessibility:account:handler:]", + "coverage": 0.9183673469387755 + }, + { + "name": "__78-[FIRInstanceIDAuthKeychain setData:forService:accessibility:account:handler:]_block_invoke", + "coverage": 0.72 + } + ] + }, + { + "name": "FIROptions.m", + "coverage": 0.9489795918367347, + "type": "objc", + "functions": [ + { + "name": "+[FIROptions defaultOptions]", + "coverage": 0.8461538461538461 + }, + { + "name": "+[FIROptions initialize]", + "coverage": 1 + }, + { + "name": "+[FIROptions defaultOptionsDictionary]", + "coverage": 1 + }, + { + "name": "+[FIROptions plistFilePathWithName:]", + "coverage": 1 + }, + { + "name": "+[FIROptions resetDefaultOptions]", + "coverage": 1 + }, + { + "name": "-[FIROptions initInternalWithOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithContentsOfFile:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithGoogleAppID:GCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions APIKey]", + "coverage": 1 + }, + { + "name": "-[FIROptions checkEditingLocked]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAPIKey:]", + "coverage": 1 + }, + { + "name": "-[FIROptions clientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions trackingID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setTrackingID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions GCMSenderID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions projectID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setProjectID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions androidClientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAndroidClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions googleAppID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGoogleAppID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions libraryVersionID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setLibraryVersionID:]", + "coverage": 0 + }, + { + "name": "-[FIROptions databaseURL]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDatabaseURL:]", + "coverage": 1 + }, + { + "name": "-[FIROptions storageBucket]", + "coverage": 1 + }, + { + "name": "-[FIROptions setStorageBucket:]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDeepLinkURLScheme:]", + "coverage": 1 + }, + { + "name": "-[FIROptions bundleID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionaryWithInfoDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIROptions isMeasurementEnabled]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIROptions isAnalyticsCollectionExpicitlySet]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionEnabled]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionDeactivated]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsEnabled]", + "coverage": 0 + }, + { + "name": "-[FIROptions isSignInEnabled]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAnalyticsConfiguration.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "+[FIRAnalyticsConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__43+[FIRAnalyticsConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration postNotificationName:value:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAnalyticsConfiguration setMinimumSessionInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setSessionTimeoutInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:persistSetting:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinPreferences+Internal.m", + "coverage": 0.9692307692307692, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) preferencesFromKeychainContents:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) initWithDeviceID:secretToken:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) reset]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) updateWithCheckinPlistContents:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) checkinKeychainContent]", + "coverage": 0.75 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinDeviceIDFromKeychainContent:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinSecretFromKeychainContent:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinKeychainContent:forIndex:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentContainer.m", + "coverage": 0.9818181818181818, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:]", + "coverage": 1 + }, + { + "name": "__55+[FIRComponentContainer registerAsComponentRegistrant:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:inSet:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:registrants:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer populateComponentsFromRegisteredClasses:forApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer instantiateInstanceForProtocol:withBlock:]", + "coverage": 0.9285714285714286 + }, + { + "name": "-[FIRComponentContainer instanceForProtocol:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer removeAllCachedInstances]", + "coverage": 1 + } + ] + }, + { + "name": "main.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "main", + "coverage": 1 + } + ] + }, + { + "name": "FIRDLScionLogging.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "FIRDLLogEventToScion", + "coverage": 1 + } + ] + }, + { + "name": "FIRDLRetrievalProcessFactory.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRDLRetrievalProcessFactory initWithNetworkingService:clientID:URLScheme:APIKey:FDLSDKVersion:delegate:]", + "coverage": 1 + }, + { + "name": "-[FIRDLRetrievalProcessFactory automaticRetrievalProcess]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPair.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDKeyPair initWithPrivateKey:publicKey:publicKeyData:privateKeyData:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair isValid]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair publicKey]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair privateKey]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPairUtilities.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDWebSafeBase64", + "coverage": 1 + }, + { + "name": "FIRInstanceIDSHA1", + "coverage": 1 + }, + { + "name": "FIRInstanceIDKeyPairQuery", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAppIdentity", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentType.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentType instanceForProtocol:inContainer:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponent.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponent componentWithProtocol:creationBlock:]", + "coverage": 1 + }, + { + "name": "+[FIRComponent componentWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRComponent initWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAppAssociationRegistration.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRAppAssociationRegistration registeredObjectWithHost:key:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDTokenStore.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDTokenStore defaultStore]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore initWithKeychain:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenStore serviceKeyForAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore tokenInfoWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore cachedTokenInfos]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenStore tokenInfoFromKeychainItem:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore saveTokenInfo:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore removeTokenWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore removeAllTokensWithHandler:]", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "GTMSessionFetcher.framework", + "coverage": 0.26761433868974044, + "files": [ + { + "name": "GTMSessionFetcherLogging.m", + "coverage": 0.006242197253433208, + "type": "objc", + "functions": [ + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) setLoggingDirectory:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) loggingDirectory]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) setLogDirectoryForCurrentRun:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) logDirectoryForCurrentRun]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) setLoggingEnabled:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) isLoggingEnabled]", + "coverage": 1 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) setLoggingToFileEnabled:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) isLoggingToFileEnabled]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) setLoggingProcessName:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) loggingProcessName]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) setLoggingDateStamp:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) loggingDateStamp]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) processNameLogPrefix]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) symlinkNameSuffix]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) htmlFileName]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLogging) deleteLogDirectoriesOlderThanDate:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher(GTMSessionFetcherLogging) formattedStringFromData:contentType:JSON:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher(GTMSessionFetcherLogging) stringFromStreamData:contentType:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher(GTMSessionFetcherLogging) logFetchWithError:]", + "coverage": 0.004866180048661801 + }, + { + "name": "-[GTMSessionFetcher(GTMSessionFetcherLogging) loggedInputStreamForInputStream:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher(GTMSessionFetcherLogging) loggedStreamProviderForStreamProvider:]", + "coverage": 0 + }, + { + "name": "__85-[GTMSessionFetcher(GTMSessionFetcherLogging) loggedStreamProviderForStreamProvider:]_block_invoke", + "coverage": 0 + }, + { + "name": "__85-[GTMSessionFetcher(GTMSessionFetcherLogging) loggedStreamProviderForStreamProvider:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher(GTMSessionFetcherLoggingUtilities) inputStream:readIntoBuffer:length:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLoggingUtilities) snipSubstringOfString:betweenStartString:endString:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher(GTMSessionFetcherLoggingUtilities) headersStringForDictionary:]", + "coverage": 0 + } + ] + }, + { + "name": "GTMSessionUploadFetcher.m", + "coverage": 0.01529535864978903, + "type": "objc", + "functions": [ + { + "name": "+[GTMSessionUploadFetcher load]", + "coverage": 1 + }, + { + "name": "+[GTMSessionUploadFetcher uploadFetcherWithRequest:uploadMIMEType:chunkSize:fetcherService:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionUploadFetcher uploadFetcherWithLocation:uploadMIMEType:chunkSize:fetcherService:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionUploadFetcher uploadFetcherForSessionIdentifierMetadata:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionUploadFetcher uploadFetcherWithRequest:fetcherService:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionUploadFetcher uploadFetcherPointerArrayForBackgroundSessions]", + "coverage": 1 + }, + { + "name": "__73+[GTMSessionUploadFetcher uploadFetcherPointerArrayForBackgroundSessions]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GTMSessionUploadFetcher uploadFetcherForSessionIdentifier:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionUploadFetcher uploadFetchersForBackgroundSessions]", + "coverage": 0.2857142857142857 + }, + { + "name": "-[GTMSessionUploadFetcher setUploadData:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadData]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setUploadFileHandle:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadFileHandle]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setUploadFileURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadFileURL]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setUploadFileLength:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setUploadDataLength:provider:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadDataProvider]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setUploadMIMEType:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadMIMEType]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setChunkSize:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher chunkSize]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setupRequestHeaders]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setLocationURL:uploadMIMEType:chunkSize:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher fullUploadLength]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher generateChunkSubdataWithOffset:length:response:]", + "coverage": 0 + }, + { + "name": "__74-[GTMSessionUploadFetcher generateChunkSubdataWithOffset:length:response:]_block_invoke", + "coverage": 0 + }, + { + "name": "__74-[GTMSessionUploadFetcher generateChunkSubdataWithOffset:length:response:]_block_invoke.237", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher generateChunkSubdataFromFileHandle:offset:length:response:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher generateChunkSubdataFromFileURL:offset:length:response:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadChunkUnavailableErrorWithDescription:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher prematureFailureErrorWithUserInfo:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionUploadFetcher uploadStatusFromResponseHeaders:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setCompletionHandler:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setDelegateCallbackQueue:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher delegateCallbackQueue]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher isRestartedUpload]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher chunkFetcher]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setChunkFetcher:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setFetcherInFlight:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher fetcherInFlight]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setCancellationHandler:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher cancellationHandler]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher beginFetchForRetry]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher beginFetchWithCompletionHandler:]", + "coverage": 0 + }, + { + "name": "__59-[GTMSessionUploadFetcher beginFetchWithCompletionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__59-[GTMSessionUploadFetcher beginFetchWithCompletionHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher beginChunkFetches]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher shouldReleaseCallbacksUponCompletion]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher invokeFinalCallbackWithData:error:shouldInvalidateLocation:]", + "coverage": 0 + }, + { + "name": "__86-[GTMSessionUploadFetcher invokeFinalCallbackWithData:error:shouldInvalidateLocation:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher releaseUploadAndBaseCallbacks:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher stopFetchReleasingCallbacks:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadNextChunkWithOffset:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher sendQueryForUploadOffsetWithFetcherProperties:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher queryFetcher:finishedWithData:error:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher sendCancelUploadWithFetcherProperties:]", + "coverage": 0 + }, + { + "name": "__65-[GTMSessionUploadFetcher sendCancelUploadWithFetcherProperties:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadNextChunkWithOffset:fetcherProperties:]", + "coverage": 0 + }, + { + "name": "__71-[GTMSessionUploadFetcher uploadNextChunkWithOffset:fetcherProperties:]_block_invoke", + "coverage": 0 + }, + { + "name": "__71-[GTMSessionUploadFetcher uploadNextChunkWithOffset:fetcherProperties:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher beginChunkFetcher:offset:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher attachSendProgressBlockToChunkFetcher:]", + "coverage": 0 + }, + { + "name": "__65-[GTMSessionUploadFetcher attachSendProgressBlockToChunkFetcher:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadSessionIdentifierMetadata]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadFetcherWithProperties:isQueryFetch:]", + "coverage": 0 + }, + { + "name": "__68-[GTMSessionUploadFetcher uploadFetcherWithProperties:isQueryFetch:]_block_invoke", + "coverage": 0 + }, + { + "name": "__68-[GTMSessionUploadFetcher uploadFetcherWithProperties:isQueryFetch:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher chunkFetcher:finishedWithData:error:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher destroyChunkFetcher]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher invokeDelegateWithDidSendBytes:totalBytesSent:totalBytesExpectedToSend:]", + "coverage": 0 + }, + { + "name": "__98-[GTMSessionUploadFetcher invokeDelegateWithDidSendBytes:totalBytesSent:totalBytesExpectedToSend:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher retrieveUploadChunkGranularityFromResponseHeaders:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher isPaused]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher pauseFetching]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher resumeFetching]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher stopFetching]", + "coverage": 0 + }, + { + "name": "__39-[GTMSessionUploadFetcher stopFetching]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher triggerCancellationHandlerForFetch:data:error:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher updateChunkFetcher:forChunkAtOffset:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionUploadFetcher removePointer:fromPointerArray:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher useBackgroundSession]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setUseBackgroundSession:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher canFetchWithBackgroundSession]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher responseHeaders]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher statusCodeUnsynchronized]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setStatusCode:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher initialBodyLength]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setInitialBodyLength:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher initialBodySent]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setInitialBodySent:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher uploadLocationURL]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher setUploadLocationURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher activeFetcher]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher isFetching]", + "coverage": 0 + }, + { + "name": "-[GTMSessionUploadFetcher waitForCompletionWithTimeout:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher(GTMSessionUploadFetcherMethods) parentUploadFetcher]", + "coverage": 0 + } + ] + }, + { + "name": "GTMSessionFetcher.m", + "coverage": 0.3696453247351451, + "type": "objc", + "functions": [ + { + "name": "InitialMinRetryInterval", + "coverage": 1 + }, + { + "name": "IsLocalhost", + "coverage": 0 + }, + { + "name": "GTMErrorUserInfoForData", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher load]", + "coverage": 1 + }, + { + "name": "+[GTMSessionFetcher fetcherWithRequest:]", + "coverage": 1 + }, + { + "name": "+[GTMSessionFetcher fetcherWithURL:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher fetcherWithURLString:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher fetcherWithDownloadResumeData:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher fetcherWithSessionIdentifier:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher sessionIdentifierToFetcherMap]", + "coverage": 1 + }, + { + "name": "__50+[GTMSessionFetcher sessionIdentifierToFetcherMap]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GTMSessionFetcher appAllowsInsecureRequests]", + "coverage": 1 + }, + { + "name": "__46+[GTMSessionFetcher appAllowsInsecureRequests]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher init]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher initWithRequest:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher initWithRequest:configuration:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher description]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher dealloc]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher beginFetchWithCompletionHandler:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher beginFetchForRetry]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher completionHandlerWithTarget:didFinishSelector:]", + "coverage": 0 + }, + { + "name": "__67-[GTMSessionFetcher completionHandlerWithTarget:didFinishSelector:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher beginFetchWithDelegate:didFinishSelector:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher beginFetchMayDelay:mayAuthorize:]", + "coverage": 0.5222222222222223 + }, + { + "name": "__53-[GTMSessionFetcher beginFetchMayDelay:mayAuthorize:]_block_invoke", + "coverage": 0 + }, + { + "name": "__53-[GTMSessionFetcher beginFetchMayDelay:mayAuthorize:]_block_invoke.276", + "coverage": 0 + }, + { + "name": "__53-[GTMSessionFetcher beginFetchMayDelay:mayAuthorize:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__53-[GTMSessionFetcher beginFetchMayDelay:mayAuthorize:]_block_invoke.377", + "coverage": 0 + }, + { + "name": "GTMDataFromInputStream", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher simulateFetchForTestBlock]", + "coverage": 1 + }, + { + "name": "__46-[GTMSessionFetcher simulateFetchForTestBlock]_block_invoke", + "coverage": 0.44642857142857145 + }, + { + "name": "__46-[GTMSessionFetcher simulateFetchForTestBlock]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__46-[GTMSessionFetcher simulateFetchForTestBlock]_block_invoke_3", + "coverage": 0 + }, + { + "name": "__46-[GTMSessionFetcher simulateFetchForTestBlock]_block_invoke_4", + "coverage": 0 + }, + { + "name": "__46-[GTMSessionFetcher simulateFetchForTestBlock]_block_invoke.434", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher simulateByteTransferReportWithDataLength:block:]", + "coverage": 0 + }, + { + "name": "__68-[GTMSessionFetcher simulateByteTransferReportWithDataLength:block:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]", + "coverage": 0.40718562874251496 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke.457", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke_2.471", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke.478", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke_2.479", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke.486", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke.493", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke.499", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke.509", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke_2.510", + "coverage": 0 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke.517", + "coverage": 1 + }, + { + "name": "__95-[GTMSessionFetcher simulateDataCallbacksForTestBlockWithBodyData:response:responseData:error:]_block_invoke_2.522", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher simulateByteTransferWithData:block:]", + "coverage": 0 + }, + { + "name": "__56-[GTMSessionFetcher simulateByteTransferWithData:block:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setSessionTask:]", + "coverage": 0.8666666666666667 + }, + { + "name": "-[GTMSessionFetcher sessionTask]", + "coverage": 1 + }, + { + "name": "+[GTMSessionFetcher fetcherUserDefaults]", + "coverage": 1 + }, + { + "name": "__40+[GTMSessionFetcher fetcherUserDefaults]_block_invoke", + "coverage": 0.875 + }, + { + "name": "-[GTMSessionFetcher addPersistedBackgroundSessionToDefaults]", + "coverage": 0.2631578947368421 + }, + { + "name": "-[GTMSessionFetcher removePersistedBackgroundSessionFromDefaults]", + "coverage": 0.11538461538461539 + }, + { + "name": "+[GTMSessionFetcher activePersistedBackgroundSessions]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher fetchersForBackgroundSessions]", + "coverage": 0.4090909090909091 + }, + { + "name": "+[GTMSessionFetcher application:handleEventsForBackgroundURLSession:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher sessionIdentifier]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setSessionIdentifier:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setSessionIdentifierInternal:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher sessionUserInfo]", + "coverage": 0 + }, + { + "name": "__36-[GTMSessionFetcher sessionUserInfo]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setSessionUserInfo:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher sessionIdentifierDefaultMetadata]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher restoreDefaultStateForSessionIdentifierMetadata]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher sessionIdentifierMetadata]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher sessionIdentifierMetadataUnsynchronized]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher createSessionIdentifierWithMetadata:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher failToBeginFetchWithError:]", + "coverage": 0.8095238095238095 + }, + { + "name": "+[GTMSessionFetcher staticCookieStorage]", + "coverage": 1 + }, + { + "name": "__40+[GTMSessionFetcher staticCookieStorage]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher endBackgroundTask]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher authorizeRequest]", + "coverage": 0.65 + }, + { + "name": "-[GTMSessionFetcher authorizer:request:finishedWithError:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher canFetchWithBackgroundSession]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher isFetching]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher isFetchingUnsynchronized]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher response]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher responseUnsynchronized]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher statusCode]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher statusCodeUnsynchronized]", + "coverage": 0.7333333333333333 + }, + { + "name": "-[GTMSessionFetcher responseHeaders]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher responseHeadersUnsynchronized]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher releaseCallbacks]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher forgetSessionIdentifierForFetcher]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher forgetSessionIdentifierForFetcherWithoutSyncCheck]", + "coverage": 0.4444444444444444 + }, + { + "name": "-[GTMSessionFetcher stopFetching]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher stopFetchReleasingCallbacks:]", + "coverage": 0.8269230769230769 + }, + { + "name": "__49-[GTMSessionFetcher stopFetchReleasingCallbacks:]_block_invoke", + "coverage": 0 + }, + { + "name": "__49-[GTMSessionFetcher stopFetchReleasingCallbacks:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setStopNotificationNeeded:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher sendStopNotificationIfNeeded]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher retryFetch]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher waitForCompletionWithTimeout:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher setGlobalTestBlock:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher setSubstituteUIApplication:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher substituteUIApplication]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher fetcherUIApplication]", + "coverage": 1 + }, + { + "name": "__41+[GTMSessionFetcher fetcherUIApplication]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:]", + "coverage": 0 + }, + { + "name": "__93-[GTMSessionFetcher URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__93-[GTMSessionFetcher URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:dataTask:didReceiveResponse:completionHandler:]", + "coverage": 0 + }, + { + "name": "__78-[GTMSessionFetcher URLSession:dataTask:didReceiveResponse:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__78-[GTMSessionFetcher URLSession:dataTask:didReceiveResponse:completionHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__78-[GTMSessionFetcher URLSession:dataTask:didReceiveResponse:completionHandler:]_block_invoke.804", + "coverage": 0 + }, + { + "name": "__78-[GTMSessionFetcher URLSession:dataTask:didReceiveResponse:completionHandler:]_block_invoke_2.805", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:dataTask:didBecomeDownloadTask:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:task:didReceiveChallenge:completionHandler:]", + "coverage": 0 + }, + { + "name": "__75-[GTMSessionFetcher URLSession:task:didReceiveChallenge:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher respondToChallenge:completionHandler:]", + "coverage": 0 + }, + { + "name": "__58-[GTMSessionFetcher respondToChallenge:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher redirectURLWithOriginalRequestURL:redirectRequestURL:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher evaluateServerTrust:forRequest:completionHandler:]", + "coverage": 0 + }, + { + "name": "__70+[GTMSessionFetcher evaluateServerTrust:forRequest:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher invokeOnCallbackQueueUnlessStopped:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher invokeOnCallbackQueueAfterUserStopped:block:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher invokeOnCallbackUnsynchronizedQueueAfterUserStopped:block:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher invokeOnCallbackQueue:afterUserStopped:block:]", + "coverage": 1 + }, + { + "name": "__66-[GTMSessionFetcher invokeOnCallbackQueue:afterUserStopped:block:]_block_invoke", + "coverage": 0.8076923076923077 + }, + { + "name": "-[GTMSessionFetcher invokeFetchCallbacksOnCallbackQueueWithData:error:]", + "coverage": 1 + }, + { + "name": "__71-[GTMSessionFetcher invokeFetchCallbacksOnCallbackQueueWithData:error:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher postNotificationOnMainThreadWithName:userInfo:requireAsync:]", + "coverage": 1 + }, + { + "name": "__80-[GTMSessionFetcher postNotificationOnMainThreadWithName:userInfo:requireAsync:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher URLSession:task:needNewBodyStream:]", + "coverage": 0 + }, + { + "name": "__55-[GTMSessionFetcher URLSession:task:needNewBodyStream:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]", + "coverage": 0 + }, + { + "name": "__93-[GTMSessionFetcher URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:dataTask:didReceiveData:]", + "coverage": 0 + }, + { + "name": "__56-[GTMSessionFetcher URLSession:dataTask:didReceiveData:]_block_invoke", + "coverage": 0 + }, + { + "name": "__56-[GTMSessionFetcher URLSession:dataTask:didReceiveData:]_block_invoke.895", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:dataTask:willCacheResponse:completionHandler:]", + "coverage": 0 + }, + { + "name": "__77-[GTMSessionFetcher URLSession:dataTask:willCacheResponse:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:]", + "coverage": 0 + }, + { + "name": "__102-[GTMSessionFetcher URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:downloadTask:didFinishDownloadingToURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:task:didCompleteWithError:]", + "coverage": 0.23880597014925373 + }, + { + "name": "__58-[GTMSessionFetcher URLSession:task:didCompleteWithError:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSessionDidFinishEventsForBackgroundURLSession:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher URLSession:didBecomeInvalidWithError:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher finishWithError:shouldRetry:]", + "coverage": 0.6165413533834586 + }, + { + "name": "__49-[GTMSessionFetcher finishWithError:shouldRetry:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher shouldReleaseCallbacksUponCompletion]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher logNowWithError:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher isRetryError:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher shouldRetryNowForStatus:error:forceAssumeRetry:response:]", + "coverage": 0.4025974025974026 + }, + { + "name": "__77-[GTMSessionFetcher shouldRetryNowForStatus:error:forceAssumeRetry:response:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher hasRetryAfterInterval]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher retryAfterInterval]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher beginRetryTimer]", + "coverage": 0 + }, + { + "name": "__36-[GTMSessionFetcher beginRetryTimer]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher retryTimerFired:]", + "coverage": 0 + }, + { + "name": "__37-[GTMSessionFetcher retryTimerFired:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher destroyRetryTimer]", + "coverage": 0.5789473684210527 + }, + { + "name": "-[GTMSessionFetcher retryCount]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher nextRetryInterval]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher nextRetryIntervalUnsynchronized]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher retryTimer]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher isRetryEnabled]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher isRetryEnabledUnsynchronized]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setRetryEnabled:]", + "coverage": 0.45 + }, + { + "name": "-[GTMSessionFetcher maxRetryInterval]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setMaxRetryInterval:]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[GTMSessionFetcher minRetryInterval]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setMinRetryInterval:]", + "coverage": 0.9333333333333333 + }, + { + "name": "-[GTMSessionFetcher systemCompletionHandler]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setSystemCompletionHandler:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher setSystemCompletionHandler:forSessionIdentifier:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcher systemCompletionHandlerForSessionIdentifier:]", + "coverage": 0.5 + }, + { + "name": "-[GTMSessionFetcher request]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setRequest:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher mutableRequestForTesting]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher updateMutableRequest:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setRequestValue:forHTTPHeaderField:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[GTMSessionFetcher updateRequestValue:forHTTPHeaderField:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setResponse:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher bodyLength]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher useUploadTask]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setUseUploadTask:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher bodyFileURL]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setBodyFileURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher bodyStreamProvider]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setBodyStreamProvider:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher authorizer]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setAuthorizer:]", + "coverage": 0.9230769230769231 + }, + { + "name": "-[GTMSessionFetcher downloadedData]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setDownloadedData:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher downloadedLength]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setDownloadedLength:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher callbackQueue]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setCallbackQueue:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher session]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher servicePriority]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setServicePriority:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setSession:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher canShareSession]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setCanShareSession:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher useBackgroundSession]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setUseBackgroundSession:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher isUsingBackgroundSession]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setUsingBackgroundSession:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher sessionNeedingInvalidation]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setSessionNeedingInvalidation:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher sessionDelegateQueue]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setSessionDelegateQueue:]", + "coverage": 0.9230769230769231 + }, + { + "name": "-[GTMSessionFetcher userStoppedFetching]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher userData]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setUserData:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher destinationFileURL]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher setDestinationFileURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setProperties:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcher properties]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setProperty:forKey:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher propertyForKey:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher addPropertiesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setCommentWithFormat:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher loggedStreamData]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher appendLoggedStreamData:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher clearLoggedStreamData]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher setDeferResponseBodyLogging:]", + "coverage": 0 + }, + { + "name": "__49-[GTMSessionFetcher setDeferResponseBodyLogging:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher deferResponseBodyLogging]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcher(BackwardsCompatibilityOnly) setCookieStorageMethod:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage init]", + "coverage": 1 + }, + { + "name": "-[GTMSessionCookieStorage cookies]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage setCookie:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage internalSetCookie:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage setCookies:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage setCookies:forURL:mainDocumentURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage deleteCookie:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage cookiesForURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage storeCookies:forTask:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage getCookiesForTask:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage cookieMatchingCookie:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage removeExpiredCookies]", + "coverage": 0 + }, + { + "name": "+[GTMSessionCookieStorage hasCookieExpired:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage removeAllCookies]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage cookieAcceptPolicy]", + "coverage": 0 + }, + { + "name": "-[GTMSessionCookieStorage setCookieAcceptPolicy:]", + "coverage": 0 + }, + { + "name": "GTMSessionFetcherAssertValidSelector", + "coverage": 0 + }, + { + "name": "GTMFetcherCleanedUserAgentString", + "coverage": 0.9230769230769231 + }, + { + "name": "GTMFetcherSystemVersionString", + "coverage": 1 + }, + { + "name": "__GTMFetcherSystemVersionString_block_invoke", + "coverage": 1 + }, + { + "name": "GTMFetcherStandardUserAgentString", + "coverage": 1 + }, + { + "name": "GTMFetcherApplicationIdentifier", + "coverage": 1 + }, + { + "name": "-[GTMSessionSyncMonitorInternal initWithSynchronizationObject:allowRecursive:functionName:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionSyncMonitorInternal dealloc]", + "coverage": 1 + }, + { + "name": "+[GTMSessionSyncMonitorInternal functionsHoldingSynchronizationOnObject:]", + "coverage": 1 + } + ] + }, + { + "name": "GTMSessionFetcherService.m", + "coverage": 0.5004757373929591, + "type": "objc", + "functions": [ + { + "name": "-[GTMSessionFetcherService init]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService dealloc]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService fetcherWithRequest:fetcherClass:]", + "coverage": 0.9473684210526315 + }, + { + "name": "-[GTMSessionFetcherService fetcherWithRequest:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService fetcherWithURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService fetcherWithURLString:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService session]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService sessionForFetcherCreation]", + "coverage": 0.9142857142857143 + }, + { + "name": "-[GTMSessionFetcherService sessionDelegate]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService addRunningFetcher:forHost:]", + "coverage": 0.8 + }, + { + "name": "-[GTMSessionFetcherService addDelayedFetcher:forHost:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService isDelayingFetcher:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService fetcherShouldBeginFetching:]", + "coverage": 0.75 + }, + { + "name": "-[GTMSessionFetcherService startFetcher:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService delegateDispatcherForFetcher:]", + "coverage": 0.8 + }, + { + "name": "-[GTMSessionFetcherService fetcherDidCreateSession:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService fetcherDidBeginFetching:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService stopFetcher:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService fetcherDidStop:]", + "coverage": 0.6621621621621622 + }, + { + "name": "-[GTMSessionFetcherService numberOfFetchers]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService numberOfRunningFetchers]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService numberOfDelayedFetchers]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService issuedFetchers]", + "coverage": 0 + }, + { + "name": "__42-[GTMSessionFetcherService issuedFetchers]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService issuedFetchersWithRequestURL:]", + "coverage": 0 + }, + { + "name": "__57-[GTMSessionFetcherService issuedFetchersWithRequestURL:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService stopAllFetchers]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService stoppedAllFetchersDate]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService reuseSession]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService setReuseSession:]", + "coverage": 0.8235294117647058 + }, + { + "name": "-[GTMSessionFetcherService resetSession]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService resetSessionInternal]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService resetSessionForDispatcherDiscardTimer:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService unusedSessionTimeout]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService setUnusedSessionTimeout:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService abandonDispatcher]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService runningFetchersByHost]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService setRunningFetchersByHost:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService delayedFetchersByHost]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService setDelayedFetchersByHost:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService authorizer]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService setAuthorizer:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService detachAuthorizer]", + "coverage": 0.8888888888888888 + }, + { + "name": "-[GTMSessionFetcherService callbackQueue]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService setCallbackQueue:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService sessionDelegateQueue]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService setSessionDelegateQueue:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService delegateQueue]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcherService numberOfNonBackgroundSessionFetchers:]", + "coverage": 0.5555555555555556 + }, + { + "name": "+[GTMSessionFetcherService(TestingSupport) mockFetcherServiceWithFakedData:fakedError:]", + "coverage": 0 + }, + { + "name": "+[GTMSessionFetcherService(TestingSupport) mockFetcherServiceWithFakedData:fakedResponse:fakedError:]", + "coverage": 0 + }, + { + "name": "__101+[GTMSessionFetcherService(TestingSupport) mockFetcherServiceWithFakedData:fakedResponse:fakedError:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService(TestingSupport) waitForCompletionOfAllFetchersWithTimeout:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherService(BackwardsCompatibilityOnly) cookieStorageMethod]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherService(BackwardsCompatibilityOnly) setCookieStorageMethod:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher init]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher initWithParentService:sessionDiscardInterval:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher description]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher discardTimer]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher startDiscardTimer]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher destroyDiscardTimer]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher discardTimerFired:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher abandon]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher startSessionUsage]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher destroySessionAndTimer]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher setFetcher:forTask:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher removeFetcher:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher fetcherForTask:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher removeTaskFromMap:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher setSession:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher session]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher discardInterval]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher setDiscardInterval:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:didBecomeInvalidWithError:]", + "coverage": 1 + }, + { + "name": "__83-[GTMSessionFetcherSessionDelegateDispatcher URLSession:didBecomeInvalidWithError:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:task:didReceiveChallenge:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:task:needNewBodyStream:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:task:didCompleteWithError:]", + "coverage": 1 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:dataTask:didReceiveResponse:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:dataTask:didBecomeDownloadTask:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:dataTask:didReceiveData:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:dataTask:willCacheResponse:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:downloadTask:didFinishDownloadingToURL:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:]", + "coverage": 0 + }, + { + "name": "-[GTMSessionFetcherSessionDelegateDispatcher URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:]", + "coverage": 0 + } + ] + } + ] + }, + { + "name": "GoogleUtilities.framework", + "coverage": 0.3882390135946886, + "files": [ + { + "name": "GULRuntimeClassDiff.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "IsEqual", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassDiff hash]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassDiff isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassDiff description]", + "coverage": 0 + } + ] + }, + { + "name": "GULRuntimeStateHelper.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[GULRuntimeStateHelper snapshotCache]", + "coverage": 0 + }, + { + "name": "__38+[GULRuntimeStateHelper snapshotCache]_block_invoke", + "coverage": 0 + }, + { + "name": "+[GULRuntimeStateHelper captureRuntimeState]", + "coverage": 0 + }, + { + "name": "+[GULRuntimeStateHelper captureRuntimeStateOfClasses:]", + "coverage": 0 + }, + { + "name": "+[GULRuntimeStateHelper diffBetween:secondSnapshot:]", + "coverage": 0 + } + ] + }, + { + "name": "GULObjectSwizzler.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[GULObjectSwizzler setAssociatedObject:key:value:association:]", + "coverage": 0 + }, + { + "name": "+[GULObjectSwizzler getAssociatedObject:key:]", + "coverage": 0 + }, + { + "name": "-[GULObjectSwizzler initWithObject:]", + "coverage": 0 + }, + { + "name": "-[GULObjectSwizzler copySelector:fromClass:isClassSelector:]", + "coverage": 0 + }, + { + "name": "-[GULObjectSwizzler setAssociatedObjectWithKey:value:association:]", + "coverage": 0 + }, + { + "name": "-[GULObjectSwizzler getAssociatedObjectForKey:]", + "coverage": 0 + }, + { + "name": "-[GULObjectSwizzler swizzle]", + "coverage": 0 + }, + { + "name": "-[GULObjectSwizzler dealloc]", + "coverage": 0 + }, + { + "name": "-[GULObjectSwizzler isSwizzlingProxyObject]", + "coverage": 0 + } + ] + }, + { + "name": "GULSwizzledObject.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[GULSwizzledObject copyDonorSelectorsUsingObjectSwizzler:]", + "coverage": 0 + }, + { + "name": "-[GULSwizzledObject init]", + "coverage": 0 + }, + { + "name": "-[GULSwizzledObject gul_objectSwizzler]", + "coverage": 0 + }, + { + "name": "-[GULSwizzledObject gul_class]", + "coverage": 0 + }, + { + "name": "-[GULSwizzledObject respondsToSelector:]", + "coverage": 0 + } + ] + }, + { + "name": "GULRuntimeSnapshot.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[GULRuntimeSnapshot init]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot initWithClasses:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot hash]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot description]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot capture]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot diff:]", + "coverage": 0 + }, + { + "name": "__27-[GULRuntimeSnapshot diff:]_block_invoke", + "coverage": 0 + }, + { + "name": "__27-[GULRuntimeSnapshot diff:]_block_invoke.34", + "coverage": 0 + }, + { + "name": "__27-[GULRuntimeSnapshot diff:]_block_invoke.43", + "coverage": 0 + } + ] + }, + { + "name": "GULRuntimeDiff.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "IsEqual", + "coverage": 0 + }, + { + "name": "-[GULRuntimeDiff hash]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeDiff isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeDiff description]", + "coverage": 0 + } + ] + }, + { + "name": "GULRuntimeClassSnapshot.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[GULRuntimeClassSnapshot init]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot initWithClass:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot capture]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot diff:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot hash]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot description]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot captureProperties]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot captureSelectorsAndImps]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot computeDiffOfProperties:withClassDiff:]", + "coverage": 0 + }, + { + "name": "__65-[GULRuntimeClassSnapshot computeDiffOfProperties:withClassDiff:]_block_invoke", + "coverage": 0 + }, + { + "name": "__65-[GULRuntimeClassSnapshot computeDiffOfProperties:withClassDiff:]_block_invoke.46", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot computeDiffOfSelectorsAndImps:withClassDiff:]", + "coverage": 0 + }, + { + "name": "__71-[GULRuntimeClassSnapshot computeDiffOfSelectorsAndImps:withClassDiff:]_block_invoke", + "coverage": 0 + }, + { + "name": "__71-[GULRuntimeClassSnapshot computeDiffOfSelectorsAndImps:withClassDiff:]_block_invoke.57", + "coverage": 0 + }, + { + "name": "__71-[GULRuntimeClassSnapshot computeDiffOfSelectorsAndImps:withClassDiff:]_block_invoke.63", + "coverage": 0 + } + ] + }, + { + "name": "GULProxy.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[GULProxy initWithDelegate:]", + "coverage": 0 + }, + { + "name": "+[GULProxy proxyWithDelegate:]", + "coverage": 0 + }, + { + "name": "-[GULProxy forwardingTargetForSelector:]", + "coverage": 0 + }, + { + "name": "-[GULProxy forwardInvocation:]", + "coverage": 0 + }, + { + "name": "-[GULProxy methodSignatureForSelector:]", + "coverage": 0 + }, + { + "name": "-[GULProxy respondsToSelector:]", + "coverage": 0 + }, + { + "name": "-[GULProxy isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULProxy hash]", + "coverage": 0 + }, + { + "name": "-[GULProxy superclass]", + "coverage": 0 + }, + { + "name": "-[GULProxy class]", + "coverage": 0 + }, + { + "name": "-[GULProxy isKindOfClass:]", + "coverage": 0 + }, + { + "name": "-[GULProxy isMemberOfClass:]", + "coverage": 0 + }, + { + "name": "-[GULProxy conformsToProtocol:]", + "coverage": 0 + }, + { + "name": "-[GULProxy isProxy]", + "coverage": 0 + }, + { + "name": "-[GULProxy description]", + "coverage": 0 + }, + { + "name": "-[GULProxy debugDescription]", + "coverage": 0 + } + ] + }, + { + "name": "GULNetwork.m", + "coverage": 0.31044776119402984, + "type": "objc", + "functions": [ + { + "name": "-[GULNetwork init]", + "coverage": 1 + }, + { + "name": "-[GULNetwork initWithReachabilityHost:]", + "coverage": 0.8666666666666667 + }, + { + "name": "-[GULNetwork dealloc]", + "coverage": 0 + }, + { + "name": "+[GULNetwork handleEventsForBackgroundURLSessionID:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GULNetwork postURL:payload:queue:usingBackgroundSession:completionHandler:]", + "coverage": 0.7215189873417721 + }, + { + "name": "__77-[GULNetwork postURL:payload:queue:usingBackgroundSession:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__77-[GULNetwork postURL:payload:queue:usingBackgroundSession:completionHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[GULNetwork getURL:headers:queue:usingBackgroundSession:completionHandler:]", + "coverage": 0 + }, + { + "name": "__76-[GULNetwork getURL:headers:queue:usingBackgroundSession:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__76-[GULNetwork getURL:headers:queue:usingBackgroundSession:completionHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[GULNetwork hasUploadInProgress]", + "coverage": 1 + }, + { + "name": "-[GULNetwork reachability:statusChanged:]", + "coverage": 1 + }, + { + "name": "-[GULNetwork setLoggerDelegate:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[GULNetwork handleErrorWithCode:queue:withHandler:]", + "coverage": 0 + }, + { + "name": "__52-[GULNetwork handleErrorWithCode:queue:withHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULNetwork GULNetwork_logWithLevel:messageCode:message:contexts:]", + "coverage": 0.3684210526315789 + }, + { + "name": "-[GULNetwork GULNetwork_logWithLevel:messageCode:message:context:]", + "coverage": 0.45454545454545453 + }, + { + "name": "-[GULNetwork GULNetwork_logWithLevel:messageCode:message:]", + "coverage": 0 + }, + { + "name": "GULLogLevelDescriptionFromLogLevel", + "coverage": 0 + }, + { + "name": "__GULLogLevelDescriptionFromLogLevel_block_invoke", + "coverage": 0 + }, + { + "name": "GULStringWithLogMessage", + "coverage": 0 + } + ] + }, + { + "name": "GULNSData+zlib.m", + "coverage": 0.3163841807909605, + "type": "objc", + "functions": [ + { + "name": "+[NSData(GULGzip) gul_dataByInflatingGzippedData:error:]", + "coverage": 0 + }, + { + "name": "+[NSData(GULGzip) gul_dataByGzippingData:error:]", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "GULNetworkURLSession.m", + "coverage": 0.3446054750402576, + "type": "objc", + "functions": [ + { + "name": "-[GULNetworkURLSession initWithNetworkLoggerDelegate:]", + "coverage": 1 + }, + { + "name": "+[GULNetworkURLSession handleEventsForBackgroundURLSessionID:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession sessionIDFromAsyncPOSTRequest:completionHandler:]", + "coverage": 0.6216216216216216 + }, + { + "name": "-[GULNetworkURLSession sessionIDFromAsyncGETRequest:completionHandler:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession URLSession:downloadTask:didFinishDownloadingToURL:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession URLSessionDidFinishEventsForBackgroundURLSession:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession URLSession:task:didCompleteWithError:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession URLSession:task:didReceiveChallenge:completionHandler:]", + "coverage": 0.788235294117647 + }, + { + "name": "__78-[GULNetworkURLSession URLSession:task:didReceiveChallenge:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__78-[GULNetworkURLSession URLSession:task:didReceiveChallenge:completionHandler:]_block_invoke.147", + "coverage": 0.8148148148148148 + }, + { + "name": "-[GULNetworkURLSession addSystemCompletionHandler:forSession:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession callSystemCompletionHandler:]", + "coverage": 0 + }, + { + "name": "__52-[GULNetworkURLSession callSystemCompletionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession setSessionID:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession backgroundSessionConfigWithSessionID:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession maybeRemoveTempFilesAtURL:expiringTime:]", + "coverage": 0.40476190476190477 + }, + { + "name": "-[GULNetworkURLSession removeTempItemAtURL:]", + "coverage": 0 + }, + { + "name": "+[GULNetworkURLSession fetcherWithSessionIdentifier:]", + "coverage": 0 + }, + { + "name": "+[GULNetworkURLSession sessionIDToFetcherMap]", + "coverage": 1 + }, + { + "name": "__45+[GULNetworkURLSession sessionIDToFetcherMap]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULNetworkURLSession sessionIDToFetcherMapReadWriteLock]", + "coverage": 1 + }, + { + "name": "__58+[GULNetworkURLSession sessionIDToFetcherMapReadWriteLock]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULNetworkURLSession sessionIDToSystemCompletionHandlerDictionary]", + "coverage": 0 + }, + { + "name": "__68+[GULNetworkURLSession sessionIDToSystemCompletionHandlerDictionary]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession temporaryFilePathWithSessionID:]", + "coverage": 1 + }, + { + "name": "-[GULNetworkURLSession ensureTemporaryDirectoryExists]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession excludeFromBackupForURL:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:]", + "coverage": 0 + }, + { + "name": "+[GULNetworkURLSession setSessionInFetcherMap:forSessionID:]", + "coverage": 0.5 + }, + { + "name": "+[GULNetworkURLSession sessionFromFetcherMapForSessionID:]", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession callCompletionHandler:withResponse:data:error:]", + "coverage": 0 + }, + { + "name": "__70-[GULNetworkURLSession callCompletionHandler:withResponse:data:error:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULNetworkURLSession populateSessionConfig:withRequest:]", + "coverage": 1 + } + ] + }, + { + "name": "GULUserDefaults.m", + "coverage": 0.423841059602649, + "type": "objc", + "functions": [ + { + "name": "+[GULUserDefaults standardUserDefaults]", + "coverage": 1 + }, + { + "name": "__39+[GULUserDefaults standardUserDefaults]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GULUserDefaults init]", + "coverage": 1 + }, + { + "name": "-[GULUserDefaults initWithSuiteName:]", + "coverage": 1 + }, + { + "name": "-[GULUserDefaults dealloc]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults objectForKey:]", + "coverage": 0.5 + }, + { + "name": "-[GULUserDefaults setObject:forKey:]", + "coverage": 0.4482758620689655 + }, + { + "name": "-[GULUserDefaults removeObjectForKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults integerForKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults floatForKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults doubleForKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults boolForKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults stringForKey:]", + "coverage": 1 + }, + { + "name": "-[GULUserDefaults arrayForKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults dictionaryForKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults setInteger:forKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults setFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults setDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults setBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults synchronize]", + "coverage": 0.42857142857142855 + }, + { + "name": "-[GULUserDefaults clearAllData]", + "coverage": 0 + }, + { + "name": "-[GULUserDefaults scheduleSynchronize]", + "coverage": 1 + } + ] + }, + { + "name": "GULAppDelegateSwizzler.m", + "coverage": 0.4431818181818182, + "type": "objc", + "functions": [ + { + "name": "+[GULAppDelegateObserver sharedInstance]", + "coverage": 1 + }, + { + "name": "__40+[GULAppDelegateObserver sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GULAppDelegateObserver observeUIApplication]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[GULAppDelegateObserver observeValueForKeyPath:ofObject:change:context:]", + "coverage": 0 + }, + { + "name": "+[GULAppDelegateSwizzler isAppDelegateProxyEnabled]", + "coverage": 0.8260869565217391 + }, + { + "name": "+[GULAppDelegateSwizzler registerAppDelegateInterceptor:]", + "coverage": 0.47058823529411764 + }, + { + "name": "+[GULAppDelegateSwizzler unregisterAppDelegateInterceptorWithID:]", + "coverage": 0 + }, + { + "name": "+[GULAppDelegateSwizzler proxyOriginalDelegate]", + "coverage": 1 + }, + { + "name": "__47+[GULAppDelegateSwizzler proxyOriginalDelegate]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULAppDelegateSwizzler sharedApplication]", + "coverage": 0.8333333333333334 + }, + { + "name": "+[GULAppDelegateSwizzler createSubclassWithObject:]", + "coverage": 0.7172413793103448 + }, + { + "name": "+[GULAppDelegateSwizzler interceptors]", + "coverage": 1 + }, + { + "name": "__38+[GULAppDelegateSwizzler interceptors]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULAppDelegateSwizzler addInstanceMethodWithSelector:fromClass:toClass:]", + "coverage": 1 + }, + { + "name": "+[GULAppDelegateSwizzler addInstanceMethodWithDestinationSelector:withImplementationFromSourceSelector:fromClass:toClass:]", + "coverage": 0.5 + }, + { + "name": "+[GULAppDelegateSwizzler implementationOfMethodSelector:fromClass:]", + "coverage": 1 + }, + { + "name": "+[GULAppDelegateSwizzler notifyInterceptorsWithMethodSelector:callback:]", + "coverage": 0 + }, + { + "name": "__72+[GULAppDelegateSwizzler notifyInterceptorsWithMethodSelector:callback:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULAppDelegateSwizzler fakeDescription]", + "coverage": 1 + }, + { + "name": "-[GULAppDelegateSwizzler application:openURL:options:]", + "coverage": 0 + }, + { + "name": "__54-[GULAppDelegateSwizzler application:openURL:options:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULAppDelegateSwizzler application:openURL:sourceApplication:annotation:]", + "coverage": 0 + }, + { + "name": "__75-[GULAppDelegateSwizzler application:openURL:sourceApplication:annotation:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULAppDelegateSwizzler application:handleEventsForBackgroundURLSession:completionHandler:]", + "coverage": 0 + }, + { + "name": "__92-[GULAppDelegateSwizzler application:handleEventsForBackgroundURLSession:completionHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULAppDelegateSwizzler application:continueUserActivity:restorationHandler:]", + "coverage": 0 + }, + { + "name": "__78-[GULAppDelegateSwizzler application:continueUserActivity:restorationHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[GULAppDelegateSwizzler proxyAppDelegate:]", + "coverage": 0.5625 + }, + { + "name": "+[GULAppDelegateSwizzler correctAppDelegateProxyKey]", + "coverage": 1 + }, + { + "name": "+[GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]", + "coverage": 0 + } + ] + }, + { + "name": "GULMutableDictionary.m", + "coverage": 0.5609756097560976, + "type": "objc", + "functions": [ + { + "name": "-[GULMutableDictionary init]", + "coverage": 1 + }, + { + "name": "-[GULMutableDictionary description]", + "coverage": 0 + }, + { + "name": "__35-[GULMutableDictionary description]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULMutableDictionary objectForKey:]", + "coverage": 1 + }, + { + "name": "__37-[GULMutableDictionary objectForKey:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GULMutableDictionary setObject:forKey:]", + "coverage": 1 + }, + { + "name": "__41-[GULMutableDictionary setObject:forKey:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GULMutableDictionary removeObjectForKey:]", + "coverage": 0 + }, + { + "name": "__43-[GULMutableDictionary removeObjectForKey:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULMutableDictionary removeAllObjects]", + "coverage": 0 + }, + { + "name": "__40-[GULMutableDictionary removeAllObjects]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GULMutableDictionary count]", + "coverage": 1 + }, + { + "name": "__29-[GULMutableDictionary count]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GULMutableDictionary objectForKeyedSubscript:]", + "coverage": 1 + }, + { + "name": "-[GULMutableDictionary setObject:forKeyedSubscript:]", + "coverage": 1 + }, + { + "name": "-[GULMutableDictionary dictionary]", + "coverage": 0 + }, + { + "name": "__34-[GULMutableDictionary dictionary]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "GULAppEnvironmentUtil.m", + "coverage": 0.5681818181818182, + "type": "objc", + "functions": [ + { + "name": "IsAppEncrypted", + "coverage": 0.7209302325581395 + }, + { + "name": "HasSCInfoFolder", + "coverage": 0 + }, + { + "name": "HasEmbeddedMobileProvision", + "coverage": 0 + }, + { + "name": "+[GULAppEnvironmentUtil isFromAppStore]", + "coverage": 0.3548387096774194 + }, + { + "name": "__39+[GULAppEnvironmentUtil isFromAppStore]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULAppEnvironmentUtil isAppStoreReceiptSandbox]", + "coverage": 0 + }, + { + "name": "+[GULAppEnvironmentUtil isSimulator]", + "coverage": 1 + }, + { + "name": "+[GULAppEnvironmentUtil deviceModel]", + "coverage": 1 + }, + { + "name": "__36+[GULAppEnvironmentUtil deviceModel]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULAppEnvironmentUtil systemVersion]", + "coverage": 1 + }, + { + "name": "+[GULAppEnvironmentUtil isAppExtension]", + "coverage": 1 + } + ] + }, + { + "name": "GULReachabilityChecker.m", + "coverage": 0.58, + "type": "objc", + "functions": [ + { + "name": "-[GULReachabilityChecker reachabilityApi]", + "coverage": 0 + }, + { + "name": "-[GULReachabilityChecker setReachabilityApi:]", + "coverage": 0 + }, + { + "name": "-[GULReachabilityChecker isActive]", + "coverage": 0 + }, + { + "name": "-[GULReachabilityChecker setReachabilityDelegate:]", + "coverage": 0.5 + }, + { + "name": "-[GULReachabilityChecker initWithReachabilityDelegate:withHost:]", + "coverage": 0.7222222222222222 + }, + { + "name": "-[GULReachabilityChecker dealloc]", + "coverage": 1 + }, + { + "name": "-[GULReachabilityChecker start]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[GULReachabilityChecker stop]", + "coverage": 1 + }, + { + "name": "-[GULReachabilityChecker statusForFlags:]", + "coverage": 0.6190476190476191 + }, + { + "name": "-[GULReachabilityChecker reachabilityFlagsChanged:]", + "coverage": 0.95 + }, + { + "name": "ReachabilityCallback", + "coverage": 1 + }, + { + "name": "GULReachabilityStatusString", + "coverage": 0 + } + ] + }, + { + "name": "GULSwizzler.m", + "coverage": 0.7844036697247706, + "type": "objc", + "functions": [ + { + "name": "GetGULSwizzlingQueue", + "coverage": 1 + }, + { + "name": "__GetGULSwizzlingQueue_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULSwizzler swizzleClass:selector:isClassSelector:withBlock:]", + "coverage": 1 + }, + { + "name": "__63+[GULSwizzler swizzleClass:selector:isClassSelector:withBlock:]_block_invoke", + "coverage": 0.9512195121951219 + }, + { + "name": "+[GULSwizzler unswizzleClass:selector:isClassSelector:]", + "coverage": 1 + }, + { + "name": "__55+[GULSwizzler unswizzleClass:selector:isClassSelector:]_block_invoke", + "coverage": 0.9047619047619048 + }, + { + "name": "+[GULSwizzler originalImplementationForClass:selector:isClassSelector:]", + "coverage": 0 + }, + { + "name": "__71+[GULSwizzler originalImplementationForClass:selector:isClassSelector:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[GULSwizzler currentImplementationForClass:selector:isClassSelector:]", + "coverage": 0.92 + }, + { + "name": "__70+[GULSwizzler currentImplementationForClass:selector:isClassSelector:]_block_invoke", + "coverage": 0.8125 + }, + { + "name": "+[GULSwizzler selector:existsInClass:isClassSelector:]", + "coverage": 0 + }, + { + "name": "+[GULSwizzler ivarObjectsForObject:]", + "coverage": 0 + } + ] + }, + { + "name": "GULSwizzlingCache.m", + "coverage": 0.8157894736842105, + "type": "objc", + "functions": [ + { + "name": "+[GULSwizzlingCache sharedInstance]", + "coverage": 1 + }, + { + "name": "__35+[GULSwizzlingCache sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache init]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache dealloc]", + "coverage": 0 + }, + { + "name": "-[GULSwizzlingCache cacheCurrentIMP:forNewIMP:forClass:withSelector:]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache cachedIMPForClass:withSelector:]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache clearCacheForSwizzledIMP:selector:aClass:]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache originalIMPOfCurrentIMP:]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache clearCache]", + "coverage": 0 + }, + { + "name": "-[GULSwizzlingCache originalImps]", + "coverage": 0 + }, + { + "name": "-[GULSwizzlingCache newToOriginalImps]", + "coverage": 0 + } + ] + }, + { + "name": "GULLogger.m", + "coverage": 0.8333333333333334, + "type": "objc", + "functions": [ + { + "name": "GULLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__GULLoggerInitializeASL_block_invoke", + "coverage": 1 + }, + { + "name": "GULLoggerEnableSTDERR", + "coverage": 0 + }, + { + "name": "GULLoggerForceDebug", + "coverage": 1 + }, + { + "name": "GULSetLoggerLevel", + "coverage": 0.6470588235294118 + }, + { + "name": "__GULSetLoggerLevel_block_invoke", + "coverage": 1 + }, + { + "name": "GULIsLoggableLevel", + "coverage": 0.7142857142857143 + }, + { + "name": "GULResetLogger", + "coverage": 1 + }, + { + "name": "getGULLoggerClient", + "coverage": 0 + }, + { + "name": "getGULClientQueue", + "coverage": 0 + }, + { + "name": "getGULLoggerDebugMode", + "coverage": 0 + }, + { + "name": "GULLoggerRegisterVersion", + "coverage": 1 + }, + { + "name": "GULLogBasic", + "coverage": 1 + }, + { + "name": "__GULLogBasic_block_invoke", + "coverage": 1 + }, + { + "name": "Definition at 182:46", + "coverage": 1 + }, + { + "name": "+[GULLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + } + ] + }, + { + "name": "GoogleUtilities.framework", + "coverage": 0.4294416243654822, + "files": [ + { + "name": "GULRuntimeDiff.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "IsEqual", + "coverage": 0 + }, + { + "name": "-[GULRuntimeDiff hash]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeDiff isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeDiff description]", + "coverage": 0 + } + ] + }, + { + "name": "GULRuntimeStateHelper.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "+[GULRuntimeStateHelper snapshotCache]", + "coverage": 0 + }, + { + "name": "__38+[GULRuntimeStateHelper snapshotCache]_block_invoke", + "coverage": 0 + }, + { + "name": "+[GULRuntimeStateHelper captureRuntimeState]", + "coverage": 0 + }, + { + "name": "+[GULRuntimeStateHelper captureRuntimeStateOfClasses:]", + "coverage": 0 + }, + { + "name": "+[GULRuntimeStateHelper diffBetween:secondSnapshot:]", + "coverage": 0 + } + ] + }, + { + "name": "GULRuntimeSnapshot.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[GULRuntimeSnapshot init]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot initWithClasses:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot hash]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot description]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot capture]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeSnapshot diff:]", + "coverage": 0 + }, + { + "name": "__27-[GULRuntimeSnapshot diff:]_block_invoke", + "coverage": 0 + }, + { + "name": "__27-[GULRuntimeSnapshot diff:]_block_invoke.34", + "coverage": 0 + }, + { + "name": "__27-[GULRuntimeSnapshot diff:]_block_invoke.43", + "coverage": 0 + } + ] + }, + { + "name": "GULProxy.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[GULProxy initWithDelegate:]", + "coverage": 0 + }, + { + "name": "+[GULProxy proxyWithDelegate:]", + "coverage": 0 + }, + { + "name": "-[GULProxy forwardingTargetForSelector:]", + "coverage": 0 + }, + { + "name": "-[GULProxy forwardInvocation:]", + "coverage": 0 + }, + { + "name": "-[GULProxy methodSignatureForSelector:]", + "coverage": 0 + }, + { + "name": "-[GULProxy respondsToSelector:]", + "coverage": 0 + }, + { + "name": "-[GULProxy isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULProxy hash]", + "coverage": 0 + }, + { + "name": "-[GULProxy superclass]", + "coverage": 0 + }, + { + "name": "-[GULProxy class]", + "coverage": 0 + }, + { + "name": "-[GULProxy isKindOfClass:]", + "coverage": 0 + }, + { + "name": "-[GULProxy isMemberOfClass:]", + "coverage": 0 + }, + { + "name": "-[GULProxy conformsToProtocol:]", + "coverage": 0 + }, + { + "name": "-[GULProxy isProxy]", + "coverage": 0 + }, + { + "name": "-[GULProxy description]", + "coverage": 0 + }, + { + "name": "-[GULProxy debugDescription]", + "coverage": 0 + } + ] + }, + { + "name": "GULRuntimeClassDiff.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "IsEqual", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassDiff hash]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassDiff isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassDiff description]", + "coverage": 0 + } + ] + }, + { + "name": "GULRuntimeClassSnapshot.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[GULRuntimeClassSnapshot init]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot initWithClass:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot capture]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot diff:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot hash]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot isEqual:]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot description]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot captureProperties]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot captureSelectorsAndImps]", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot computeDiffOfProperties:withClassDiff:]", + "coverage": 0 + }, + { + "name": "__65-[GULRuntimeClassSnapshot computeDiffOfProperties:withClassDiff:]_block_invoke", + "coverage": 0 + }, + { + "name": "__65-[GULRuntimeClassSnapshot computeDiffOfProperties:withClassDiff:]_block_invoke.46", + "coverage": 0 + }, + { + "name": "-[GULRuntimeClassSnapshot computeDiffOfSelectorsAndImps:withClassDiff:]", + "coverage": 0 + }, + { + "name": "__71-[GULRuntimeClassSnapshot computeDiffOfSelectorsAndImps:withClassDiff:]_block_invoke", + "coverage": 0 + }, + { + "name": "__71-[GULRuntimeClassSnapshot computeDiffOfSelectorsAndImps:withClassDiff:]_block_invoke.57", + "coverage": 0 + }, + { + "name": "__71-[GULRuntimeClassSnapshot computeDiffOfSelectorsAndImps:withClassDiff:]_block_invoke.63", + "coverage": 0 + } + ] + }, + { + "name": "GULAppEnvironmentUtil.m", + "coverage": 0.5681818181818182, + "type": "objc", + "functions": [ + { + "name": "IsAppEncrypted", + "coverage": 0.7209302325581395 + }, + { + "name": "HasSCInfoFolder", + "coverage": 0 + }, + { + "name": "HasEmbeddedMobileProvision", + "coverage": 0 + }, + { + "name": "+[GULAppEnvironmentUtil isFromAppStore]", + "coverage": 0.3548387096774194 + }, + { + "name": "__39+[GULAppEnvironmentUtil isFromAppStore]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULAppEnvironmentUtil isAppStoreReceiptSandbox]", + "coverage": 0 + }, + { + "name": "+[GULAppEnvironmentUtil isSimulator]", + "coverage": 1 + }, + { + "name": "+[GULAppEnvironmentUtil deviceModel]", + "coverage": 1 + }, + { + "name": "__36+[GULAppEnvironmentUtil deviceModel]_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULAppEnvironmentUtil systemVersion]", + "coverage": 1 + }, + { + "name": "+[GULAppEnvironmentUtil isAppExtension]", + "coverage": 1 + } + ] + }, + { + "name": "GULSwizzler.m", + "coverage": 0.7844036697247706, + "type": "objc", + "functions": [ + { + "name": "GetGULSwizzlingQueue", + "coverage": 1 + }, + { + "name": "__GetGULSwizzlingQueue_block_invoke", + "coverage": 1 + }, + { + "name": "+[GULSwizzler swizzleClass:selector:isClassSelector:withBlock:]", + "coverage": 1 + }, + { + "name": "__63+[GULSwizzler swizzleClass:selector:isClassSelector:withBlock:]_block_invoke", + "coverage": 0.9512195121951219 + }, + { + "name": "+[GULSwizzler unswizzleClass:selector:isClassSelector:]", + "coverage": 1 + }, + { + "name": "__55+[GULSwizzler unswizzleClass:selector:isClassSelector:]_block_invoke", + "coverage": 0.9047619047619048 + }, + { + "name": "+[GULSwizzler originalImplementationForClass:selector:isClassSelector:]", + "coverage": 0 + }, + { + "name": "__71+[GULSwizzler originalImplementationForClass:selector:isClassSelector:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[GULSwizzler currentImplementationForClass:selector:isClassSelector:]", + "coverage": 0.92 + }, + { + "name": "__70+[GULSwizzler currentImplementationForClass:selector:isClassSelector:]_block_invoke", + "coverage": 0.8125 + }, + { + "name": "+[GULSwizzler selector:existsInClass:isClassSelector:]", + "coverage": 0 + }, + { + "name": "+[GULSwizzler ivarObjectsForObject:]", + "coverage": 0 + } + ] + }, + { + "name": "GULSwizzlingCache.m", + "coverage": 0.8157894736842105, + "type": "objc", + "functions": [ + { + "name": "+[GULSwizzlingCache sharedInstance]", + "coverage": 1 + }, + { + "name": "__35+[GULSwizzlingCache sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache init]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache dealloc]", + "coverage": 0 + }, + { + "name": "-[GULSwizzlingCache cacheCurrentIMP:forNewIMP:forClass:withSelector:]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache cachedIMPForClass:withSelector:]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache clearCacheForSwizzledIMP:selector:aClass:]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache originalIMPOfCurrentIMP:]", + "coverage": 1 + }, + { + "name": "-[GULSwizzlingCache clearCache]", + "coverage": 0 + }, + { + "name": "-[GULSwizzlingCache originalImps]", + "coverage": 0 + }, + { + "name": "-[GULSwizzlingCache newToOriginalImps]", + "coverage": 0 + } + ] + }, + { + "name": "GULLogger.m", + "coverage": 0.8333333333333334, + "type": "objc", + "functions": [ + { + "name": "GULLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__GULLoggerInitializeASL_block_invoke", + "coverage": 1 + }, + { + "name": "GULLoggerEnableSTDERR", + "coverage": 0 + }, + { + "name": "GULLoggerForceDebug", + "coverage": 1 + }, + { + "name": "GULSetLoggerLevel", + "coverage": 0.6470588235294118 + }, + { + "name": "__GULSetLoggerLevel_block_invoke", + "coverage": 1 + }, + { + "name": "GULIsLoggableLevel", + "coverage": 0.7142857142857143 + }, + { + "name": "GULResetLogger", + "coverage": 1 + }, + { + "name": "getGULLoggerClient", + "coverage": 0 + }, + { + "name": "getGULClientQueue", + "coverage": 0 + }, + { + "name": "getGULLoggerDebugMode", + "coverage": 0 + }, + { + "name": "GULLoggerRegisterVersion", + "coverage": 1 + }, + { + "name": "GULLogBasic", + "coverage": 1 + }, + { + "name": "__GULLogBasic_block_invoke", + "coverage": 1 + }, + { + "name": "Definition at 182:46", + "coverage": 1 + }, + { + "name": "+[GULLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + } + ] + }, + { + "name": "InstanceID_Example_iOS.app", + "coverage": 0.8083930704898447, + "files": [ + { + "name": "FIRInstanceIDTokenDeleteOperation.m", + "coverage": 0.12643678160919541, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenDeleteOperation initWithAuthorizedEntity:scope:checkinPreferences:keyPair:action:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenDeleteOperation performTokenOperation]", + "coverage": 0 + }, + { + "name": "__58-[FIRInstanceIDTokenDeleteOperation performTokenOperation]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenDeleteOperation handleResponseWithData:response:error:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAppDelegate.m", + "coverage": 0.2413793103448276, + "type": "objc", + "functions": [ + { + "name": "-[FIRAppDelegate application:didFinishLaunchingWithOptions:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillResignActive:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidEnterBackground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationWillEnterForeground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidBecomeActive:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillTerminate:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceID+Private.m", + "coverage": 0.3333333333333333, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceID(Private) cachedCheckinPreferences]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID(Private) fetchCheckinInfoWithHandler:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID(Private) appInstanceID:]", + "coverage": 1 + } + ] + }, + { + "name": "NSError+FIRInstanceID.m", + "coverage": 0.375, + "type": "objc", + "functions": [ + { + "name": "-[NSError(FIRInstanceID) instanceIDErrorCode]", + "coverage": 0 + }, + { + "name": "+[NSError(FIRInstanceID) errorWithFIRInstanceIDErrorCode:]", + "coverage": 1 + }, + { + "name": "+[NSError(FIRInstanceID) errorWithFIRInstanceIDErrorCode:userInfo:]", + "coverage": 1 + }, + { + "name": "+[NSError(FIRInstanceID) FIRInstanceIDErrorMissingCheckin]", + "coverage": 0 + } + ] + }, + { + "name": "FIRViewController.m", + "coverage": 0.5, + "type": "objc", + "functions": [ + { + "name": "-[FIRViewController viewDidLoad]", + "coverage": 1 + }, + { + "name": "-[FIRViewController didReceiveMemoryWarning]", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDStringEncoding.m", + "coverage": 0.5308641975308642, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDStringEncoding rfc4648Base64WebsafeStringEncoding]", + "coverage": 1 + }, + { + "name": "lcm", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStringEncoding stringEncodingWithString:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding initWithString:]", + "coverage": 0.6896551724137931 + }, + { + "name": "-[FIRInstanceIDStringEncoding description]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDStringEncoding doPad]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDStringEncoding setDoPad:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding setPaddingChar:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding encode:]", + "coverage": 0.8444444444444444 + }, + { + "name": "-[FIRInstanceIDStringEncoding decode:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDTokenManager.m", + "coverage": 0.7067039106145251, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenManager init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager configureTokenOperations]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke.52", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke_2.53", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager cachedTokenInfoWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]", + "coverage": 0.9166666666666666 + }, + { + "name": "__83-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__83-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke.86", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteAllTokensLocallyWithHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager stopAllTokenOperations]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager store:didDeleteFCMScopedTokensForCheckin:]", + "coverage": 0 + }, + { + "name": "__70-[FIRInstanceIDTokenManager store:didDeleteFCMScopedTokensForCheckin:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager createFetchOperationWithAuthorizedEntity:scope:options:keyPair:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager createDeleteOperationWithAuthorizedEntity:scope:checkinPreferences:keyPair:action:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager checkForTokenRefreshPolicy]", + "coverage": 0.8125 + }, + { + "name": "-[FIRInstanceIDTokenManager updateTokensToAPNSDeviceToken:isSandbox:]", + "coverage": 0.9333333333333333 + } + ] + }, + { + "name": "FIRInstanceIDLogger.m", + "coverage": 0.72, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDLogger formatMessageCode:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncDebug:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncInfo:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncNotice:messageCode:msg:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDLogger logFuncWarning:messageCode:msg:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDLogger logFuncError:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "FIRInstanceIDSharedLogger", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDSharedLogger_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceID.m", + "coverage": 0.7266414141414141, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDResult copyWithZone:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID instanceID]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIRInstanceID initPrivately]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID instanceIDForTests]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID token]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID instanceIDWithHandler:]", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceID instanceIDWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceID instanceIDWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID cachedTokenIfAvailable]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setDefaultFCMToken:]", + "coverage": 0.8666666666666667 + }, + { + "name": "-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]", + "coverage": 0.8584905660377359 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke.163", + "coverage": 0.9302325581395349 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke_2.171", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]", + "coverage": 0.84375 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke", + "coverage": 0.8 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke.195", + "coverage": 0.8695652173913043 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke_2.196", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID asyncLoadKeyPairWithHandler:]", + "coverage": 1 + }, + { + "name": "__45-[FIRInstanceID asyncLoadKeyPairWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__45-[FIRInstanceID asyncLoadKeyPairWithHandler:]_block_invoke_2", + "coverage": 0.7222222222222222 + }, + { + "name": "-[FIRInstanceID getIDWithHandler:]", + "coverage": 0.7837837837837838 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke.223", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteIDWithHandler:]", + "coverage": 0.8333333333333334 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke", + "coverage": 0.5555555555555556 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke.240", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke_2.241", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke.250", + "coverage": 0.8 + }, + { + "name": "-[FIRInstanceID notifyIdentityReset]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteIdentityWithHandler:]", + "coverage": 1 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke", + "coverage": 0.9137931034482759 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_4", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID load]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID componentsToRegister]", + "coverage": 1 + }, + { + "name": "__37+[FIRInstanceID componentsToRegister]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID configureWithApp:]", + "coverage": 0.4444444444444444 + }, + { + "name": "-[FIRInstanceID configureInstanceIDWithOptions:app:]", + "coverage": 0.7222222222222222 + }, + { + "name": "+[FIRInstanceID configureErrorWithReason:]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID exitWithReason:forFirebaseApp:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID didCompleteConfigure]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID isFCMAutoInitEnabled]", + "coverage": 0.8709677419354839 + }, + { + "name": "-[FIRInstanceID start]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FIRInstanceID setupTokenManager]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setupKeyPairManager]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setupNotificationListeners]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID maxRetryCountForDefaultToken]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID minIntervalForDefaultTokenRetry]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID maxRetryIntervalForDefaultTokenInSeconds]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID retryIntervalToFetchDefaultToken]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIRInstanceID fetchDefaultToken]", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID fetchDefaultToken]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID defaultTokenWithHandler:]", + "coverage": 0.89 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke", + "coverage": 0.8133333333333334 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke.406", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID notifyAPNSTokenIsSet:]", + "coverage": 0 + }, + { + "name": "__38-[FIRInstanceID notifyAPNSTokenIsSet:]_block_invoke", + "coverage": 0 + }, + { + "name": "__38-[FIRInstanceID notifyAPNSTokenIsSet:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID isSandboxApp]", + "coverage": 1 + }, + { + "name": "__29-[FIRInstanceID isSandboxApp]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID isProductionApp]", + "coverage": 0.22556390977443608 + }, + { + "name": "-[FIRInstanceID logAPNSConfigurationError:]", + "coverage": 0.875 + } + ] + }, + { + "name": "FIRInstanceIDTokenFetchOperation.m", + "coverage": 0.75, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenFetchOperation initWithAuthorizedEntity:scope:options:checkinPreferences:keyPair:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation performTokenOperation]", + "coverage": 0.9206349206349206 + }, + { + "name": "__57-[FIRInstanceIDTokenFetchOperation performTokenOperation]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation handleResponseWithData:response:error:]", + "coverage": 0.5454545454545454 + }, + { + "name": "__74-[FIRInstanceIDTokenFetchOperation handleResponseWithData:response:error:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation parseFetchTokenResponse:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDVersionUtilities.m", + "coverage": 0.775, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDParseCurrentLibraryVersion", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDParseCurrentLibraryVersion_block_invoke", + "coverage": 0.9285714285714286 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionMajor", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionMinor", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionPatch", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionIsBeta", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDURLQueryItem.m", + "coverage": 0.78125, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDURLQueryItem queryItemWithName:value:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDURLQueryItem initWithName:value:]", + "coverage": 1 + }, + { + "name": "FIRInstanceIDQueryFromQueryItems", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FIRInstanceIDBackupExcludedPlist.m", + "coverage": 0.7816901408450704, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDBackupExcludedPlist initWithFileName:subDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist writeDictionary:error:]", + "coverage": 0.696969696969697 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist deleteFile:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist contentAsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist moveToApplicationSupportSubDirectory:]", + "coverage": 0.5428571428571428 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist doesFileExist]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist plistDirectory]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist plistPathInDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist pathWithName:inDirectory:]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist doesFileExistInDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist supportedDirectory]", + "coverage": 1 + } + ] + }, + { + "name": "FIRDependency.m", + "coverage": 0.7857142857142857, + "type": "objc", + "functions": [ + { + "name": "+[FIRDependency dependencyWithProtocol:]", + "coverage": 0 + }, + { + "name": "+[FIRDependency dependencyWithProtocol:isRequired:]", + "coverage": 1 + }, + { + "name": "-[FIRDependency initWithProtocol:isRequired:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRBundleUtil.m", + "coverage": 0.7872340425531915, + "type": "objc", + "functions": [ + { + "name": "+[FIRBundleUtil relevantBundles]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil optionsDictionaryPathWithResourceName:andFileType:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil relevantURLSchemes]", + "coverage": 0 + }, + { + "name": "+[FIRBundleUtil hasBundleIdentifierPrefix:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil bundleIdentifierByRemovingLastPartFrom:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinStore.m", + "coverage": 0.7874396135265701, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinStore initWithCheckinPlistFileName:subDirectoryName:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore initWithCheckinPlist:keychain:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore hasCheckinPlist]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore bundleIdentifierForKeychainAccount]", + "coverage": 1 + }, + { + "name": "__63-[FIRInstanceIDCheckinStore bundleIdentifierForKeychainAccount]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore saveCheckinPreferences:handler:]", + "coverage": 0.5573770491803278 + }, + { + "name": "__60-[FIRInstanceIDCheckinStore saveCheckinPreferences:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore removeCheckinPreferencesWithHandler:]", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceIDCheckinStore removeCheckinPreferencesWithHandler:]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRInstanceIDCheckinStore cachedCheckinPreferences]", + "coverage": 0.7073170731707317 + }, + { + "name": "-[FIRInstanceIDCheckinStore migrateCheckinItemIfNeeded]", + "coverage": 1 + } + ] + }, + { + "name": "FIRConfiguration.m", + "coverage": 0.8076923076923077, + "type": "objc", + "functions": [ + { + "name": "+[FIRConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__34+[FIRConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration init]", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration setLoggerLevel:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRLogger.m", + "coverage": 0.8118811881188119, + "type": "objc", + "functions": [ + { + "name": "FIRLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__FIRLoggerInitializeASL_block_invoke", + "coverage": 0.9333333333333333 + }, + { + "name": "FIRSetAnalyticsDebugMode", + "coverage": 0 + }, + { + "name": "FIRSetLoggerLevel", + "coverage": 0 + }, + { + "name": "FIRResetLogger", + "coverage": 1 + }, + { + "name": "FIRSetLoggerUserDefaults", + "coverage": 1 + }, + { + "name": "FIRIsLoggableLevel", + "coverage": 0 + }, + { + "name": "FIRLogBasic", + "coverage": 1 + }, + { + "name": "Definition at 158:95", + "coverage": 1 + }, + { + "name": "+[FIRLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDTokenOperation.m", + "coverage": 0.8323353293413174, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDTokenOperation sharedURLSession]", + "coverage": 0 + }, + { + "name": "__47+[FIRInstanceIDTokenOperation sharedURLSession]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenOperation initWithAction:forAuthorizedEntity:scope:options:checkinPreferences:keyPair:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation addCompletionHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation isAsynchronous]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenOperation isExecuting]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation setExecuting:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation isFinished]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation setFinished:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation start]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRInstanceIDTokenOperation finishWithResult:token:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation cancel]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation performTokenOperation]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceIDTokenOperation requestWithAuthHeader:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenOperation standardQueryItemsWithDeviceID:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation queryItemsWithKeyPair:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenOperation HTTPAuthHeaderFromCheckin:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPairStore.m", + "coverage": 0.8351449275362319, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDKeyDataWithTag", + "coverage": 0.8333333333333334 + }, + { + "name": "FIRInstanceIDCachedKeyRefWithTag", + "coverage": 0.7777777777777778 + }, + { + "name": "FIRInstanceIDHasMigratedKeyPair", + "coverage": 1 + }, + { + "name": "FIRInstanceIDLegacyPublicTagWithSubtype", + "coverage": 1 + }, + { + "name": "FIRInstanceIDLegacyPrivateTagWithSubtype", + "coverage": 1 + }, + { + "name": "FIRInstanceIDPublicTagWithSubtype", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDPublicTagWithSubtype_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDPrivateTagWithSubtype", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDPrivateTagWithSubtype_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCreationTimeKeyWithSubtype", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore invalidateKeyPairsIfNeeded]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore hasCachedKeyPairs]", + "coverage": 0.29411764705882354 + }, + { + "name": "-[FIRInstanceIDKeyPairStore appIdentityWithError:]", + "coverage": 0.6 + }, + { + "name": "-[FIRInstanceIDKeyPairStore loadKeyPairWithError:]", + "coverage": 0.78125 + }, + { + "name": "-[FIRInstanceIDKeyPairStore generateAndSaveKeyWithSubtype:creationTime:error:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRInstanceIDKeyPairStore validCachedKeyPairWithSubtype:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore cachedKeyPairWithSubtype:error:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDKeyPairStore keyPairForPrivateKeyTag:publicKeyTag:error:]", + "coverage": 0.8387096774193549 + }, + { + "name": "-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]", + "coverage": 0.8771929824561403 + }, + { + "name": "__68-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]_block_invoke", + "coverage": 0.8636363636363636 + }, + { + "name": "__68-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]_block_invoke_2", + "coverage": 0.6428571428571429 + }, + { + "name": "-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]", + "coverage": 1 + }, + { + "name": "__58-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]_block_invoke", + "coverage": 0.7619047619047619 + }, + { + "name": "__58-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore deleteSavedKeyPairWithSubtype:handler:]", + "coverage": 0.9375 + }, + { + "name": "__67-[FIRInstanceIDKeyPairStore deleteSavedKeyPairWithSubtype:handler:]_block_invoke", + "coverage": 0.76 + }, + { + "name": "+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]", + "coverage": 1 + }, + { + "name": "__75+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]_block_invoke", + "coverage": 0.7619047619047619 + }, + { + "name": "__75+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]_block_invoke_2", + "coverage": 0.5454545454545454 + }, + { + "name": "-[FIRInstanceIDKeyPairStore removeKeyPairCreationTimePlistWithError:]", + "coverage": 0.5 + }, + { + "name": "+[FIRInstanceIDKeyPairStore keyStoreFileName]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDUtilities.m", + "coverage": 0.8547008547008547, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDRegisterServer", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentTimestampInSeconds", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentTimestampInMilliseconds", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentAppVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAppIdentifier", + "coverage": 1 + }, + { + "name": "FIRInstanceIDFirebaseAppID", + "coverage": 1 + }, + { + "name": "FIRInstanceIDDeviceModel", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDDeviceModel_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDOperatingSystemVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDHasLocaleChanged", + "coverage": 1 + }, + { + "name": "FIRInstanceIDIsValidGCMScope", + "coverage": 1 + }, + { + "name": "FIRInstanceIDStringForAPNSDeviceToken", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAPNSTupleStringForTokenAndServerType", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentGCMVersion", + "coverage": 0.6 + }, + { + "name": "FIRInstanceIDCurrentLocale", + "coverage": 0.4583333333333333 + } + ] + }, + { + "name": "FIRInstanceIDStore.m", + "coverage": 0.8579234972677595, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDStore initWithDelegate:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore initWithCheckinStore:tokenStore:delegate:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore hasSubDirectory:]", + "coverage": 0.8181818181818182 + }, + { + "name": "+[FIRInstanceIDStore supportedDirectory]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore pathForSupportSubDirectory:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore createSubDirectory:]", + "coverage": 0.6 + }, + { + "name": "+[FIRInstanceIDStore removeSubDirectory:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore resetCredentialsIfNeeded]", + "coverage": 1 + }, + { + "name": "__46-[FIRInstanceIDStore resetCredentialsIfNeeded]_block_invoke", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRInstanceIDStore tokenInfoWithAuthorizedEntity:scope:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRInstanceIDStore cachedTokenInfos]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore saveTokenInfo:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore removeCachedTokenWithAuthorizedEntity:scope:]", + "coverage": 0.4444444444444444 + }, + { + "name": "-[FIRInstanceIDStore removeAllCachedTokensWithHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore saveCheckinPreferences:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore cachedCheckinPreferences]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore removeCheckinPreferencesWithHandler:]", + "coverage": 1 + }, + { + "name": "__58-[FIRInstanceIDStore removeCheckinPreferencesWithHandler:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIRApp.m", + "coverage": 0.8848, + "type": "objc", + "functions": [ + { + "name": "+[FIRApp configure]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithOptions:]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithName:options:]", + "coverage": 0.9333333333333333 + }, + { + "name": "+[FIRApp defaultApp]", + "coverage": 1 + }, + { + "name": "+[FIRApp appNamed:]", + "coverage": 1 + }, + { + "name": "+[FIRApp allApps]", + "coverage": 1 + }, + { + "name": "+[FIRApp resetApps]", + "coverage": 1 + }, + { + "name": "-[FIRApp deleteApp:]", + "coverage": 0.7916666666666666 + }, + { + "name": "+[FIRApp addAppToAppDictionary:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRApp initInstanceWithName:options:]", + "coverage": 1 + }, + { + "name": "-[FIRApp configureCore]", + "coverage": 0.7962962962962963 + }, + { + "name": "-[FIRApp options]", + "coverage": 1 + }, + { + "name": "-[FIRApp setDataCollectionDefaultEnabled:]", + "coverage": 0.9354838709677419 + }, + { + "name": "-[FIRApp isDataCollectionDefaultEnabled]", + "coverage": 1 + }, + { + "name": "+[FIRApp sendNotificationsToSDKs:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForMissingOptions]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForSubspecConfigurationFailureWithDomain:errorCode:service:reason:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForInvalidAppID]", + "coverage": 0 + }, + { + "name": "+[FIRApp isDefaultAppConfigured]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerLibrary:withVersion:]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerInternalLibrary:withName:withVersion:]", + "coverage": 1 + }, + { + "name": "__55+[FIRApp registerInternalLibrary:withName:withVersion:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRApp firebaseUserAgent]", + "coverage": 1 + }, + { + "name": "-[FIRApp checkExpectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp isAppIDValid]", + "coverage": 1 + }, + { + "name": "+[FIRApp validateAppID:]", + "coverage": 0.9473684210526315 + }, + { + "name": "+[FIRApp actualBundleID]", + "coverage": 0 + }, + { + "name": "+[FIRApp validateAppIDFormat:withVersion:]", + "coverage": 0.8450704225352113 + }, + { + "name": "+[FIRApp validateAppIDFingerprint:withVersion:]", + "coverage": 0.8620689655172413 + }, + { + "name": "-[FIRApp expectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp clearDataCollectionSwitchFromUserDefaults]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromUserDefaultsForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromPlist]", + "coverage": 1 + }, + { + "name": "__43+[FIRApp readDataCollectionSwitchFromPlist]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRApp sendLogsWithServiceName:version:error:]", + "coverage": 0.2631578947368421 + } + ] + }, + { + "name": "FIRInstanceIDTokenInfo.m", + "coverage": 0.889763779527559, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenInfo initWithAuthorizedEntity:scope:token:appVersion:firebaseAppID:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenInfo isFresh]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenInfo initWithCoder:]", + "coverage": 0.78125 + }, + { + "name": "-[FIRInstanceIDTokenInfo encodeWithCoder:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinPreferences.m", + "coverage": 0.8913043478260869, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinPreferences checkinPlistContents]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences hasCheckinInfo]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences hasValidCheckinInfo]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences setHasPreCachedAuthCredentials:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinService.m", + "coverage": 0.896414342629482, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinService init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService checkinWithExistingCheckin:completion:]", + "coverage": 0.9649122807017544 + }, + { + "name": "__69-[FIRInstanceIDCheckinService checkinWithExistingCheckin:completion:]_block_invoke", + "coverage": 0.7105263157894737 + }, + { + "name": "-[FIRInstanceIDCheckinService stopFetching]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService checkinParametersWithExistingCheckin:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinService setCheckinTestBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDAPNSInfo.m", + "coverage": 0.9090909090909091, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAPNSInfo initWithDeviceToken:isSandbox:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo initWithTokenOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo initWithCoder:]", + "coverage": 0.75 + }, + { + "name": "-[FIRInstanceIDAPNSInfo encodeWithCoder:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo isEqualToAPNSInfo:]", + "coverage": 0.7142857142857143 + } + ] + }, + { + "name": "FIRInstanceIDAuthService.m", + "coverage": 0.9166666666666666, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAuthService initWithCheckinService:store:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService initWithStore:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService scheduleCheckin:]", + "coverage": 0.7 + }, + { + "name": "-[FIRInstanceIDAuthService startCheckinTimerWithDuration:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService clearScheduledCheckinTimer]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService onScheduledCheckinTimerFired:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService performScheduledCheckin]", + "coverage": 1 + }, + { + "name": "__51-[FIRInstanceIDAuthService performScheduledCheckin]_block_invoke", + "coverage": 1 + }, + { + "name": "__51-[FIRInstanceIDAuthService performScheduledCheckin]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService calculateNextCheckinRetryIntervalInSeconds]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRInstanceIDAuthService hasValidCheckinInfo]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService checkinPreferences]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService stopCheckinRequest]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService resetCheckinWithHandler:]", + "coverage": 0 + }, + { + "name": "__52-[FIRInstanceIDAuthService resetCheckinWithHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDAuthService notifyCheckinHandlersWithCheckin:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService cachedCheckinMatchesCheckin:]", + "coverage": 0.5714285714285714 + } + ] + }, + { + "name": "FIRInstanceIDKeychain.m", + "coverage": 0.9227053140096618, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDKeychain sharedInstance]", + "coverage": 1 + }, + { + "name": "__39+[FIRInstanceIDKeychain sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain itemWithQuery:]", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceIDKeychain itemWithQuery:]_block_invoke", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRInstanceIDKeychain removeItemWithQuery:handler:]", + "coverage": 1 + }, + { + "name": "__53-[FIRInstanceIDKeychain removeItemWithQuery:handler:]_block_invoke", + "coverage": 0.8260869565217391 + }, + { + "name": "__53-[FIRInstanceIDKeychain removeItemWithQuery:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain addItemWithQuery:handler:]", + "coverage": 1 + }, + { + "name": "__50-[FIRInstanceIDKeychain addItemWithQuery:handler:]_block_invoke", + "coverage": 0.6470588235294118 + }, + { + "name": "__50-[FIRInstanceIDKeychain addItemWithQuery:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain generateKeyPairWithPrivateTag:publicTag:]", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceIDKeychain generateKeyPairWithPrivateTag:publicTag:]_block_invoke", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FIRInstanceIDAuthKeyChain.m", + "coverage": 0.9405405405405406, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAuthKeychain initWithIdentifier:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDAuthKeychain keychainQueryForService:account:generic:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain keychainQueryForService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain itemsMatchingService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain dataForService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain removeItemsMatchingService:account:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain setData:forService:accessibility:account:handler:]", + "coverage": 0.9183673469387755 + }, + { + "name": "__78-[FIRInstanceIDAuthKeychain setData:forService:accessibility:account:handler:]_block_invoke", + "coverage": 0.72 + } + ] + }, + { + "name": "FIROptions.m", + "coverage": 0.9489795918367347, + "type": "objc", + "functions": [ + { + "name": "+[FIROptions defaultOptions]", + "coverage": 0.8461538461538461 + }, + { + "name": "+[FIROptions initialize]", + "coverage": 1 + }, + { + "name": "+[FIROptions defaultOptionsDictionary]", + "coverage": 1 + }, + { + "name": "+[FIROptions plistFilePathWithName:]", + "coverage": 1 + }, + { + "name": "+[FIROptions resetDefaultOptions]", + "coverage": 1 + }, + { + "name": "-[FIROptions initInternalWithOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithContentsOfFile:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithGoogleAppID:GCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions APIKey]", + "coverage": 1 + }, + { + "name": "-[FIROptions checkEditingLocked]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAPIKey:]", + "coverage": 1 + }, + { + "name": "-[FIROptions clientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions trackingID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setTrackingID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions GCMSenderID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions projectID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setProjectID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions androidClientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAndroidClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions googleAppID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGoogleAppID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions libraryVersionID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setLibraryVersionID:]", + "coverage": 0 + }, + { + "name": "-[FIROptions databaseURL]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDatabaseURL:]", + "coverage": 1 + }, + { + "name": "-[FIROptions storageBucket]", + "coverage": 1 + }, + { + "name": "-[FIROptions setStorageBucket:]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDeepLinkURLScheme:]", + "coverage": 1 + }, + { + "name": "-[FIROptions bundleID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionaryWithInfoDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIROptions isMeasurementEnabled]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIROptions isAnalyticsCollectionExpicitlySet]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionEnabled]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionDeactivated]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsEnabled]", + "coverage": 0 + }, + { + "name": "-[FIROptions isSignInEnabled]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAnalyticsConfiguration.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "+[FIRAnalyticsConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__43+[FIRAnalyticsConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration postNotificationName:value:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAnalyticsConfiguration setMinimumSessionInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setSessionTimeoutInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:persistSetting:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinPreferences+Internal.m", + "coverage": 0.9692307692307692, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) preferencesFromKeychainContents:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) initWithDeviceID:secretToken:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) reset]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) updateWithCheckinPlistContents:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) checkinKeychainContent]", + "coverage": 0.75 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinDeviceIDFromKeychainContent:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinSecretFromKeychainContent:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinKeychainContent:forIndex:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentContainer.m", + "coverage": 0.9818181818181818, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:]", + "coverage": 1 + }, + { + "name": "__55+[FIRComponentContainer registerAsComponentRegistrant:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:inSet:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:registrants:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer populateComponentsFromRegisteredClasses:forApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer instantiateInstanceForProtocol:withBlock:]", + "coverage": 0.9285714285714286 + }, + { + "name": "-[FIRComponentContainer instanceForProtocol:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer removeAllCachedInstances]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPair.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDKeyPair initWithPrivateKey:publicKey:publicKeyData:privateKeyData:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair isValid]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair publicKey]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair privateKey]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDTokenStore.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDTokenStore defaultStore]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore initWithKeychain:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenStore serviceKeyForAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore tokenInfoWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore cachedTokenInfos]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenStore tokenInfoFromKeychainItem:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore saveTokenInfo:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore removeTokenWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore removeAllTokensWithHandler:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentType.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentType instanceForProtocol:inContainer:]", + "coverage": 1 + } + ] + }, + { + "name": "main.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "main", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponent.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponent componentWithProtocol:creationBlock:]", + "coverage": 1 + }, + { + "name": "+[FIRComponent componentWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRComponent initWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPairUtilities.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDWebSafeBase64", + "coverage": 1 + }, + { + "name": "FIRInstanceIDSHA1", + "coverage": 1 + }, + { + "name": "FIRInstanceIDKeyPairQuery", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAppIdentity", + "coverage": 1 + } + ] + }, + { + "name": "FIRAppAssociationRegistration.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRAppAssociationRegistration registeredObjectWithHost:key:creationBlock:]", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "Messaging_Example_iOS.app", + "coverage": 0.6270501835985313, + "files": [ + { + "name": "GtalkExtensions.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GtalkGtalkExtensionsRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GtalkRosterQuery descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkRosterItem descriptor]", + "coverage": 0 + }, + { + "name": "GtalkRosterItem_SubscriptionType_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkRosterItem_SubscriptionType_IsValidValue", + "coverage": 0 + }, + { + "name": "GtalkRosterItem_AskType_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkRosterItem_AskType_IsValidValue", + "coverage": 0 + }, + { + "name": "GtalkRosterItem_DisplayType_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkRosterItem_DisplayType_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GtalkRmqLastId descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkRmqAck descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkVCard descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkPhoto descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkChatRead descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkChatClosed descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkCapabilities descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkSharedStatus descriptor]", + "coverage": 0 + }, + { + "name": "GtalkSharedStatus_ShowType_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkSharedStatus_ShowType_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GtalkSharedStatus_StatusList descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkOtrQuery descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkOtrItem descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkIdle descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkPostAuthBatchQuery descriptor]", + "coverage": 0 + }, + { + "name": "GtalkPostAuthBatchQuery_CapabilitiesExtFlags_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkPostAuthBatchQuery_CapabilitiesExtFlags_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GtalkStreamAck descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkSelectiveAck descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessagingTopicOperation.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "FIRMessagingSubscriptionsServer", + "coverage": 0 + }, + { + "name": "__FIRMessagingSubscriptionsServer_block_invoke", + "coverage": 0 + }, + { + "name": "+[FIRMessagingTopicOperation sharedSession]", + "coverage": 0 + }, + { + "name": "__43+[FIRMessagingTopicOperation sharedSession]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation initWithTopic:action:token:options:checkinService:completion:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation dealloc]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation isAsynchronous]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation isExecuting]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation setExecuting:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation isFinished]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation setFinished:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation start]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation finishWithError:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation cancel]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicOperation performSubscriptionChange]", + "coverage": 0 + }, + { + "name": "__55-[FIRMessagingTopicOperation performSubscriptionChange]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDTokenDeleteOperation.m", + "coverage": 0.12643678160919541, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenDeleteOperation initWithAuthorizedEntity:scope:checkinPreferences:keyPair:action:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenDeleteOperation performTokenOperation]", + "coverage": 0 + }, + { + "name": "__58-[FIRInstanceIDTokenDeleteOperation performTokenOperation]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenDeleteOperation handleResponseWithData:response:error:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessagingReceiver.m", + "coverage": 0.2012987012987013, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingReceiver initWithUserDefaults:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingReceiver didReceiveMessage:withIdentifier:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingReceiver willSendDataMessageWithID:error:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingReceiver didSendDataMessageWithID:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingReceiver didDeleteMessagesOnServer]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingReceiver scheduleIos10NotificationForMessage:withIdentifier:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingReceiver scheduleNotificationForMessage:]", + "coverage": 0 + }, + { + "name": "__55-[FIRMessagingReceiver scheduleNotificationForMessage:]_block_invoke", + "coverage": 0 + }, + { + "name": "__55-[FIRMessagingReceiver scheduleNotificationForMessage:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "+[FIRMessagingReceiver nextMessageID]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingReceiver useDirectChannel]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingReceiver setUseDirectChannel:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAppDelegate.m", + "coverage": 0.2413793103448276, + "type": "objc", + "functions": [ + { + "name": "-[FIRAppDelegate application:didFinishLaunchingWithOptions:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillResignActive:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidEnterBackground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationWillEnterForeground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidBecomeActive:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillTerminate:]", + "coverage": 0 + } + ] + }, + { + "name": "NSDictionary+FIRMessaging.m", + "coverage": 0.2857142857142857, + "type": "objc", + "functions": [ + { + "name": "-[NSDictionary(FIRMessaging) fcm_string]", + "coverage": 0 + }, + { + "name": "-[NSDictionary(FIRMessaging) fcm_hasNonStringKeysOrValues]", + "coverage": 0.4444444444444444 + }, + { + "name": "-[NSDictionary(FIRMessaging) fcm_trimNonStringValues]", + "coverage": 0.5454545454545454 + } + ] + }, + { + "name": "FIRMessagingCheckinService.m", + "coverage": 0.3058823529411765, + "type": "objc", + "functions": [ + { + "name": "FIRMessagingCheckinService_hasValidCheckinInfo", + "coverage": 0 + }, + { + "name": "FIRMessagingCheckinService_propertyNamed", + "coverage": 0.5 + }, + { + "name": "-[FIRMessagingCheckinService tryToLoadPrefetchedCheckinPreferences]", + "coverage": 0.35714285714285715 + }, + { + "name": "-[FIRMessagingCheckinService deviceAuthID]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingCheckinService secretToken]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingCheckinService versionInfo]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingCheckinService digest]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingCheckinService hasValidCheckinInfo]", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceID+Private.m", + "coverage": 0.3333333333333333, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceID(Private) cachedCheckinPreferences]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID(Private) fetchCheckinInfoWithHandler:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID(Private) appInstanceID:]", + "coverage": 1 + } + ] + }, + { + "name": "NSError+FIRInstanceID.m", + "coverage": 0.375, + "type": "objc", + "functions": [ + { + "name": "-[NSError(FIRInstanceID) instanceIDErrorCode]", + "coverage": 0 + }, + { + "name": "+[NSError(FIRInstanceID) errorWithFIRInstanceIDErrorCode:]", + "coverage": 1 + }, + { + "name": "+[NSError(FIRInstanceID) errorWithFIRInstanceIDErrorCode:userInfo:]", + "coverage": 1 + }, + { + "name": "+[NSError(FIRInstanceID) FIRInstanceIDErrorMissingCheckin]", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessagingPersistentSyncMessage.m", + "coverage": 0.4, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingPersistentSyncMessage init]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPersistentSyncMessage initWithRMQID:expirationTime:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPersistentSyncMessage description]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPersistentSyncMessage debugDescription]", + "coverage": 0 + } + ] + }, + { + "name": "GPBUtilities_PackagePrivate.h", + "coverage": 0.4166666666666667, + "type": "objc", + "functions": [ + { + "name": "Definition at 54:52", + "coverage": 0 + }, + { + "name": "Definition at 65:47", + "coverage": 0 + }, + { + "name": "Definition at 73:54", + "coverage": 0 + }, + { + "name": "Definition at 79:52", + "coverage": 0 + }, + { + "name": "Definition at 85:54", + "coverage": 0 + }, + { + "name": "Definition at 91:52", + "coverage": 0 + }, + { + "name": "Definition at 97:74", + "coverage": 1 + }, + { + "name": "Definition at 101:74", + "coverage": 1 + }, + { + "name": "Definition at 109:50", + "coverage": 0 + }, + { + "name": "Definition at 117:50", + "coverage": 0 + }, + { + "name": "Definition at 125:50", + "coverage": 0 + }, + { + "name": "Definition at 134:50", + "coverage": 0 + }, + { + "name": "Definition at 143:55", + "coverage": 1 + }, + { + "name": "Definition at 155:56", + "coverage": 1 + }, + { + "name": "Definition at 165:70", + "coverage": 1 + }, + { + "name": "Definition at 169:69", + "coverage": 1 + }, + { + "name": "Definition at 173:68", + "coverage": 0 + }, + { + "name": "Definition at 178:65", + "coverage": 0 + }, + { + "name": "Definition at 192:65", + "coverage": 1 + }, + { + "name": "Definition at 197:48", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingPubSubRegistrar.m", + "coverage": 0.42424242424242425, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingPubSubRegistrar init]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSubRegistrar initWithCheckinService:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPubSubRegistrar stopAllSubscriptionRequests]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPubSubRegistrar updateSubscriptionToTopic:withToken:options:shouldDelete:handler:]", + "coverage": 0 + } + ] + }, + { + "name": "GtalkCore.pbobjc.m", + "coverage": 0.4363238512035011, + "type": "objc", + "functions": [ + { + "name": "GtalkGtalkCoreRoot_FileDescriptor", + "coverage": 1 + }, + { + "name": "+[GtalkHeartbeatPing descriptor]", + "coverage": 1 + }, + { + "name": "+[GtalkHeartbeatAck descriptor]", + "coverage": 1 + }, + { + "name": "+[GtalkErrorInfo descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkSetting descriptor]", + "coverage": 1 + }, + { + "name": "+[GtalkHeartbeatStat descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkHeartbeatConfig descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkLoginRequest descriptor]", + "coverage": 1 + }, + { + "name": "GtalkLoginRequest_AuthService_EnumDescriptor", + "coverage": 0.9130434782608695 + }, + { + "name": "GtalkLoginRequest_AuthService_IsValidValue", + "coverage": 0.9 + }, + { + "name": "+[GtalkLoginResponse descriptor]", + "coverage": 1 + }, + { + "name": "+[GtalkBindAccountRequest descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkBindAccountResponse descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkStreamErrorStanza descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkClose descriptor]", + "coverage": 1 + }, + { + "name": "+[GtalkExtension descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkMessageStanza descriptor]", + "coverage": 0 + }, + { + "name": "GtalkMessageStanza_MessageType_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkMessageStanza_MessageType_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GtalkPresenceStanza descriptor]", + "coverage": 0 + }, + { + "name": "GtalkPresenceStanza_PresenceType_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkPresenceStanza_PresenceType_IsValidValue", + "coverage": 0 + }, + { + "name": "GtalkPresenceStanza_ShowType_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkPresenceStanza_ShowType_IsValidValue", + "coverage": 0 + }, + { + "name": "GtalkPresenceStanza_ClientType_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkPresenceStanza_ClientType_IsValidValue", + "coverage": 0 + }, + { + "name": "GtalkPresenceStanza_CapabilitiesFlags_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkPresenceStanza_CapabilitiesFlags_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GtalkBatchPresenceStanza descriptor]", + "coverage": 0 + }, + { + "name": "GtalkBatchPresenceStanza_Type_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkBatchPresenceStanza_Type_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GtalkIqStanza descriptor]", + "coverage": 1 + }, + { + "name": "GtalkIqStanza_IqType_EnumDescriptor", + "coverage": 0.9130434782608695 + }, + { + "name": "GtalkIqStanza_IqType_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GtalkAppData descriptor]", + "coverage": 1 + }, + { + "name": "+[GtalkDataMessageStanza descriptor]", + "coverage": 1 + }, + { + "name": "+[GtalkTalkMetadata descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkCellTower descriptor]", + "coverage": 0 + }, + { + "name": "+[GtalkClientEvent descriptor]", + "coverage": 0 + }, + { + "name": "GtalkClientEvent_Type_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkClientEvent_Type_IsValidValue", + "coverage": 0 + }, + { + "name": "GtalkClientEvent_McsReconnectAction_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GtalkClientEvent_McsReconnectAction_IsValidValue", + "coverage": 0 + } + ] + }, + { + "name": "FIRViewController.m", + "coverage": 0.5, + "type": "objc", + "functions": [ + { + "name": "-[FIRViewController viewDidLoad]", + "coverage": 1 + }, + { + "name": "-[FIRViewController didReceiveMemoryWarning]", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessagingConnection.m", + "coverage": 0.5072727272727273, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingD2SInfo initWithStreamId:d2sId:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingD2SInfo isEqual:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingD2SInfo hash]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection initWithAuthID:token:host:port:runLoop:rmq2Manager:fcmManager:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection description]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection signIn]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection setupConnectionSocket]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection connectToSocket:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection signOut]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection teardown]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection secureSocketDidConnect:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection didDisconnectWithSecureSocket:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection secureSocket:didReceiveData:withTag:]", + "coverage": 0.7368421052631579 + }, + { + "name": "-[FIRMessagingConnection secureSocket:didSendProtoWithTag:rmqId:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection sendProto:]", + "coverage": 0.5714285714285714 + }, + { + "name": "-[FIRMessagingConnection sendOnConnectOrDrop:]", + "coverage": 0 + }, + { + "name": "+[FIRMessagingConnection loginRequestWithToken:authID:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingConnection currentNetworkType]", + "coverage": 0.7894736842105263 + }, + { + "name": "-[FIRMessagingConnection sendLoginRequest:token:]", + "coverage": 0.6296296296296297 + }, + { + "name": "-[FIRMessagingConnection sendHeartbeatAck]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection sendHeartbeatPing]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingConnection createStreamAck]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection sendStreamAck]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection sendClose]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection handleIqStanza:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection didReceiveLoginResponse:]", + "coverage": 0.7083333333333334 + }, + { + "name": "-[FIRMessagingConnection didReceiveHeartbeatPing:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection didReceiveHeartbeatAck:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection didReceiveDataMessageStanza:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection didReceiveUnhandledProto:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection didReceiveStreamAck:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection didReceiveSelectiveAck:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection didReceiveClose:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection willProcessProto:]", + "coverage": 0.5945945945945946 + }, + { + "name": "-[FIRMessagingConnection willSendProto:]", + "coverage": 0.625 + }, + { + "name": "-[FIRMessagingConnection confirmAckedD2sIdsWithStreamId:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection didReceiveAckForRmqIds:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection confirmAckedS2dIdsWithStreamId:]", + "coverage": 0 + }, + { + "name": "__57-[FIRMessagingConnection confirmAckedS2dIdsWithStreamId:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection resetUnconfirmedAcks]", + "coverage": 1 + }, + { + "name": "__46-[FIRMessagingConnection resetUnconfirmedAcks]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection disconnect]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection connectionTimedOut]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection scheduleConnectionTimeoutTask]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection cancelConnectionTimeoutTask]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingConnection logMessage:messageType:isOut:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingConnection connectionTimeoutInterval]", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessagingClient.m", + "coverage": 0.5215311004784688, + "type": "objc", + "functions": [ + { + "name": "FIRMessagingServerHost", + "coverage": 1 + }, + { + "name": "__FIRMessagingServerHost_block_invoke", + "coverage": 0.9 + }, + { + "name": "FIRMessagingServerPort", + "coverage": 1 + }, + { + "name": "__FIRMessagingServerPort_block_invoke", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FIRMessagingClient init]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient initWithDelegate:reachability:rmq2Manager:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient teardown]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient cancelAllRequests]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient updateSubscriptionWithToken:topic:options:shouldDelete:handler:]", + "coverage": 0 + }, + { + "name": "__85-[FIRMessagingClient updateSubscriptionWithToken:topic:options:shouldDelete:handler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient isConnected]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient isConnectionActive]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient shouldStayConnected]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient retryConnectionImmediately:]", + "coverage": 0 + }, + { + "name": "__49-[FIRMessagingClient retryConnectionImmediately:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient connectWithHandler:]", + "coverage": 0.46153846153846156 + }, + { + "name": "-[FIRMessagingClient connect]", + "coverage": 0.43478260869565216 + }, + { + "name": "-[FIRMessagingClient disconnect]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient disconnectWithTryToConnectLater:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient checkinFetched:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient sendMessage:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient sendOnConnectOrDrop:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient connection:didCloseForReason:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient didLoginWithConnection:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient connectionDidRecieveMessage:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient connectionDidReceiveAckForRmqIds:]", + "coverage": 0 + }, + { + "name": "__55-[FIRMessagingClient connectionDidReceiveAckForRmqIds:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingClient setupConnectionAndConnect]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient setupConnection]", + "coverage": 0.7894736842105263 + }, + { + "name": "-[FIRMessagingClient tryToConnect]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FIRMessagingClient didConnectTimeout]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient scheduleConnectRetry]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingClient nextRetryInterval]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDStringEncoding.m", + "coverage": 0.5308641975308642, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDStringEncoding rfc4648Base64WebsafeStringEncoding]", + "coverage": 1 + }, + { + "name": "lcm", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStringEncoding stringEncodingWithString:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding initWithString:]", + "coverage": 0.6896551724137931 + }, + { + "name": "-[FIRInstanceIDStringEncoding description]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDStringEncoding doPad]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDStringEncoding setDoPad:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding setPaddingChar:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStringEncoding encode:]", + "coverage": 0.8444444444444444 + }, + { + "name": "-[FIRInstanceIDStringEncoding decode:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessaging.m", + "coverage": 0.5459032576505429, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingMessageInfo init]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingMessageInfo initWithStatus:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRemoteMessage init]", + "coverage": 0 + }, + { + "name": "+[FIRMessaging messaging]", + "coverage": 1 + }, + { + "name": "__25+[FIRMessaging messaging]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRMessaging extensionHelper]", + "coverage": 0 + }, + { + "name": "__31+[FIRMessaging extensionHelper]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessaging initWithAnalytics:withInstanceID:withUserDefaults:]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging dealloc]", + "coverage": 1 + }, + { + "name": "+[FIRMessaging load]", + "coverage": 1 + }, + { + "name": "+[FIRMessaging componentsToRegister]", + "coverage": 0 + }, + { + "name": "__36+[FIRMessaging componentsToRegister]_block_invoke", + "coverage": 0 + }, + { + "name": "+[FIRMessaging configureWithApp:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging configureMessaging:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging start]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setupFileManagerSubDirectory]", + "coverage": 0.6 + }, + { + "name": "-[FIRMessaging setupNotificationListeners]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setupReceiver]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setupClient]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setupDataMessageManager]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setupRmqManager]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setupTopics]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setupSyncMessageManager]", + "coverage": 1 + }, + { + "name": "__39-[FIRMessaging setupSyncMessageManager]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRMessaging teardown]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging appDidReceiveMessage:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging handleContextManagerMessage:]", + "coverage": 0 + }, + { + "name": "+[FIRMessaging isAPNSSyncMessage:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging handleIncomingLinkIfNeededFromMessage:]", + "coverage": 0 + }, + { + "name": "__54-[FIRMessaging handleIncomingLinkIfNeededFromMessage:]_block_invoke", + "coverage": 0 + }, + { + "name": "__54-[FIRMessaging handleIncomingLinkIfNeededFromMessage:]_block_invoke.322", + "coverage": 0 + }, + { + "name": "-[FIRMessaging linkURLFromMessage:]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging APNSToken]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setAPNSToken:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging setAPNSToken:type:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging isAutoInitEnabled]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging setAutoInitEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging FCMToken]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging retrieveFCMTokenForSenderID:completion:]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging deleteFCMTokenForSenderID:completion:]", + "coverage": 0.7647058823529411 + }, + { + "name": "-[FIRMessaging setDelegate:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging validateDelegateConformsToTokenAvailabilityMethods]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging notifyDelegateOfFCMTokenAvailability]", + "coverage": 0 + }, + { + "name": "__52-[FIRMessaging notifyDelegateOfFCMTokenAvailability]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessaging setUseMessagingDelegateForDirectChannel:]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging useMessagingDelegateForDirectChannel]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging applicationStateChanged]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging setShouldEstablishDirectChannel:]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging isDirectChannelEstablished]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging shouldBeConnectedAutomatically]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRMessaging updateAutomaticClientConnection]", + "coverage": 0.2857142857142857 + }, + { + "name": "__47-[FIRMessaging updateAutomaticClientConnection]_block_invoke", + "coverage": 0 + }, + { + "name": "__47-[FIRMessaging updateAutomaticClientConnection]_block_invoke.428", + "coverage": 0 + }, + { + "name": "-[FIRMessaging notifyOfDirectChannelConnectionChange]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging connectWithCompletion:]", + "coverage": 0 + }, + { + "name": "__38-[FIRMessaging connectWithCompletion:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessaging disconnect]", + "coverage": 0 + }, + { + "name": "+[FIRMessaging normalizeTopic:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRMessaging subscribeToTopic:]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging subscribeToTopic:completion:]", + "coverage": 0.8260869565217391 + }, + { + "name": "-[FIRMessaging unsubscribeFromTopic:]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging unsubscribeFromTopic:completion:]", + "coverage": 0.8260869565217391 + }, + { + "name": "-[FIRMessaging sendMessage:to:withMessageID:timeToLive:]", + "coverage": 0 + }, + { + "name": "+[FIRMessaging createFIRMessagingMessageWithMessage:to:withID:timeToLive:delay:]", + "coverage": 1 + }, + { + "name": "+[FIRMessaging FIRMessagingSDKVersion]", + "coverage": 1 + }, + { + "name": "+[FIRMessaging FIRMessagingSDKCurrentLocale]", + "coverage": 1 + }, + { + "name": "-[FIRMessaging receiver:receivedRemoteMessage:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging reachability:statusChanged:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging onNetworkStatusChanged]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging isNetworkAvailable]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging networkType]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging didReceiveDefaultInstanceIDToken:]", + "coverage": 0 + }, + { + "name": "-[FIRMessaging defaultInstanceIDTokenWasRefreshed:]", + "coverage": 0 + }, + { + "name": "+[FIRMessaging hasSubDirectory:]", + "coverage": 0.7272727272727273 + }, + { + "name": "+[FIRMessaging pathForSubDirectory:]", + "coverage": 1 + }, + { + "name": "+[FIRMessaging createSubDirectory:]", + "coverage": 0 + }, + { + "name": "+[FIRMessaging currentLocale]", + "coverage": 1 + }, + { + "name": "+[FIRMessaging firebaseLocales]", + "coverage": 1 + }, + { + "name": "+[FIRMessaging firebaselocalesMap]", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingRemoteNotificationsProxy.m", + "coverage": 0.6026490066225165, + "type": "objc", + "functions": [ + { + "name": "+[FIRMessagingRemoteNotificationsProxy canSwizzleMethods]", + "coverage": 0 + }, + { + "name": "+[FIRMessagingRemoteNotificationsProxy swizzleMethods]", + "coverage": 0 + }, + { + "name": "+[FIRMessagingRemoteNotificationsProxy sharedProxy]", + "coverage": 0 + }, + { + "name": "__51+[FIRMessagingRemoteNotificationsProxy sharedProxy]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy init]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy swizzleMethodsIfPossible]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy unswizzleAllMethods]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy swizzleAppDelegateMethods:]", + "coverage": 0.9076923076923077 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy listenForDelegateChangesInUserNotificationCenter:]", + "coverage": 0.35714285714285715 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy swizzleUserNotificationCenterDelegate:]", + "coverage": 0.9117647058823529 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy unswizzleUserNotificationCenterDelegate:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy addDelegateObserverToUserNotificationCenter:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy removeUserNotificationCenterDelegateObserver]", + "coverage": 0.21052631578947367 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy observeValueForKeyPath:ofObject:change:context:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy saveOriginalImplementation:forSelector:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy originalImplementationForSelector:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy trackSwizzledSelector:ofClass:]", + "coverage": 0.9090909090909091 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy removeImplementationForSelector:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy swizzleSelector:inClass:withImplementation:inProtocol:]", + "coverage": 0.8648648648648649 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy unswizzleSelector:inClass:]", + "coverage": 0.88 + }, + { + "name": "-[FIRMessagingRemoteNotificationsProxy nonExistantMethodImplementationForClass:]", + "coverage": 1 + }, + { + "name": "getNamedPropertyFromObject", + "coverage": 0.7142857142857143 + }, + { + "name": "FCM_swizzle_appDidReceiveRemoteNotification", + "coverage": 1 + }, + { + "name": "FCM_swizzle_appDidReceiveRemoteNotificationWithHandler", + "coverage": 1 + }, + { + "name": "FCM_swizzle_willPresentNotificationWithHandler", + "coverage": 0.5925925925925926 + }, + { + "name": "__FCM_swizzle_willPresentNotificationWithHandler_block_invoke", + "coverage": 1 + }, + { + "name": "FCM_swizzle_didReceiveNotificationResponseWithHandler", + "coverage": 0.6101694915254238 + }, + { + "name": "__FCM_swizzle_didReceiveNotificationResponseWithHandler_block_invoke", + "coverage": 1 + }, + { + "name": "userInfoFromNotification", + "coverage": 0.6923076923076923 + }, + { + "name": "FCM_swizzle_messagingDidReceiveMessage", + "coverage": 0 + }, + { + "name": "FCM_swizzle_appDidFailToRegisterForRemoteNotifications", + "coverage": 0 + }, + { + "name": "FCM_swizzle_appDidRegisterForRemoteNotifications", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessagingPacketQueue.m", + "coverage": 0.6078431372549019, + "type": "objc", + "functions": [ + { + "name": "+[FIRMessagingPacket packetWithTag:rmqId:data:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPacket init]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPacket initWithTag:rmqId:data:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPacket description]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPacketQueue init]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPacketQueue isEmpty]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPacketQueue count]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPacketQueue push:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPacketQueue pushHead:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPacketQueue pop]", + "coverage": 0.75 + } + ] + }, + { + "name": "FIRMessagingPubSub.m", + "coverage": 0.625, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingPubSub init]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub initWithClient:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPubSub subscribeWithToken:topic:options:handler:]", + "coverage": 0.7435897435897436 + }, + { + "name": "__63-[FIRMessagingPubSub subscribeWithToken:topic:options:handler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub unsubscribeWithToken:topic:options:handler:]", + "coverage": 0.6578947368421053 + }, + { + "name": "__65-[FIRMessagingPubSub unsubscribeWithToken:topic:options:handler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub subscribeToTopic:handler:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub unsubscribeFromTopic:handler:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub scheduleSync:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub pendingTopicsList:requestedUpdateForTopic:action:completion:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub pendingTopicsListDidUpdate:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub pendingTopicsListCanRequestTopicUpdates:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub archivePendingTopicsList:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPubSub restorePendingTopicsList]", + "coverage": 0.8421052631578947 + }, + { + "name": "-[FIRMessagingPubSub verifyPubSubOptions:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingPubSub addPrefixToTopic:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingPubSub removePrefixFromTopic:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingPubSub hasTopicsPrefix:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingPubSub topicRegex]", + "coverage": 1 + }, + { + "name": "__32+[FIRMessagingPubSub topicRegex]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRMessagingPubSub isValidTopicWithPrefix:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingCodedInputStream.m", + "coverage": 0.6363636363636364, + "type": "objc", + "functions": [ + { + "name": "CheckSize", + "coverage": 1 + }, + { + "name": "ReadRawByte", + "coverage": 1 + }, + { + "name": "ReadRawVarInt32", + "coverage": 0.35714285714285715 + }, + { + "name": "-[FIRMessagingCodedInputStream initWithData:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingCodedInputStream offset]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingCodedInputStream readTag:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingCodedInputStream readLength:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingCodedInputStream readDataWithLength:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingSecureSocket.m", + "coverage": 0.6518105849582173, + "type": "objc", + "functions": [ + { + "name": "LogicalRightShift32", + "coverage": 0 + }, + { + "name": "SerializedSize", + "coverage": 0.75 + }, + { + "name": "-[FIRMessagingSecureSocket init]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingSecureSocket dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingSecureSocket connectToHost:port:onRunLoop:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingSecureSocket disconnect]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingSecureSocket sendData:withTag:rmqId:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingSecureSocket stream:handleEvent:]", + "coverage": 0.7241379310344828 + }, + { + "name": "-[FIRMessagingSecureSocket openStream:isVOIPStream:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingSecureSocket closeStream:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingSecureSocket performRead]", + "coverage": 0.6826923076923077 + }, + { + "name": "-[FIRMessagingSecureSocket processCurrentInputBuffer:outOffset:]", + "coverage": 0.6551724137931034 + }, + { + "name": "-[FIRMessagingSecureSocket performWrite]", + "coverage": 0.9523809523809523 + } + ] + }, + { + "name": "FIRMessagingRmq2PersistentStore.m", + "coverage": 0.6566455696202531, + "type": "objc", + "functions": [ + { + "name": "FIRMessagingStringFromSQLiteResult", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore initWithDatabaseName:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore moveToApplicationSupportSubDirectory:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore doesFileExistInDirectory:]", + "coverage": 0 + }, + { + "name": "+[FIRMessagingRmq2PersistentStore pathForDatabase:inDirectory:]", + "coverage": 0.8 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore createTableWithName:command:]", + "coverage": 0.3333333333333333 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore dropTableWithName:]", + "coverage": 0.625 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore removeDatabase]", + "coverage": 0 + }, + { + "name": "+[FIRMessagingRmq2PersistentStore removeDatabase:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore openDatabase:]", + "coverage": 0.5625 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore updateDbWithStringRmqID]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore saveUnackedS2dMessageWithRmqId:]", + "coverage": 0.7391304347826086 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore saveMessageWithRmqId:tag:data:error:]", + "coverage": 0.5161290322580645 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore deleteMessagesFromTable:withRmqIds:]", + "coverage": 0.8522727272727273 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore queryHighestRmqId]", + "coverage": 0.7894736842105263 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore queryLastRmqId]", + "coverage": 0.7894736842105263 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore updateLastOutgoingRmqId:]", + "coverage": 0.6190476190476191 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore unackedS2dRmqIds]", + "coverage": 0.8 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore scanOutgoingRmqMessagesWithHandler:]", + "coverage": 0.8666666666666667 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore querySyncMessageWithRmqID:]", + "coverage": 0.9111111111111111 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore deleteSyncMessageWithRmqID:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore deleteExpiredOrFinishedSyncMessages:]", + "coverage": 0.6571428571428571 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore saveSyncMessageWithRmqID:expirationTime:apnsReceived:mcsReceived:error:]", + "coverage": 0.6363636363636364 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore updateSyncMessageViaAPNSWithRmqID:error:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore updateSyncMessageViaMCSWithRmqID:error:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore updateSyncMessageWithRmqID:column:value:error:]", + "coverage": 0.7058823529411765 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore lastErrorMessage]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore lastErrorCode]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore logError]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingRmq2PersistentStore logErrorAndFinalizeStatement:]", + "coverage": 0 + } + ] + }, + { + "name": "NSError+FIRMessaging.m", + "coverage": 0.6666666666666666, + "type": "objc", + "functions": [ + { + "name": "-[NSError(FIRMessaging) fcmErrorCode]", + "coverage": 0 + }, + { + "name": "+[NSError(FIRMessaging) errorWithFCMErrorCode:]", + "coverage": 1 + }, + { + "name": "+[NSError(FIRMessaging) fcm_errorWithCode:userInfo:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDTokenManager.m", + "coverage": 0.7067039106145251, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenManager init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager configureTokenOperations]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke.52", + "coverage": 1 + }, + { + "name": "__93-[FIRInstanceIDTokenManager fetchNewTokenWithAuthorizedEntity:scope:keyPair:options:handler:]_block_invoke_2.53", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager cachedTokenInfoWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]", + "coverage": 0.9166666666666666 + }, + { + "name": "__83-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__83-[FIRInstanceIDTokenManager deleteTokenWithAuthorizedEntity:scope:keyPair:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke.86", + "coverage": 0 + }, + { + "name": "__64-[FIRInstanceIDTokenManager deleteAllTokensWithKeyPair:handler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager deleteAllTokensLocallyWithHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager stopAllTokenOperations]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenManager store:didDeleteFCMScopedTokensForCheckin:]", + "coverage": 0 + }, + { + "name": "__70-[FIRInstanceIDTokenManager store:didDeleteFCMScopedTokensForCheckin:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager createFetchOperationWithAuthorizedEntity:scope:options:keyPair:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager createDeleteOperationWithAuthorizedEntity:scope:checkinPreferences:keyPair:action:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenManager checkForTokenRefreshPolicy]", + "coverage": 0.8125 + }, + { + "name": "-[FIRInstanceIDTokenManager updateTokensToAPNSDeviceToken:isSandbox:]", + "coverage": 0.9333333333333333 + } + ] + }, + { + "name": "FIRInstanceIDLogger.m", + "coverage": 0.72, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDLogger formatMessageCode:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncDebug:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncInfo:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDLogger logFuncNotice:messageCode:msg:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDLogger logFuncWarning:messageCode:msg:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDLogger logFuncError:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "FIRInstanceIDSharedLogger", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDSharedLogger_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingDelayedMessageQueue.m", + "coverage": 0.723404255319149, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingDelayedMessageQueue init]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue initWithRmqScanner:sendDelayedMessagesHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue queueMessage:]", + "coverage": 0.75 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue removeDelayedMessages]", + "coverage": 0.5862068965517241 + }, + { + "name": "__56-[FIRMessagingDelayedMessageQueue removeDelayedMessages]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue sendMessages]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue messageCount]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue isTimeoutScheduled]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue calculateTimeoutInMillisWithDelayInSeconds:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue scheduleTimeoutInMillis:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDelayedMessageQueue cancelTimeout]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceID.m", + "coverage": 0.7266414141414141, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDResult copyWithZone:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID instanceID]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIRInstanceID initPrivately]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID instanceIDForTests]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID token]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID instanceIDWithHandler:]", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceID instanceIDWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceID instanceIDWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID cachedTokenIfAvailable]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setDefaultFCMToken:]", + "coverage": 0.8666666666666667 + }, + { + "name": "-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]", + "coverage": 0.8584905660377359 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke.163", + "coverage": 0.9302325581395349 + }, + { + "name": "__65-[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler:]_block_invoke_2.171", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]", + "coverage": 0.84375 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke", + "coverage": 0.8 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke.195", + "coverage": 0.8695652173913043 + }, + { + "name": "__63-[FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler:]_block_invoke_2.196", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID asyncLoadKeyPairWithHandler:]", + "coverage": 1 + }, + { + "name": "__45-[FIRInstanceID asyncLoadKeyPairWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__45-[FIRInstanceID asyncLoadKeyPairWithHandler:]_block_invoke_2", + "coverage": 0.7222222222222222 + }, + { + "name": "-[FIRInstanceID getIDWithHandler:]", + "coverage": 0.7837837837837838 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID getIDWithHandler:]_block_invoke.223", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteIDWithHandler:]", + "coverage": 0.8333333333333334 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke", + "coverage": 0.5555555555555556 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke.240", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke_2.241", + "coverage": 0 + }, + { + "name": "__37-[FIRInstanceID deleteIDWithHandler:]_block_invoke.250", + "coverage": 0.8 + }, + { + "name": "-[FIRInstanceID notifyIdentityReset]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID deleteIdentityWithHandler:]", + "coverage": 1 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke", + "coverage": 0.9137931034482759 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "__43-[FIRInstanceID deleteIdentityWithHandler:]_block_invoke_4", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID load]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID componentsToRegister]", + "coverage": 1 + }, + { + "name": "__37+[FIRInstanceID componentsToRegister]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID configureWithApp:]", + "coverage": 0.4444444444444444 + }, + { + "name": "-[FIRInstanceID configureInstanceIDWithOptions:app:]", + "coverage": 0.7222222222222222 + }, + { + "name": "+[FIRInstanceID configureErrorWithReason:]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID exitWithReason:forFirebaseApp:]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID didCompleteConfigure]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID isFCMAutoInitEnabled]", + "coverage": 0.8709677419354839 + }, + { + "name": "-[FIRInstanceID start]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FIRInstanceID setupTokenManager]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setupKeyPairManager]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID setupNotificationListeners]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID maxRetryCountForDefaultToken]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceID minIntervalForDefaultTokenRetry]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceID maxRetryIntervalForDefaultTokenInSeconds]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID retryIntervalToFetchDefaultToken]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIRInstanceID fetchDefaultToken]", + "coverage": 1 + }, + { + "name": "__34-[FIRInstanceID fetchDefaultToken]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID defaultTokenWithHandler:]", + "coverage": 0.89 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke", + "coverage": 0.8133333333333334 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__41-[FIRInstanceID defaultTokenWithHandler:]_block_invoke.406", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID notifyAPNSTokenIsSet:]", + "coverage": 0 + }, + { + "name": "__38-[FIRInstanceID notifyAPNSTokenIsSet:]_block_invoke", + "coverage": 0 + }, + { + "name": "__38-[FIRInstanceID notifyAPNSTokenIsSet:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "-[FIRInstanceID isSandboxApp]", + "coverage": 1 + }, + { + "name": "__29-[FIRInstanceID isSandboxApp]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceID isProductionApp]", + "coverage": 0.22556390977443608 + }, + { + "name": "-[FIRInstanceID logAPNSConfigurationError:]", + "coverage": 0.875 + } + ] + }, + { + "name": "FIRMessagingPendingTopicsList.m", + "coverage": 0.7335766423357665, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingTopicBatch initWithAction:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingTopicBatch encodeWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingTopicBatch initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPendingTopicsList init]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingPendingTopicsList pruneTopicBatches:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPendingTopicsList encodeWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPendingTopicsList initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingPendingTopicsList numberOfBatches]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPendingTopicsList addOperationForTopic:withAction:completion:]", + "coverage": 0.8205128205128205 + }, + { + "name": "__76-[FIRMessagingPendingTopicsList addOperationForTopic:withAction:completion:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPendingTopicsList resumeOperationsIfNeeded]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FIRMessagingPendingTopicsList subscriptionErrorIsRecoverable:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingPendingTopicsList beginUpdateForCurrentBatchTopic:]", + "coverage": 1 + }, + { + "name": "__65-[FIRMessagingPendingTopicsList beginUpdateForCurrentBatchTopic:]_block_invoke", + "coverage": 1 + }, + { + "name": "__65-[FIRMessagingPendingTopicsList beginUpdateForCurrentBatchTopic:]_block_invoke_2", + "coverage": 0.8055555555555556 + }, + { + "name": "__65-[FIRMessagingPendingTopicsList beginUpdateForCurrentBatchTopic:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "__65-[FIRMessagingPendingTopicsList beginUpdateForCurrentBatchTopic:]_block_invoke.130", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingContextManagerService.m", + "coverage": 0.7361111111111112, + "type": "objc", + "functions": [ + { + "name": "+[FIRMessagingContextManagerService isContextManagerMessage:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingContextManagerService handleContextManagerMessage:]", + "coverage": 0.7272727272727273 + }, + { + "name": "+[FIRMessagingContextManagerService handleContextManagerLocalTimeMessage:]", + "coverage": 0.7307692307692307 + }, + { + "name": "+[FIRMessagingContextManagerService scheduleLocalNotificationForMessage:atDate:]", + "coverage": 0.6428571428571429 + }, + { + "name": "+[FIRMessagingContextManagerService parseDataFromMessage:]", + "coverage": 0.9333333333333333 + } + ] + }, + { + "name": "FIRInstanceIDTokenFetchOperation.m", + "coverage": 0.75, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenFetchOperation initWithAuthorizedEntity:scope:options:checkinPreferences:keyPair:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation performTokenOperation]", + "coverage": 0.9206349206349206 + }, + { + "name": "__57-[FIRInstanceIDTokenFetchOperation performTokenOperation]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation handleResponseWithData:response:error:]", + "coverage": 0.5454545454545454 + }, + { + "name": "__74-[FIRInstanceIDTokenFetchOperation handleResponseWithData:response:error:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenFetchOperation parseFetchTokenResponse:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingUtilities.m", + "coverage": 0.7535211267605634, + "type": "objc", + "functions": [ + { + "name": "FIRMessagingGetTagForProto", + "coverage": 0.7777777777777778 + }, + { + "name": "FIRMessagingGetClassForTag", + "coverage": 0.8571428571428571 + }, + { + "name": "FIRMessagingGetRmq2Id", + "coverage": 0.75 + }, + { + "name": "FIRMessagingSetRmq2Id", + "coverage": 0.8571428571428571 + }, + { + "name": "FIRMessagingGetLastStreamId", + "coverage": 0.5 + }, + { + "name": "FIRMessagingSetLastStreamId", + "coverage": 0.9090909090909091 + }, + { + "name": "FIRMessagingCurrentTimestampInSeconds", + "coverage": 1 + }, + { + "name": "FIRMessagingCurrentTimestampInMilliseconds", + "coverage": 1 + }, + { + "name": "FIRMessagingCurrentAppVersion", + "coverage": 0 + }, + { + "name": "FIRMessagingAppIdentifier", + "coverage": 1 + }, + { + "name": "FIRMessagingGetFreeDiskSpaceInMB", + "coverage": 0.8 + }, + { + "name": "FIRMessagingUIApplication", + "coverage": 1 + }, + { + "name": "FIRMessagingSupportedDirectory", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingVersionUtilities.m", + "coverage": 0.7567567567567568, + "type": "objc", + "functions": [ + { + "name": "FIRMessagingParseCurrentLibraryVersion", + "coverage": 1 + }, + { + "name": "__FIRMessagingParseCurrentLibraryVersion_block_invoke", + "coverage": 0.92 + }, + { + "name": "FIRMessagingCurrentLibraryVersion", + "coverage": 1 + }, + { + "name": "FIRMessagingCurrentLibraryVersionMajor", + "coverage": 0 + }, + { + "name": "FIRMessagingCurrentLibraryVersionMinor", + "coverage": 0 + }, + { + "name": "FIRMessagingCurrentLibraryVersionPatch", + "coverage": 0 + }, + { + "name": "FIRMessagingCurrentLibraryVersionIsBeta", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessagingSyncMessageManager.m", + "coverage": 0.77, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingSyncMessageManager init]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingSyncMessageManager initWithRmqManager:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingSyncMessageManager removeExpiredSyncMessages]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIRMessagingSyncMessageManager didReceiveAPNSSyncMessage:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingSyncMessageManager didReceiveMCSSyncMessage:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingSyncMessageManager didReceiveSyncMessage:viaAPNS:viaMCS:]", + "coverage": 0.71875 + }, + { + "name": "+[FIRMessagingSyncMessageManager expirationTimeForSyncMessage:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDVersionUtilities.m", + "coverage": 0.775, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDParseCurrentLibraryVersion", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDParseCurrentLibraryVersion_block_invoke", + "coverage": 0.9285714285714286 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionMajor", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionMinor", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionPatch", + "coverage": 0 + }, + { + "name": "FIRInstanceIDCurrentLibraryVersionIsBeta", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDURLQueryItem.m", + "coverage": 0.78125, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDURLQueryItem queryItemWithName:value:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDURLQueryItem initWithName:value:]", + "coverage": 1 + }, + { + "name": "FIRInstanceIDQueryFromQueryItems", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FIRInstanceIDBackupExcludedPlist.m", + "coverage": 0.7816901408450704, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDBackupExcludedPlist initWithFileName:subDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist writeDictionary:error:]", + "coverage": 0.696969696969697 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist deleteFile:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist contentAsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist moveToApplicationSupportSubDirectory:]", + "coverage": 0.5428571428571428 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist doesFileExist]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist plistDirectory]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist plistPathInDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist pathWithName:inDirectory:]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist doesFileExistInDirectory:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDBackupExcludedPlist supportedDirectory]", + "coverage": 1 + } + ] + }, + { + "name": "FIRDependency.m", + "coverage": 0.7857142857142857, + "type": "objc", + "functions": [ + { + "name": "+[FIRDependency dependencyWithProtocol:]", + "coverage": 0 + }, + { + "name": "+[FIRDependency dependencyWithProtocol:isRequired:]", + "coverage": 1 + }, + { + "name": "-[FIRDependency initWithProtocol:isRequired:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRBundleUtil.m", + "coverage": 0.7872340425531915, + "type": "objc", + "functions": [ + { + "name": "+[FIRBundleUtil relevantBundles]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil optionsDictionaryPathWithResourceName:andFileType:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil relevantURLSchemes]", + "coverage": 0 + }, + { + "name": "+[FIRBundleUtil hasBundleIdentifierPrefix:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil bundleIdentifierByRemovingLastPartFrom:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinStore.m", + "coverage": 0.7874396135265701, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinStore initWithCheckinPlistFileName:subDirectoryName:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore initWithCheckinPlist:keychain:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore hasCheckinPlist]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore bundleIdentifierForKeychainAccount]", + "coverage": 1 + }, + { + "name": "__63-[FIRInstanceIDCheckinStore bundleIdentifierForKeychainAccount]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore saveCheckinPreferences:handler:]", + "coverage": 0.5573770491803278 + }, + { + "name": "__60-[FIRInstanceIDCheckinStore saveCheckinPreferences:handler:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinStore removeCheckinPreferencesWithHandler:]", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceIDCheckinStore removeCheckinPreferencesWithHandler:]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRInstanceIDCheckinStore cachedCheckinPreferences]", + "coverage": 0.7073170731707317 + }, + { + "name": "-[FIRInstanceIDCheckinStore migrateCheckinItemIfNeeded]", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingExtensionHelper.m", + "coverage": 0.8053097345132744, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingExtensionHelper populateNotificationContent:withContentHandler:]", + "coverage": 0.8636363636363636 + }, + { + "name": "__78-[FIRMessagingExtensionHelper populateNotificationContent:withContentHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRMessagingExtensionHelper loadAttachmentForURL:completionHandler:]", + "coverage": 1 + }, + { + "name": "__70-[FIRMessagingExtensionHelper loadAttachmentForURL:completionHandler:]_block_invoke", + "coverage": 0.4864864864864865 + }, + { + "name": "-[FIRMessagingExtensionHelper deliverNotification]", + "coverage": 1 + } + ] + }, + { + "name": "FIRConfiguration.m", + "coverage": 0.8076923076923077, + "type": "objc", + "functions": [ + { + "name": "+[FIRConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__34+[FIRConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration init]", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration setLoggerLevel:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRMessagingDataMessageManager.m", + "coverage": 0.8086124401913876, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingDataMessageManager initWithDelegate:client:rmq2Manager:syncMessageManager:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDataMessageManager setDeviceAuthID:secretToken:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDataMessageManager refreshDelayedMessages]", + "coverage": 1 + }, + { + "name": "__56-[FIRMessagingDataMessageManager refreshDelayedMessages]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDataMessageManager processPacket:]", + "coverage": 0.6428571428571429 + }, + { + "name": "-[FIRMessagingDataMessageManager handleMCSDataMessage:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingDataMessageManager parseDataMessage:]", + "coverage": 0.746268656716418 + }, + { + "name": "-[FIRMessagingDataMessageManager didReceiveParsedMessage:]", + "coverage": 0.5909090909090909 + }, + { + "name": "-[FIRMessagingDataMessageManager filterInternalFIRMessagingKeysFromMessage:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDataMessageManager sendDataMessageStanza:]", + "coverage": 0.91 + }, + { + "name": "-[FIRMessagingDataMessageManager sendDelayedMessages:]", + "coverage": 0.375 + }, + { + "name": "-[FIRMessagingDataMessageManager didSendDataMessageStanza:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingDataMessageManager addParamWithKey:value:toStanza:]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[FIRMessagingDataMessageManager addData:toStanza:]", + "coverage": 0.5 + }, + { + "name": "-[FIRMessagingDataMessageManager willSendDataMessageSuccess:withMessageId:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDataMessageManager willSendDataMessageFail:withMessageId:error:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDataMessageManager resendMessagesWithConnection:]", + "coverage": 1 + }, + { + "name": "__63-[FIRMessagingDataMessageManager resendMessagesWithConnection:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDataMessageManager handleExpirationForDataMessage:]", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRMessagingDataMessageManager delayForMessage:]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[FIRMessagingDataMessageManager delayMessage:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingDataMessageManager tryToSendDataMessageStanza:]", + "coverage": 0.625 + }, + { + "name": "-[FIRMessagingDataMessageManager categoryForUpstreamMessages]", + "coverage": 1 + } + ] + }, + { + "name": "FIRLogger.m", + "coverage": 0.8118811881188119, + "type": "objc", + "functions": [ + { + "name": "FIRLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__FIRLoggerInitializeASL_block_invoke", + "coverage": 0.9333333333333333 + }, + { + "name": "FIRSetAnalyticsDebugMode", + "coverage": 0 + }, + { + "name": "FIRSetLoggerLevel", + "coverage": 0 + }, + { + "name": "FIRResetLogger", + "coverage": 1 + }, + { + "name": "FIRSetLoggerUserDefaults", + "coverage": 1 + }, + { + "name": "FIRIsLoggableLevel", + "coverage": 0 + }, + { + "name": "FIRLogBasic", + "coverage": 1 + }, + { + "name": "Definition at 158:95", + "coverage": 1 + }, + { + "name": "+[FIRLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRInstanceIDTokenOperation.m", + "coverage": 0.8323353293413174, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDTokenOperation sharedURLSession]", + "coverage": 0 + }, + { + "name": "__47+[FIRInstanceIDTokenOperation sharedURLSession]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenOperation initWithAction:forAuthorizedEntity:scope:options:checkinPreferences:keyPair:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation addCompletionHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation isAsynchronous]", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDTokenOperation isExecuting]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation setExecuting:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation isFinished]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation setFinished:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation start]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRInstanceIDTokenOperation finishWithResult:token:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation cancel]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation performTokenOperation]", + "coverage": 0 + }, + { + "name": "+[FIRInstanceIDTokenOperation requestWithAuthHeader:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenOperation standardQueryItemsWithDeviceID:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenOperation queryItemsWithKeyPair:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenOperation HTTPAuthHeaderFromCheckin:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPairStore.m", + "coverage": 0.8351449275362319, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDKeyDataWithTag", + "coverage": 0.8333333333333334 + }, + { + "name": "FIRInstanceIDCachedKeyRefWithTag", + "coverage": 0.7777777777777778 + }, + { + "name": "FIRInstanceIDHasMigratedKeyPair", + "coverage": 1 + }, + { + "name": "FIRInstanceIDLegacyPublicTagWithSubtype", + "coverage": 1 + }, + { + "name": "FIRInstanceIDLegacyPrivateTagWithSubtype", + "coverage": 1 + }, + { + "name": "FIRInstanceIDPublicTagWithSubtype", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDPublicTagWithSubtype_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDPrivateTagWithSubtype", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDPrivateTagWithSubtype_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCreationTimeKeyWithSubtype", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore invalidateKeyPairsIfNeeded]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore hasCachedKeyPairs]", + "coverage": 0.29411764705882354 + }, + { + "name": "-[FIRInstanceIDKeyPairStore appIdentityWithError:]", + "coverage": 0.6 + }, + { + "name": "-[FIRInstanceIDKeyPairStore loadKeyPairWithError:]", + "coverage": 0.78125 + }, + { + "name": "-[FIRInstanceIDKeyPairStore generateAndSaveKeyWithSubtype:creationTime:error:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRInstanceIDKeyPairStore validCachedKeyPairWithSubtype:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore cachedKeyPairWithSubtype:error:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDKeyPairStore keyPairForPrivateKeyTag:publicKeyTag:error:]", + "coverage": 0.8387096774193549 + }, + { + "name": "-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]", + "coverage": 0.8771929824561403 + }, + { + "name": "__68-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]_block_invoke", + "coverage": 0.8636363636363636 + }, + { + "name": "__68-[FIRInstanceIDKeyPairStore migrateKeyPairCacheIfNeededWithHandler:]_block_invoke_2", + "coverage": 0.6428571428571429 + }, + { + "name": "-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]", + "coverage": 1 + }, + { + "name": "__58-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]_block_invoke", + "coverage": 0.7619047619047619 + }, + { + "name": "__58-[FIRInstanceIDKeyPairStore updateKeyRef:withTag:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPairStore deleteSavedKeyPairWithSubtype:handler:]", + "coverage": 0.9375 + }, + { + "name": "__67-[FIRInstanceIDKeyPairStore deleteSavedKeyPairWithSubtype:handler:]_block_invoke", + "coverage": 0.76 + }, + { + "name": "+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]", + "coverage": 1 + }, + { + "name": "__75+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]_block_invoke", + "coverage": 0.7619047619047619 + }, + { + "name": "__75+[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:publicTag:handler:]_block_invoke_2", + "coverage": 0.5454545454545454 + }, + { + "name": "-[FIRInstanceIDKeyPairStore removeKeyPairCreationTimePlistWithError:]", + "coverage": 0.5 + }, + { + "name": "+[FIRInstanceIDKeyPairStore keyStoreFileName]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDUtilities.m", + "coverage": 0.8547008547008547, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDRegisterServer", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentTimestampInSeconds", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentTimestampInMilliseconds", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentAppVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAppIdentifier", + "coverage": 1 + }, + { + "name": "FIRInstanceIDFirebaseAppID", + "coverage": 1 + }, + { + "name": "FIRInstanceIDDeviceModel", + "coverage": 1 + }, + { + "name": "__FIRInstanceIDDeviceModel_block_invoke", + "coverage": 1 + }, + { + "name": "FIRInstanceIDOperatingSystemVersion", + "coverage": 1 + }, + { + "name": "FIRInstanceIDHasLocaleChanged", + "coverage": 1 + }, + { + "name": "FIRInstanceIDIsValidGCMScope", + "coverage": 1 + }, + { + "name": "FIRInstanceIDStringForAPNSDeviceToken", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAPNSTupleStringForTokenAndServerType", + "coverage": 1 + }, + { + "name": "FIRInstanceIDCurrentGCMVersion", + "coverage": 0.6 + }, + { + "name": "FIRInstanceIDCurrentLocale", + "coverage": 0.4583333333333333 + } + ] + }, + { + "name": "FIRInstanceIDStore.m", + "coverage": 0.8579234972677595, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDStore initWithDelegate:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore initWithCheckinStore:tokenStore:delegate:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore hasSubDirectory:]", + "coverage": 0.8181818181818182 + }, + { + "name": "+[FIRInstanceIDStore supportedDirectory]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore pathForSupportSubDirectory:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDStore createSubDirectory:]", + "coverage": 0.6 + }, + { + "name": "+[FIRInstanceIDStore removeSubDirectory:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore resetCredentialsIfNeeded]", + "coverage": 1 + }, + { + "name": "__46-[FIRInstanceIDStore resetCredentialsIfNeeded]_block_invoke", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRInstanceIDStore tokenInfoWithAuthorizedEntity:scope:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRInstanceIDStore cachedTokenInfos]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore saveTokenInfo:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore removeCachedTokenWithAuthorizedEntity:scope:]", + "coverage": 0.4444444444444444 + }, + { + "name": "-[FIRInstanceIDStore removeAllCachedTokensWithHandler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore saveCheckinPreferences:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore cachedCheckinPreferences]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDStore removeCheckinPreferencesWithHandler:]", + "coverage": 1 + }, + { + "name": "__58-[FIRInstanceIDStore removeCheckinPreferencesWithHandler:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingLogger.m", + "coverage": 0.8679245283018868, + "type": "objc", + "functions": [ + { + "name": "+[FIRMessagingLogger standardLogger]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingLogger formatMessageCode:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingLogger logFuncDebug:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingLogger logFuncInfo:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingLogger logFuncNotice:messageCode:msg:]", + "coverage": 0 + }, + { + "name": "-[FIRMessagingLogger logFuncWarning:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingLogger logFuncError:messageCode:msg:]", + "coverage": 1 + }, + { + "name": "FIRMessagingSharedLogger", + "coverage": 1 + }, + { + "name": "__FIRMessagingSharedLogger_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingAnalytics.m", + "coverage": 0.8758169934640523, + "type": "objc", + "functions": [ + { + "name": "+[FIRMessagingAnalytics canLogNotification:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingAnalytics logOpenNotification:toAnalytics:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingAnalytics logForegroundNotification:toAnalytics:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingAnalytics logEvent:withNotification:toAnalytics:]", + "coverage": 0.6666666666666666 + }, + { + "name": "+[FIRMessagingAnalytics paramsForEvent:withNotification:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingAnalytics logUserPropertyForConversionTracking:toAnalytics:]", + "coverage": 0.8666666666666667 + }, + { + "name": "+[FIRMessagingAnalytics logMessage:toAnalytics:]", + "coverage": 0.6071428571428571 + }, + { + "name": "+[FIRMessagingAnalytics currentUIApplication]", + "coverage": 1 + } + ] + }, + { + "name": "FIRApp.m", + "coverage": 0.8848, + "type": "objc", + "functions": [ + { + "name": "+[FIRApp configure]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithOptions:]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithName:options:]", + "coverage": 0.9333333333333333 + }, + { + "name": "+[FIRApp defaultApp]", + "coverage": 1 + }, + { + "name": "+[FIRApp appNamed:]", + "coverage": 1 + }, + { + "name": "+[FIRApp allApps]", + "coverage": 1 + }, + { + "name": "+[FIRApp resetApps]", + "coverage": 1 + }, + { + "name": "-[FIRApp deleteApp:]", + "coverage": 0.7916666666666666 + }, + { + "name": "+[FIRApp addAppToAppDictionary:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRApp initInstanceWithName:options:]", + "coverage": 1 + }, + { + "name": "-[FIRApp configureCore]", + "coverage": 0.7962962962962963 + }, + { + "name": "-[FIRApp options]", + "coverage": 1 + }, + { + "name": "-[FIRApp setDataCollectionDefaultEnabled:]", + "coverage": 0.9354838709677419 + }, + { + "name": "-[FIRApp isDataCollectionDefaultEnabled]", + "coverage": 1 + }, + { + "name": "+[FIRApp sendNotificationsToSDKs:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForMissingOptions]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForSubspecConfigurationFailureWithDomain:errorCode:service:reason:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForInvalidAppID]", + "coverage": 0 + }, + { + "name": "+[FIRApp isDefaultAppConfigured]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerLibrary:withVersion:]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerInternalLibrary:withName:withVersion:]", + "coverage": 1 + }, + { + "name": "__55+[FIRApp registerInternalLibrary:withName:withVersion:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRApp firebaseUserAgent]", + "coverage": 1 + }, + { + "name": "-[FIRApp checkExpectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp isAppIDValid]", + "coverage": 1 + }, + { + "name": "+[FIRApp validateAppID:]", + "coverage": 0.9473684210526315 + }, + { + "name": "+[FIRApp actualBundleID]", + "coverage": 0 + }, + { + "name": "+[FIRApp validateAppIDFormat:withVersion:]", + "coverage": 0.8450704225352113 + }, + { + "name": "+[FIRApp validateAppIDFingerprint:withVersion:]", + "coverage": 0.8620689655172413 + }, + { + "name": "-[FIRApp expectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp clearDataCollectionSwitchFromUserDefaults]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromUserDefaultsForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromPlist]", + "coverage": 1 + }, + { + "name": "__43+[FIRApp readDataCollectionSwitchFromPlist]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRApp sendLogsWithServiceName:version:error:]", + "coverage": 0.2631578947368421 + } + ] + }, + { + "name": "FIRInstanceIDTokenInfo.m", + "coverage": 0.889763779527559, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDTokenInfo initWithAuthorizedEntity:scope:token:appVersion:firebaseAppID:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenInfo isFresh]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenInfo initWithCoder:]", + "coverage": 0.78125 + }, + { + "name": "-[FIRInstanceIDTokenInfo encodeWithCoder:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinPreferences.m", + "coverage": 0.8913043478260869, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinPreferences checkinPlistContents]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences hasCheckinInfo]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences hasValidCheckinInfo]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences setHasPreCachedAuthCredentials:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinService.m", + "coverage": 0.896414342629482, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDCheckinService init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService checkinWithExistingCheckin:completion:]", + "coverage": 0.9649122807017544 + }, + { + "name": "__69-[FIRInstanceIDCheckinService checkinWithExistingCheckin:completion:]_block_invoke", + "coverage": 0.7105263157894737 + }, + { + "name": "-[FIRInstanceIDCheckinService stopFetching]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinService checkinParametersWithExistingCheckin:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinService setCheckinTestBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDAPNSInfo.m", + "coverage": 0.9090909090909091, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAPNSInfo initWithDeviceToken:isSandbox:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo initWithTokenOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo initWithCoder:]", + "coverage": 0.75 + }, + { + "name": "-[FIRInstanceIDAPNSInfo encodeWithCoder:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAPNSInfo isEqualToAPNSInfo:]", + "coverage": 0.7142857142857143 + } + ] + }, + { + "name": "FIRInstanceIDAuthService.m", + "coverage": 0.9166666666666666, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAuthService initWithCheckinService:store:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService initWithStore:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService scheduleCheckin:]", + "coverage": 0.7 + }, + { + "name": "-[FIRInstanceIDAuthService startCheckinTimerWithDuration:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService clearScheduledCheckinTimer]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService onScheduledCheckinTimerFired:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService performScheduledCheckin]", + "coverage": 1 + }, + { + "name": "__51-[FIRInstanceIDAuthService performScheduledCheckin]_block_invoke", + "coverage": 1 + }, + { + "name": "__51-[FIRInstanceIDAuthService performScheduledCheckin]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService calculateNextCheckinRetryIntervalInSeconds]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[FIRInstanceIDAuthService hasValidCheckinInfo]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__56-[FIRInstanceIDAuthService fetchCheckinInfoWithHandler:]_block_invoke_3", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService checkinPreferences]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService stopCheckinRequest]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService resetCheckinWithHandler:]", + "coverage": 0 + }, + { + "name": "__52-[FIRInstanceIDAuthService resetCheckinWithHandler:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRInstanceIDAuthService notifyCheckinHandlersWithCheckin:error:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthService cachedCheckinMatchesCheckin:]", + "coverage": 0.5714285714285714 + } + ] + }, + { + "name": "FIRInstanceIDKeychain.m", + "coverage": 0.9227053140096618, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDKeychain sharedInstance]", + "coverage": 1 + }, + { + "name": "__39+[FIRInstanceIDKeychain sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain init]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain itemWithQuery:]", + "coverage": 1 + }, + { + "name": "__39-[FIRInstanceIDKeychain itemWithQuery:]_block_invoke", + "coverage": 0.8571428571428571 + }, + { + "name": "-[FIRInstanceIDKeychain removeItemWithQuery:handler:]", + "coverage": 1 + }, + { + "name": "__53-[FIRInstanceIDKeychain removeItemWithQuery:handler:]_block_invoke", + "coverage": 0.8260869565217391 + }, + { + "name": "__53-[FIRInstanceIDKeychain removeItemWithQuery:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain addItemWithQuery:handler:]", + "coverage": 1 + }, + { + "name": "__50-[FIRInstanceIDKeychain addItemWithQuery:handler:]_block_invoke", + "coverage": 0.6470588235294118 + }, + { + "name": "__50-[FIRInstanceIDKeychain addItemWithQuery:handler:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeychain generateKeyPairWithPrivateTag:publicTag:]", + "coverage": 1 + }, + { + "name": "__65-[FIRInstanceIDKeychain generateKeyPairWithPrivateTag:publicTag:]_block_invoke", + "coverage": 0.6666666666666666 + } + ] + }, + { + "name": "FIRInstanceIDAuthKeyChain.m", + "coverage": 0.9405405405405406, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDAuthKeychain initWithIdentifier:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDAuthKeychain keychainQueryForService:account:generic:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain keychainQueryForService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain itemsMatchingService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain dataForService:account:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain removeItemsMatchingService:account:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDAuthKeychain setData:forService:accessibility:account:handler:]", + "coverage": 0.9183673469387755 + }, + { + "name": "__78-[FIRInstanceIDAuthKeychain setData:forService:accessibility:account:handler:]_block_invoke", + "coverage": 0.72 + } + ] + }, + { + "name": "FIRMessagingRmqManager.m", + "coverage": 0.9473684210526315, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingRmqManager initWithDatabaseName:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager loadRmqId]", + "coverage": 0.5 + }, + { + "name": "-[FIRMessagingRmqManager loadInitialOutgoingPersistentId]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager saveRmqMessage:error:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager saveMessage:withRmqId:tag:error:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager saveLastOutgoingRmqId:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager saveS2dMessageWithRmqId:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager queryHighestRmqId]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager querylastRmqId]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager unackedS2dRmqIds]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager scanWithRmqMessageHandler:dataMessageHandler:]", + "coverage": 1 + }, + { + "name": "__71-[FIRMessagingRmqManager scanWithRmqMessageHandler:dataMessageHandler:]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager ackReceivedForRmqId:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager removeRmqMessagesWithRmqId:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager removeRmqMessagesWithRmqIds:]", + "coverage": 0.9 + }, + { + "name": "-[FIRMessagingRmqManager removeS2dIds:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager querySyncMessageWithRmqID:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager deleteSyncMessageWithRmqID:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager deleteExpiredOrFinishedSyncMessages:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager saveSyncMessageWithRmqID:expirationTime:apnsReceived:mcsReceived:error:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager updateSyncMessageViaAPNSWithRmqID:error:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager updateSyncMessageViaMCSWithRmqID:error:]", + "coverage": 1 + }, + { + "name": "+[FIRMessagingRmqManager removeDatabaseWithName:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRmqManager nextRmqId]", + "coverage": 1 + } + ] + }, + { + "name": "FIROptions.m", + "coverage": 0.9489795918367347, + "type": "objc", + "functions": [ + { + "name": "+[FIROptions defaultOptions]", + "coverage": 0.8461538461538461 + }, + { + "name": "+[FIROptions initialize]", + "coverage": 1 + }, + { + "name": "+[FIROptions defaultOptionsDictionary]", + "coverage": 1 + }, + { + "name": "+[FIROptions plistFilePathWithName:]", + "coverage": 1 + }, + { + "name": "+[FIROptions resetDefaultOptions]", + "coverage": 1 + }, + { + "name": "-[FIROptions initInternalWithOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithContentsOfFile:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithGoogleAppID:GCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions APIKey]", + "coverage": 1 + }, + { + "name": "-[FIROptions checkEditingLocked]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAPIKey:]", + "coverage": 1 + }, + { + "name": "-[FIROptions clientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions trackingID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setTrackingID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions GCMSenderID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions projectID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setProjectID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions androidClientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAndroidClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions googleAppID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGoogleAppID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions libraryVersionID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setLibraryVersionID:]", + "coverage": 0 + }, + { + "name": "-[FIROptions databaseURL]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDatabaseURL:]", + "coverage": 1 + }, + { + "name": "-[FIROptions storageBucket]", + "coverage": 1 + }, + { + "name": "-[FIROptions setStorageBucket:]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDeepLinkURLScheme:]", + "coverage": 1 + }, + { + "name": "-[FIROptions bundleID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionaryWithInfoDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIROptions isMeasurementEnabled]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIROptions isAnalyticsCollectionExpicitlySet]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionEnabled]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionDeactivated]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsEnabled]", + "coverage": 0 + }, + { + "name": "-[FIROptions isSignInEnabled]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAnalyticsConfiguration.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "+[FIRAnalyticsConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__43+[FIRAnalyticsConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration postNotificationName:value:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAnalyticsConfiguration setMinimumSessionInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setSessionTimeoutInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:persistSetting:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDCheckinPreferences+Internal.m", + "coverage": 0.9692307692307692, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) preferencesFromKeychainContents:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) initWithDeviceID:secretToken:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) reset]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) updateWithCheckinPlistContents:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDCheckinPreferences(Internal) checkinKeychainContent]", + "coverage": 0.75 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinDeviceIDFromKeychainContent:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinSecretFromKeychainContent:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDCheckinPreferences(Internal) checkinKeychainContent:forIndex:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentContainer.m", + "coverage": 0.9818181818181818, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:]", + "coverage": 1 + }, + { + "name": "__55+[FIRComponentContainer registerAsComponentRegistrant:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:inSet:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:registrants:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer populateComponentsFromRegisteredClasses:forApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer instantiateInstanceForProtocol:withBlock:]", + "coverage": 0.9285714285714286 + }, + { + "name": "-[FIRComponentContainer instanceForProtocol:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer removeAllCachedInstances]", + "coverage": 1 + } + ] + }, + { + "name": "main.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "main", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDTokenStore.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRInstanceIDTokenStore defaultStore]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore initWithKeychain:]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenStore serviceKeyForAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore tokenInfoWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore cachedTokenInfos]", + "coverage": 1 + }, + { + "name": "+[FIRInstanceIDTokenStore tokenInfoFromKeychainItem:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore saveTokenInfo:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore removeTokenWithAuthorizedEntity:scope:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDTokenStore removeAllTokensWithHandler:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPairUtilities.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "FIRInstanceIDWebSafeBase64", + "coverage": 1 + }, + { + "name": "FIRInstanceIDSHA1", + "coverage": 1 + }, + { + "name": "FIRInstanceIDKeyPairQuery", + "coverage": 1 + }, + { + "name": "FIRInstanceIDAppIdentity", + "coverage": 1 + } + ] + }, + { + "name": "FIRInstanceIDKeyPair.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRInstanceIDKeyPair initWithPrivateKey:publicKey:publicKeyData:privateKeyData:]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair isValid]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair publicKey]", + "coverage": 1 + }, + { + "name": "-[FIRInstanceIDKeyPair privateKey]", + "coverage": 1 + } + ] + }, + { + "name": "FIRMessagingRegistrar.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRMessagingRegistrar deviceAuthID]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRegistrar secretToken]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRegistrar init]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRegistrar tryToLoadValidCheckinInfo]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRegistrar updateSubscriptionToTopic:withToken:options:shouldDelete:handler:]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRegistrar cancelAllRequests]", + "coverage": 1 + }, + { + "name": "-[FIRMessagingRegistrar doUpdateSubscriptionForTopic:token:options:shouldDelete:completion:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentType.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentType instanceForProtocol:inContainer:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponent.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponent componentWithProtocol:creationBlock:]", + "coverage": 1 + }, + { + "name": "+[FIRComponent componentWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRComponent initWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAppAssociationRegistration.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRAppAssociationRegistration registeredObjectWithHost:key:creationBlock:]", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "OCMock.framework", + "coverage": 0.6530303030303031, + "files": [ + { + "name": "OCMExceptionReturnValueProvider.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[OCMExceptionReturnValueProvider handleInvocation:]", + "coverage": 0 + } + ] + }, + { + "name": "OCMRealObjectForwarder.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[OCMRealObjectForwarder handleInvocation:]", + "coverage": 0 + } + ] + }, + { + "name": "OCMNotificationPoster.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[OCMNotificationPoster initWithNotification:]", + "coverage": 0 + }, + { + "name": "-[OCMNotificationPoster dealloc]", + "coverage": 0 + }, + { + "name": "-[OCMNotificationPoster handleInvocation:]", + "coverage": 0 + } + ] + }, + { + "name": "OCMArgAction.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[OCMArgAction handleArgument:]", + "coverage": 0 + } + ] + }, + { + "name": "NSInvocation+OCMAdditions.m", + "coverage": 0.35990888382687924, + "type": "objc", + "functions": [ + { + "name": "+[NSInvocation(OCMAdditions) invocationForBlock:withArguments:]", + "coverage": 0.9411764705882353 + }, + { + "name": "-[NSInvocation(OCMAdditions) retainObjectArgumentsExcludingObject:]", + "coverage": 0.7794117647058824 + }, + { + "name": "-[NSInvocation(OCMAdditions) setArgumentWithObject:atIndex:]", + "coverage": 0.3090909090909091 + }, + { + "name": "-[NSInvocation(OCMAdditions) getArgumentAtIndexAsObject:]", + "coverage": 0.5669291338582677 + }, + { + "name": "-[NSInvocation(OCMAdditions) invocationDescription]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) argumentDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) objectDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) boolDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) charDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) unsignedCharDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) intDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) unsignedIntDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) shortDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) unsignedShortDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) longDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) unsignedLongDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) longLongDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) unsignedLongLongDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) doubleDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) floatDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) longDoubleDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) structDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) pointerDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) cStringDescriptionAtIndex:]", + "coverage": 0 + }, + { + "name": "-[NSInvocation(OCMAdditions) selectorDescriptionAtIndex:]", + "coverage": 0 + } + ] + }, + { + "name": "OCMFunctions.m", + "coverage": 0.43243243243243246, + "type": "objc", + "functions": [ + { + "name": "OCMTypeWithoutQualifiers", + "coverage": 1 + }, + { + "name": "OCMIsUnqualifiedClassType", + "coverage": 1 + }, + { + "name": "OCMIsUnqualifiedBlockType", + "coverage": 0.9090909090909091 + }, + { + "name": "OCMIsBlockType", + "coverage": 1 + }, + { + "name": "OCMIsObjectType", + "coverage": 0.9523809523809523 + }, + { + "name": "OCMNumberTypeForObjCType", + "coverage": 1 + }, + { + "name": "ParseStructType", + "coverage": 0 + }, + { + "name": "OCMEqualTypesAllowingOpaqueStructsInternal", + "coverage": 0.18556701030927836 + }, + { + "name": "OCMEqualTypesAllowingOpaqueStructs", + "coverage": 0.7272727272727273 + }, + { + "name": "OCMCreateSubclass", + "coverage": 1 + }, + { + "name": "OCMIsAliasSelector", + "coverage": 1 + }, + { + "name": "OCMAliasForOriginalSelector", + "coverage": 1 + }, + { + "name": "OCMOriginalSelectorForAlias", + "coverage": 0.8333333333333334 + }, + { + "name": "OCMSetAssociatedMockForClass", + "coverage": 0.8 + }, + { + "name": "OCMGetAssociatedMockForClass", + "coverage": 1 + }, + { + "name": "OCMSetAssociatedMockForObject", + "coverage": 0.8 + }, + { + "name": "OCMGetAssociatedMockForObject", + "coverage": 1 + }, + { + "name": "OCMReportFailure", + "coverage": 0 + } + ] + }, + { + "name": "OCMConstraint.m", + "coverage": 0.4426229508196721, + "type": "objc", + "functions": [ + { + "name": "+[OCMConstraint constraint]", + "coverage": 1 + }, + { + "name": "-[OCMConstraint evaluate:]", + "coverage": 0 + }, + { + "name": "-[OCMConstraint copyWithZone:]", + "coverage": 1 + }, + { + "name": "+[OCMConstraint constraintWithSelector:onObject:]", + "coverage": 0 + }, + { + "name": "+[OCMConstraint constraintWithSelector:onObject:withValue:]", + "coverage": 0 + }, + { + "name": "-[OCMAnyConstraint evaluate:]", + "coverage": 1 + }, + { + "name": "-[OCMIsNilConstraint evaluate:]", + "coverage": 1 + }, + { + "name": "-[OCMIsNotNilConstraint evaluate:]", + "coverage": 0 + }, + { + "name": "-[OCMIsNotEqualConstraint evaluate:]", + "coverage": 0 + }, + { + "name": "-[OCMInvocationConstraint evaluate:]", + "coverage": 0 + }, + { + "name": "-[OCMBlockConstraint initWithConstraintBlock:]", + "coverage": 1 + }, + { + "name": "-[OCMBlockConstraint dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMBlockConstraint evaluate:]", + "coverage": 1 + } + ] + }, + { + "name": "OCMStubRecorder.m", + "coverage": 0.5702479338842975, + "type": "objc", + "functions": [ + { + "name": "-[OCMStubRecorder init]", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder stub]", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder andReturn:]", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder andReturnValue:]", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder andThrow:]", + "coverage": 0 + }, + { + "name": "-[OCMStubRecorder andPost:]", + "coverage": 0 + }, + { + "name": "-[OCMStubRecorder andCall:onObject:]", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder andDo:]", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder andForwardToRealObject]", + "coverage": 0 + }, + { + "name": "-[OCMStubRecorder forwardInvocation:]", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder(Properties) _andReturn]", + "coverage": 1 + }, + { + "name": "__41-[OCMStubRecorder(Properties) _andReturn]_block_invoke", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder(Properties) _andThrow]", + "coverage": 0 + }, + { + "name": "__40-[OCMStubRecorder(Properties) _andThrow]_block_invoke", + "coverage": 0 + }, + { + "name": "-[OCMStubRecorder(Properties) _andPost]", + "coverage": 0 + }, + { + "name": "__39-[OCMStubRecorder(Properties) _andPost]_block_invoke", + "coverage": 0 + }, + { + "name": "-[OCMStubRecorder(Properties) _andCall]", + "coverage": 0 + }, + { + "name": "__39-[OCMStubRecorder(Properties) _andCall]_block_invoke", + "coverage": 0 + }, + { + "name": "-[OCMStubRecorder(Properties) _andDo]", + "coverage": 1 + }, + { + "name": "__37-[OCMStubRecorder(Properties) _andDo]_block_invoke", + "coverage": 1 + }, + { + "name": "-[OCMStubRecorder(Properties) _andForwardToRealObject]", + "coverage": 0 + }, + { + "name": "__54-[OCMStubRecorder(Properties) _andForwardToRealObject]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "NSMethodSignature+OCMAdditions.m", + "coverage": 0.5746268656716418, + "type": "objc", + "functions": [ + { + "name": "+[NSMethodSignature(OCMAdditions) signatureForDynamicPropertyAccessedWithSelector:inClass:]", + "coverage": 0.16666666666666666 + }, + { + "name": "+[NSMethodSignature(OCMAdditions) propertyMatchingSelector:inClass:isGetter:]", + "coverage": 0.58 + }, + { + "name": "+[NSMethodSignature(OCMAdditions) signatureForBlock:]", + "coverage": 0.9565217391304348 + }, + { + "name": "-[NSMethodSignature(OCMAdditions) usesSpecialStructureReturn]", + "coverage": 1 + }, + { + "name": "-[NSMethodSignature(OCMAdditions) fullTypeString]", + "coverage": 0 + }, + { + "name": "-[NSMethodSignature(OCMAdditions) fullObjCTypes]", + "coverage": 0 + } + ] + }, + { + "name": "OCMArg.m", + "coverage": 0.5955056179775281, + "type": "objc", + "functions": [ + { + "name": "+[OCMArg any]", + "coverage": 1 + }, + { + "name": "+[OCMArg anyPointer]", + "coverage": 1 + }, + { + "name": "+[OCMArg anyObjectRef]", + "coverage": 1 + }, + { + "name": "+[OCMArg anySelector]", + "coverage": 0 + }, + { + "name": "+[OCMArg isNil]", + "coverage": 1 + }, + { + "name": "+[OCMArg isNotNil]", + "coverage": 0 + }, + { + "name": "+[OCMArg isEqual:]", + "coverage": 1 + }, + { + "name": "+[OCMArg isNotEqual:]", + "coverage": 0 + }, + { + "name": "+[OCMArg isKindOfClass:]", + "coverage": 0 + }, + { + "name": "__24+[OCMArg isKindOfClass:]_block_invoke", + "coverage": 0 + }, + { + "name": "+[OCMArg checkWithSelector:onObject:]", + "coverage": 0 + }, + { + "name": "+[OCMArg checkWithBlock:]", + "coverage": 1 + }, + { + "name": "+[OCMArg setTo:]", + "coverage": 1 + }, + { + "name": "+[OCMArg setToValue:]", + "coverage": 0 + }, + { + "name": "+[OCMArg invokeBlock]", + "coverage": 0 + }, + { + "name": "+[OCMArg invokeBlockWithArgs:]", + "coverage": 1 + }, + { + "name": "+[OCMArg defaultValue]", + "coverage": 0 + }, + { + "name": "+[OCMArg resolveSpecialValues:]", + "coverage": 0.7368421052631579 + } + ] + }, + { + "name": "OCProtocolMockObject.m", + "coverage": 0.6153846153846154, + "type": "objc", + "functions": [ + { + "name": "-[OCProtocolMockObject initWithProtocol:]", + "coverage": 1 + }, + { + "name": "-[OCProtocolMockObject description]", + "coverage": 0 + }, + { + "name": "-[OCProtocolMockObject methodSignatureForSelector:]", + "coverage": 1 + }, + { + "name": "-[OCProtocolMockObject conformsToProtocol:]", + "coverage": 0 + }, + { + "name": "-[OCProtocolMockObject respondsToSelector:]", + "coverage": 0 + } + ] + }, + { + "name": "OCMLocation.m", + "coverage": 0.6896551724137931, + "type": "objc", + "functions": [ + { + "name": "+[OCMLocation locationWithTestCase:file:line:]", + "coverage": 1 + }, + { + "name": "-[OCMLocation initWithTestCase:file:line:]", + "coverage": 1 + }, + { + "name": "-[OCMLocation dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMLocation testCase]", + "coverage": 0 + }, + { + "name": "-[OCMLocation file]", + "coverage": 0 + }, + { + "name": "-[OCMLocation line]", + "coverage": 0 + }, + { + "name": "OCMMakeLocation", + "coverage": 1 + } + ] + }, + { + "name": "OCObserverMockObject.m", + "coverage": 0.7108433734939759, + "type": "objc", + "functions": [ + { + "name": "-[OCObserverMockObject init]", + "coverage": 1 + }, + { + "name": "-[OCObserverMockObject retain]", + "coverage": 1 + }, + { + "name": "-[OCObserverMockObject dealloc]", + "coverage": 1 + }, + { + "name": "-[OCObserverMockObject description]", + "coverage": 0 + }, + { + "name": "-[OCObserverMockObject setExpectationOrderMatters:]", + "coverage": 1 + }, + { + "name": "-[OCObserverMockObject autoRemoveFromCenter:]", + "coverage": 1 + }, + { + "name": "-[OCObserverMockObject expect]", + "coverage": 1 + }, + { + "name": "-[OCObserverMockObject verify]", + "coverage": 0 + }, + { + "name": "-[OCObserverMockObject verifyAtLocation:]", + "coverage": 0.4117647058823529 + }, + { + "name": "-[OCObserverMockObject notificationWithName:object:]", + "coverage": 0 + }, + { + "name": "-[OCObserverMockObject notificationWithName:object:userInfo:]", + "coverage": 0 + }, + { + "name": "-[OCObserverMockObject handleNotification:]", + "coverage": 0.8888888888888888 + } + ] + }, + { + "name": "OCMockObject.m", + "coverage": 0.7164179104477612, + "type": "objc", + "functions": [ + { + "name": "+[OCMockObject initialize]", + "coverage": 1 + }, + { + "name": "+[OCMockObject mockForClass:]", + "coverage": 1 + }, + { + "name": "+[OCMockObject mockForProtocol:]", + "coverage": 1 + }, + { + "name": "+[OCMockObject partialMockForObject:]", + "coverage": 1 + }, + { + "name": "+[OCMockObject niceMockForClass:]", + "coverage": 1 + }, + { + "name": "+[OCMockObject niceMockForProtocol:]", + "coverage": 1 + }, + { + "name": "+[OCMockObject _makeNice:]", + "coverage": 1 + }, + { + "name": "+[OCMockObject observerMock]", + "coverage": 1 + }, + { + "name": "-[OCMockObject init]", + "coverage": 0.782608695652174 + }, + { + "name": "-[OCMockObject dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMockObject description]", + "coverage": 0 + }, + { + "name": "-[OCMockObject addStub:]", + "coverage": 1 + }, + { + "name": "-[OCMockObject addExpectation:]", + "coverage": 1 + }, + { + "name": "-[OCMockObject setExpectationOrderMatters:]", + "coverage": 0 + }, + { + "name": "-[OCMockObject stopMocking]", + "coverage": 1 + }, + { + "name": "-[OCMockObject stub]", + "coverage": 1 + }, + { + "name": "-[OCMockObject expect]", + "coverage": 1 + }, + { + "name": "-[OCMockObject reject]", + "coverage": 1 + }, + { + "name": "-[OCMockObject verify]", + "coverage": 1 + }, + { + "name": "-[OCMockObject verifyAtLocation:]", + "coverage": 0.5897435897435898 + }, + { + "name": "-[OCMockObject verifyWithDelay:]", + "coverage": 0 + }, + { + "name": "-[OCMockObject verifyWithDelay:atLocation:]", + "coverage": 0.9583333333333334 + }, + { + "name": "-[OCMockObject verifyInvocation:]", + "coverage": 0 + }, + { + "name": "-[OCMockObject verifyInvocation:atLocation:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[OCMockObject forwardingTargetForSelector:]", + "coverage": 1 + }, + { + "name": "-[OCMockObject handleSelector:]", + "coverage": 1 + }, + { + "name": "-[OCMockObject forwardInvocation:]", + "coverage": 0.8695652173913043 + }, + { + "name": "-[OCMockObject handleInvocation:]", + "coverage": 0.9354838709677419 + }, + { + "name": "-[OCMockObject _nextExpectedInvocation]", + "coverage": 1 + }, + { + "name": "-[OCMockObject handleUnRecordedInvocation:]", + "coverage": 0.5714285714285714 + }, + { + "name": "-[OCMockObject doesNotRecognizeSelector:]", + "coverage": 0 + }, + { + "name": "-[OCMockObject _stubDescriptions:]", + "coverage": 0 + } + ] + }, + { + "name": "OCMReturnValueProvider.m", + "coverage": 0.72, + "type": "objc", + "functions": [ + { + "name": "-[OCMReturnValueProvider initWithValue:]", + "coverage": 1 + }, + { + "name": "-[OCMReturnValueProvider dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMReturnValueProvider handleInvocation:]", + "coverage": 0.46153846153846156 + } + ] + }, + { + "name": "OCClassMockObject.m", + "coverage": 0.7669902912621359, + "type": "objc", + "functions": [ + { + "name": "-[OCClassMockObject initWithClass:]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject dealloc]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject description]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject mockedClass]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject stopMocking]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject stopMockingClassMethods]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject addStub:]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject prepareClassForClassMethodMocking]", + "coverage": 0.9649122807017544 + }, + { + "name": "__54-[OCClassMockObject prepareClassForClassMethodMocking]_block_invoke", + "coverage": 0.9473684210526315 + }, + { + "name": "-[OCClassMockObject setupForwarderForClassMethodSelector:]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject forwardInvocationForClassObject:]", + "coverage": 0.8461538461538461 + }, + { + "name": "-[OCClassMockObject initializeForClassObject]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject methodSignatureForSelector:]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject mockObjectClass]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject class]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject respondsToSelector:]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject isKindOfClass:]", + "coverage": 1 + }, + { + "name": "-[OCClassMockObject conformsToProtocol:]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSValue__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSTimeZone__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSSet__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSOrderedSet__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSNumber__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSDate__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSString__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSDictionary__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSData__]", + "coverage": 0 + }, + { + "name": "-[OCClassMockObject(NSIsKindsImplementation) isNSArray__]", + "coverage": 0 + } + ] + }, + { + "name": "OCMObserverRecorder.m", + "coverage": 0.7692307692307693, + "type": "objc", + "functions": [ + { + "name": "-[OCMObserverRecorder dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMObserverRecorder notificationWithName:object:]", + "coverage": 0 + }, + { + "name": "-[OCMObserverRecorder notificationWithName:object:userInfo:]", + "coverage": 1 + }, + { + "name": "-[OCMObserverRecorder matchesNotification:]", + "coverage": 1 + }, + { + "name": "-[OCMObserverRecorder argument:matchesArgument:]", + "coverage": 0.7727272727272727 + } + ] + }, + { + "name": "OCMInvocationExpectation.m", + "coverage": 0.8, + "type": "objc", + "functions": [ + { + "name": "-[OCMInvocationExpectation setMatchAndReject:]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationExpectation isMatchAndReject]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationExpectation isSatisfied]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationExpectation handleInvocation:]", + "coverage": 0.6428571428571429 + } + ] + }, + { + "name": "OCMIndirectReturnValueProvider.m", + "coverage": 0.8333333333333334, + "type": "objc", + "functions": [ + { + "name": "-[OCMIndirectReturnValueProvider initWithProvider:andSelector:]", + "coverage": 1 + }, + { + "name": "-[OCMIndirectReturnValueProvider dealloc]", + "coverage": 0 + }, + { + "name": "-[OCMIndirectReturnValueProvider handleInvocation:]", + "coverage": 1 + } + ] + }, + { + "name": "OCMRecorder.m", + "coverage": 0.8363636363636363, + "type": "objc", + "functions": [ + { + "name": "-[OCMRecorder init]", + "coverage": 1 + }, + { + "name": "-[OCMRecorder initWithMockObject:]", + "coverage": 1 + }, + { + "name": "-[OCMRecorder setMockObject:]", + "coverage": 1 + }, + { + "name": "-[OCMRecorder dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMRecorder description]", + "coverage": 0 + }, + { + "name": "-[OCMRecorder invocationMatcher]", + "coverage": 0 + }, + { + "name": "-[OCMRecorder classMethod]", + "coverage": 1 + }, + { + "name": "-[OCMRecorder ignoringNonObjectArgs]", + "coverage": 1 + }, + { + "name": "-[OCMRecorder methodSignatureForSelector:]", + "coverage": 1 + }, + { + "name": "-[OCMRecorder forwardInvocation:]", + "coverage": 1 + }, + { + "name": "-[OCMRecorder doesNotRecognizeSelector:]", + "coverage": 0 + } + ] + }, + { + "name": "OCMBoxedReturnValueProvider.m", + "coverage": 0.84375, + "type": "objc", + "functions": [ + { + "name": "-[OCMBoxedReturnValueProvider handleInvocation:]", + "coverage": 0.7619047619047619 + }, + { + "name": "-[OCMBoxedReturnValueProvider isMethodReturnType:compatibleWithValueType:]", + "coverage": 1 + } + ] + }, + { + "name": "OCMInvocationMatcher.m", + "coverage": 0.9148936170212766, + "type": "objc", + "functions": [ + { + "name": "-[OCMInvocationMatcher dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationMatcher setInvocation:]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationMatcher setRecordedAsClassMethod:]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationMatcher recordedAsClassMethod]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationMatcher setIgnoreNonObjectArgs:]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationMatcher description]", + "coverage": 0 + }, + { + "name": "-[OCMInvocationMatcher recordedInvocation]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationMatcher matchesSelector:]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationMatcher matchesInvocation:]", + "coverage": 0.9122807017543859 + } + ] + }, + { + "name": "OCMInvocationStub.m", + "coverage": 0.918918918918919, + "type": "objc", + "functions": [ + { + "name": "-[OCMInvocationStub init]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationStub dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationStub addInvocationAction:]", + "coverage": 1 + }, + { + "name": "-[OCMInvocationStub invocationActions]", + "coverage": 0 + }, + { + "name": "-[OCMInvocationStub handleInvocation:]", + "coverage": 1 + } + ] + }, + { + "name": "OCMPassByRefSetter.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "-[OCMPassByRefSetter initWithValue:]", + "coverage": 1 + }, + { + "name": "-[OCMPassByRefSetter dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMPassByRefSetter handleArgument:]", + "coverage": 0.9 + } + ] + }, + { + "name": "OCPartialMockObject.m", + "coverage": 0.9560439560439561, + "type": "objc", + "functions": [ + { + "name": "-[OCPartialMockObject initWithObject:]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject dealloc]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject description]", + "coverage": 0 + }, + { + "name": "-[OCPartialMockObject realObject]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject assertClassIsSupported:]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[OCPartialMockObject classToSubclassForObject:]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject stopMocking]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject addStub:]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject handleUnRecordedInvocation:]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject prepareObjectForInstanceMethodMocking]", + "coverage": 1 + }, + { + "name": "__60-[OCPartialMockObject prepareObjectForInstanceMethodMocking]_block_invoke", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject setupForwarderForSelector:]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject classForRealObject]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject forwardingTargetForSelectorForRealObject:]", + "coverage": 1 + }, + { + "name": "-[OCPartialMockObject ocmock_replaced_forwardingTargetForSelector:]", + "coverage": 0 + }, + { + "name": "-[OCPartialMockObject forwardInvocationForRealObject:]", + "coverage": 1 + } + ] + }, + { + "name": "NSValue+OCMAdditions.m", + "coverage": 0.9649122807017544, + "type": "objc", + "functions": [ + { + "name": "OCMNumberForValue", + "coverage": 1 + }, + { + "name": "-[NSValue(OCMAdditions) getBytes:objCType:]", + "coverage": 0.9459459459459459 + } + ] + }, + { + "name": "OCMBlockCaller.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[OCMBlockCaller initWithCallBlock:]", + "coverage": 1 + }, + { + "name": "-[OCMBlockCaller dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMBlockCaller handleInvocation:]", + "coverage": 1 + } + ] + }, + { + "name": "OCMMacroState.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[OCMMacroState beginStubMacro]", + "coverage": 1 + }, + { + "name": "+[OCMMacroState endStubMacro]", + "coverage": 1 + }, + { + "name": "+[OCMMacroState beginExpectMacro]", + "coverage": 1 + }, + { + "name": "+[OCMMacroState endExpectMacro]", + "coverage": 1 + }, + { + "name": "+[OCMMacroState beginRejectMacro]", + "coverage": 1 + }, + { + "name": "+[OCMMacroState endRejectMacro]", + "coverage": 1 + }, + { + "name": "+[OCMMacroState beginVerifyMacroAtLocation:]", + "coverage": 1 + }, + { + "name": "+[OCMMacroState endVerifyMacro]", + "coverage": 1 + }, + { + "name": "+[OCMMacroState globalState]", + "coverage": 1 + }, + { + "name": "-[OCMMacroState initWithRecorder:]", + "coverage": 1 + }, + { + "name": "-[OCMMacroState dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMMacroState recorder]", + "coverage": 1 + }, + { + "name": "-[OCMMacroState switchToClassMethod]", + "coverage": 1 + } + ] + }, + { + "name": "NSNotificationCenter+OCMAdditions.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[NSNotificationCenter(OCMAdditions) addMockObserver:name:object:]", + "coverage": 1 + } + ] + }, + { + "name": "OCMBlockArgCaller.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[OCMBlockArgCaller initWithBlockArguments:]", + "coverage": 1 + }, + { + "name": "-[OCMBlockArgCaller dealloc]", + "coverage": 1 + }, + { + "name": "-[OCMBlockArgCaller copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[OCMBlockArgCaller handleArgument:]", + "coverage": 1 + } + ] + }, + { + "name": "NSObject+OCMAdditions.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[NSObject(OCMAdditions) instanceMethodForwarderForSelector:]", + "coverage": 1 + }, + { + "name": "+[NSObject(OCMAdditions) enumerateMethodsInClass:usingBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "OCMVerifier.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[OCMVerifier init]", + "coverage": 1 + }, + { + "name": "-[OCMVerifier forwardInvocation:]", + "coverage": 1 + }, + { + "name": "-[OCMVerifier dealloc]", + "coverage": 1 + } + ] + }, + { + "name": "OCMExpectationRecorder.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[OCMExpectationRecorder init]", + "coverage": 1 + }, + { + "name": "-[OCMExpectationRecorder expectation]", + "coverage": 1 + }, + { + "name": "-[OCMExpectationRecorder never]", + "coverage": 1 + }, + { + "name": "-[OCMExpectationRecorder forwardInvocation:]", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "Protobuf.framework", + "coverage": 0.09864249142749777, + "files": [ + { + "name": "Any.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBAnyRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GPBAny descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "Wrappers.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBWrappersRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GPBDoubleValue descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBFloatValue descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBInt64Value descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBUInt64Value descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBInt32Value descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBUInt32Value descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBBoolValue descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBStringValue descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBBytesValue descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "Type.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBTypeRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "GPBSyntax_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GPBSyntax_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GPBType descriptor]", + "coverage": 0 + }, + { + "name": "GPBType_Syntax_RawValue", + "coverage": 0 + }, + { + "name": "SetGPBType_Syntax_RawValue", + "coverage": 0 + }, + { + "name": "+[GPBField descriptor]", + "coverage": 0 + }, + { + "name": "GPBField_Kind_RawValue", + "coverage": 0 + }, + { + "name": "SetGPBField_Kind_RawValue", + "coverage": 0 + }, + { + "name": "GPBField_Cardinality_RawValue", + "coverage": 0 + }, + { + "name": "SetGPBField_Cardinality_RawValue", + "coverage": 0 + }, + { + "name": "GPBField_Kind_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GPBField_Kind_IsValidValue", + "coverage": 0 + }, + { + "name": "GPBField_Cardinality_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GPBField_Cardinality_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GPBEnum descriptor]", + "coverage": 0 + }, + { + "name": "GPBEnum_Syntax_RawValue", + "coverage": 0 + }, + { + "name": "SetGPBEnum_Syntax_RawValue", + "coverage": 0 + }, + { + "name": "+[GPBEnumValue descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBOption descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "Timestamp.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBTimestampRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GPBTimestamp descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "Struct.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBStructRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "GPBNullValue_EnumDescriptor", + "coverage": 0 + }, + { + "name": "GPBNullValue_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GPBStruct descriptor]", + "coverage": 0 + }, + { + "name": "+[GPBValue descriptor]", + "coverage": 0 + }, + { + "name": "GPBValue_NullValue_RawValue", + "coverage": 0 + }, + { + "name": "SetGPBValue_NullValue_RawValue", + "coverage": 0 + }, + { + "name": "GPBValue_ClearKindOneOfCase", + "coverage": 0 + }, + { + "name": "+[GPBListValue descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "GPBDictionary.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "DictDefault_IsValidValue", + "coverage": 0 + }, + { + "name": "ComputeDictInt32FieldSize", + "coverage": 0 + }, + { + "name": "WriteDictInt32Field", + "coverage": 0 + }, + { + "name": "ComputeDictUInt32FieldSize", + "coverage": 0 + }, + { + "name": "WriteDictUInt32Field", + "coverage": 0 + }, + { + "name": "ComputeDictInt64FieldSize", + "coverage": 0 + }, + { + "name": "WriteDictInt64Field", + "coverage": 0 + }, + { + "name": "ComputeDictUInt64FieldSize", + "coverage": 0 + }, + { + "name": "WriteDictUInt64Field", + "coverage": 0 + }, + { + "name": "ComputeDictBoolFieldSize", + "coverage": 0 + }, + { + "name": "WriteDictBoolField", + "coverage": 0 + }, + { + "name": "ComputeDictEnumFieldSize", + "coverage": 0 + }, + { + "name": "WriteDictEnumField", + "coverage": 0 + }, + { + "name": "ComputeDictFloatFieldSize", + "coverage": 0 + }, + { + "name": "WriteDictFloatField", + "coverage": 0 + }, + { + "name": "ComputeDictDoubleFieldSize", + "coverage": 0 + }, + { + "name": "WriteDictDoubleField", + "coverage": 0 + }, + { + "name": "ComputeDictStringFieldSize", + "coverage": 0 + }, + { + "name": "WriteDictStringField", + "coverage": 0 + }, + { + "name": "ComputeDictObjectFieldSize", + "coverage": 0 + }, + { + "name": "WriteDictObjectField", + "coverage": 0 + }, + { + "name": "GPBDictionaryComputeSizeInternalHelper", + "coverage": 0 + }, + { + "name": "GPBDictionaryWriteToStreamInternalHelper", + "coverage": 0 + }, + { + "name": "GPBDictionaryIsInitializedInternalHelper", + "coverage": 0 + }, + { + "name": "ReadValue", + "coverage": 0 + }, + { + "name": "GPBDictionaryReadEntry", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary initWithUInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary enumerateKeysAndUInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBUInt32UInt32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary getUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary setUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary removeUInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary initWithInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary enumerateKeysAndInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBUInt32Int32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary getInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary setInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary removeInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary initWithUInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary enumerateKeysAndUInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBUInt32UInt64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary getUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary setUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary removeUInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32UInt64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary initWithInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary enumerateKeysAndInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBUInt32Int64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary getInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary setInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary removeInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Int64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary initWithBools:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary enumerateKeysAndBoolsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBUInt32BoolDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary getBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary setBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary removeBoolForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32BoolDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary initWithFloats:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary enumerateKeysAndFloatsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBUInt32FloatDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary getFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary setFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary removeFloatForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32FloatDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary initWithDoubles:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary enumerateKeysAndDoublesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBUInt32DoubleDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary getDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary setDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary removeDoubleForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32DoubleDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary initWithValidationFunction:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary initWithValidationFunction:rawValues:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary initWithValidationFunction:capacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary enumerateKeysAndRawValuesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary serializedDataForUnknownValue:forKey:keyDataType:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBUInt32EnumDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary getEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary getRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary enumerateKeysAndEnumsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary addRawEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary setRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary removeEnumForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32EnumDictionary setEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary initWithObjects:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary isInitialized]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary deepCopyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBUInt32ObjectDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary objectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary setObject:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary removeObjectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32ObjectDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary initWithUInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary enumerateKeysAndUInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBInt32UInt32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary getUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary setUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary removeUInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary initWithInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary enumerateKeysAndInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBInt32Int32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary getInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary setInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary removeInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary initWithUInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary enumerateKeysAndUInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBInt32UInt64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary getUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary setUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary removeUInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32UInt64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary initWithInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary enumerateKeysAndInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBInt32Int64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary getInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary setInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary removeInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Int64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary initWithBools:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary enumerateKeysAndBoolsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__49-[GPBInt32BoolDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary getBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary setBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary removeBoolForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32BoolDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary initWithFloats:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary enumerateKeysAndFloatsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBInt32FloatDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary getFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary setFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary removeFloatForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32FloatDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary initWithDoubles:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary enumerateKeysAndDoublesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBInt32DoubleDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary getDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary setDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary removeDoubleForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32DoubleDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary initWithValidationFunction:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary initWithValidationFunction:rawValues:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary initWithValidationFunction:capacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary enumerateKeysAndRawValuesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary serializedDataForUnknownValue:forKey:keyDataType:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__49-[GPBInt32EnumDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary getEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary getRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary enumerateKeysAndEnumsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary addRawEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary setRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary removeEnumForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32EnumDictionary setEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary initWithObjects:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary isInitialized]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary deepCopyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBInt32ObjectDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary objectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary setObject:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary removeObjectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32ObjectDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary initWithUInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary enumerateKeysAndUInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBUInt64UInt32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary getUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary setUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary removeUInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary initWithInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary enumerateKeysAndInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBUInt64Int32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary getInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary setInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary removeInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary initWithUInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary enumerateKeysAndUInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBUInt64UInt64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary getUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary setUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary removeUInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64UInt64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary initWithInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary enumerateKeysAndInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBUInt64Int64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary getInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary setInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary removeInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Int64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary initWithBools:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary enumerateKeysAndBoolsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBUInt64BoolDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary getBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary setBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary removeBoolForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64BoolDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary initWithFloats:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary enumerateKeysAndFloatsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBUInt64FloatDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary getFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary setFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary removeFloatForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64FloatDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary initWithDoubles:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary enumerateKeysAndDoublesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBUInt64DoubleDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary getDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary setDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary removeDoubleForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64DoubleDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary initWithValidationFunction:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary initWithValidationFunction:rawValues:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary initWithValidationFunction:capacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary enumerateKeysAndRawValuesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary serializedDataForUnknownValue:forKey:keyDataType:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBUInt64EnumDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary getEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary getRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary enumerateKeysAndEnumsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary addRawEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary setRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary removeEnumForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64EnumDictionary setEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary initWithObjects:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary isInitialized]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary deepCopyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBUInt64ObjectDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary objectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary setObject:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary removeObjectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64ObjectDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary initWithUInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary enumerateKeysAndUInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBInt64UInt32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary getUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary setUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary removeUInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary initWithInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary enumerateKeysAndInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBInt64Int32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary getInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary setInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary removeInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary initWithUInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary enumerateKeysAndUInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBInt64UInt64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary getUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary setUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary removeUInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64UInt64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary initWithInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary enumerateKeysAndInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBInt64Int64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary getInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary setInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary removeInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Int64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary initWithBools:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary enumerateKeysAndBoolsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__49-[GPBInt64BoolDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary getBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary setBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary removeBoolForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64BoolDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary initWithFloats:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary enumerateKeysAndFloatsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBInt64FloatDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary getFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary setFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary removeFloatForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64FloatDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary initWithDoubles:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary enumerateKeysAndDoublesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBInt64DoubleDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary getDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary setDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary removeDoubleForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64DoubleDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary initWithValidationFunction:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary initWithValidationFunction:rawValues:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary initWithValidationFunction:capacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary enumerateKeysAndRawValuesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary serializedDataForUnknownValue:forKey:keyDataType:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__49-[GPBInt64EnumDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary getEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary getRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary enumerateKeysAndEnumsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary addRawEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary setRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary removeEnumForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64EnumDictionary setEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary initWithObjects:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary isInitialized]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary deepCopyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBInt64ObjectDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary objectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary setObject:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary removeObjectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64ObjectDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary initWithUInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary enumerateKeysAndUInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBStringUInt32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary getUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary setUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary removeUInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary initWithInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary enumerateKeysAndInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBStringInt32Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary getInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary setInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary removeInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary initWithUInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary enumerateKeysAndUInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBStringUInt64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary getUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary setUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary removeUInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringUInt64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary initWithInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary enumerateKeysAndInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBStringInt64Dictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary getInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary setInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary removeInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringInt64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary initWithBools:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary enumerateKeysAndBoolsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBStringBoolDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary getBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary setBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary removeBoolForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringBoolDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary initWithFloats:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary enumerateKeysAndFloatsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__51-[GPBStringFloatDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary getFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary setFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary removeFloatForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringFloatDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary initWithDoubles:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary enumerateKeysAndDoublesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__52-[GPBStringDoubleDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary getDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary setDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary removeDoubleForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringDoubleDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary initWithValidationFunction:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary initWithValidationFunction:rawValues:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary initWithValidationFunction:capacity:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary enumerateKeysAndRawValuesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary serializedDataForUnknownValue:forKey:keyDataType:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "__50-[GPBStringEnumDictionary enumerateForTextFormat:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary getEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary getRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary enumerateKeysAndEnumsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary addRawEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary setRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary removeEnumForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBStringEnumDictionary setEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary initWithUInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary getUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary enumerateKeysAndUInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary setUInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary removeUInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary initWithInt32s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary getInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary enumerateKeysAndInt32sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary setInt32:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary removeInt32ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt32Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary initWithUInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary getUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary enumerateKeysAndUInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary setUInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary removeUInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolUInt64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary initWithInt64s:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary getInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary enumerateKeysAndInt64sUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary setInt64:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary removeInt64ForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolInt64Dictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary initWithBools:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary getBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary enumerateKeysAndBoolsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary setBool:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary removeBoolForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolBoolDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary initWithFloats:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary getFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary enumerateKeysAndFloatsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary setFloat:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary removeFloatForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolFloatDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary initWithDoubles:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary getDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary enumerateKeysAndDoublesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary setDouble:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary removeDoubleForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolDoubleDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary initWithObjects:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary objectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary isInitialized]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary deepCopyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary addEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary setObject:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary removeObjectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolObjectDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary initWithValidationFunction:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary initWithValidationFunction:rawValues:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary initWithDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary initWithValidationFunction:capacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary getEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary getRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary enumerateKeysAndRawValuesUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary enumerateKeysAndEnumsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary serializedDataForUnknownValue:forKey:keyDataType:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary computeSerializedSizeAsField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary writeToCodedOutputStream:asField:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary enumerateForTextFormat:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary setGPBGenericValue:forGPBGenericValueKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary addRawEntriesFromDictionary:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary setEnum:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary setRawValue:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary removeEnumForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolEnumDictionary removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary initWithObjects:forKeys:count:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary count]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary objectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary keyEnumerator]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary setObject:forKey:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary removeObjectForKey:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary mutableCopyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary objectForKeyedSubscript:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary setObject:forKeyedSubscript:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary enumerateKeysAndObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedDictionary enumerateKeysAndObjectsWithOptions:usingBlock:]", + "coverage": 0 + } + ] + }, + { + "name": "GPBExtensionInternals.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "DataTypeSize", + "coverage": 0 + }, + { + "name": "ComputePBSerializedSizeNoTagOfObject", + "coverage": 0 + }, + { + "name": "ComputeSerializedSizeIncludingTagOfObject", + "coverage": 0 + }, + { + "name": "ComputeSerializedSizeIncludingTagOfArray", + "coverage": 0 + }, + { + "name": "WriteObjectIncludingTagToCodedOutputStream", + "coverage": 0 + }, + { + "name": "WriteObjectNoTagToCodedOutputStream", + "coverage": 0 + }, + { + "name": "WriteArrayIncludingTagsToCodedOutputStream", + "coverage": 0 + }, + { + "name": "GPBExtensionMergeFromInputStream", + "coverage": 0 + }, + { + "name": "GPBWriteExtensionValueToOutputStream", + "coverage": 0 + }, + { + "name": "GPBComputeExtensionSerializedSizeIncludingTag", + "coverage": 0 + }, + { + "name": "NewSingleValueFromInputStream", + "coverage": 0 + } + ] + }, + { + "name": "SourceContext.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBSourceContextRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GPBSourceContext descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "FieldMask.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBFieldMaskRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GPBFieldMask descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "Empty.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBEmptyRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GPBEmpty descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "GPBUnknownField.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[GPBUnknownField initWithNumber:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField hash]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField writeToOutput:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField serializedSize]", + "coverage": 0 + }, + { + "name": "__33-[GPBUnknownField serializedSize]_block_invoke", + "coverage": 0 + }, + { + "name": "__33-[GPBUnknownField serializedSize]_block_invoke.42", + "coverage": 0 + }, + { + "name": "__33-[GPBUnknownField serializedSize]_block_invoke.47", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField writeAsMessageSetExtensionToOutput:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField serializedSizeAsMessageSetExtension]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField description]", + "coverage": 0 + }, + { + "name": "__30-[GPBUnknownField description]_block_invoke", + "coverage": 0 + }, + { + "name": "__30-[GPBUnknownField description]_block_invoke.64", + "coverage": 0 + }, + { + "name": "__30-[GPBUnknownField description]_block_invoke.70", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField mergeFromField:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField addVarint:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField addFixed32:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField addFixed64:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField addLengthDelimited:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownField addGroup:]", + "coverage": 0 + } + ] + }, + { + "name": "Duration.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBDurationRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GPBDuration descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "Api.pbobjc.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "GPBApiRoot_FileDescriptor", + "coverage": 0 + }, + { + "name": "+[GPBApi descriptor]", + "coverage": 0 + }, + { + "name": "GPBApi_Syntax_RawValue", + "coverage": 0 + }, + { + "name": "SetGPBApi_Syntax_RawValue", + "coverage": 0 + }, + { + "name": "+[GPBMethod descriptor]", + "coverage": 0 + }, + { + "name": "GPBMethod_Syntax_RawValue", + "coverage": 0 + }, + { + "name": "SetGPBMethod_Syntax_RawValue", + "coverage": 0 + }, + { + "name": "+[GPBMixin descriptor]", + "coverage": 0 + } + ] + }, + { + "name": "GPBWellKnownTypes.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "TimeIntervalFromSecondsAndNanos", + "coverage": 0 + }, + { + "name": "SecondsAndNanosFromTimeInterval", + "coverage": 0 + }, + { + "name": "BuildTypeURL", + "coverage": 0 + }, + { + "name": "ParseTypeFromURL", + "coverage": 0 + }, + { + "name": "-[GPBTimestamp(GBPWellKnownTypes) initWithDate:]", + "coverage": 0 + }, + { + "name": "-[GPBTimestamp(GBPWellKnownTypes) initWithTimeIntervalSince1970:]", + "coverage": 0 + }, + { + "name": "-[GPBTimestamp(GBPWellKnownTypes) date]", + "coverage": 0 + }, + { + "name": "-[GPBTimestamp(GBPWellKnownTypes) setDate:]", + "coverage": 0 + }, + { + "name": "-[GPBTimestamp(GBPWellKnownTypes) timeIntervalSince1970]", + "coverage": 0 + }, + { + "name": "-[GPBTimestamp(GBPWellKnownTypes) setTimeIntervalSince1970:]", + "coverage": 0 + }, + { + "name": "-[GPBDuration(GBPWellKnownTypes) initWithTimeInterval:]", + "coverage": 0 + }, + { + "name": "-[GPBDuration(GBPWellKnownTypes) initWithTimeIntervalSince1970:]", + "coverage": 0 + }, + { + "name": "-[GPBDuration(GBPWellKnownTypes) timeInterval]", + "coverage": 0 + }, + { + "name": "-[GPBDuration(GBPWellKnownTypes) setTimeInterval:]", + "coverage": 0 + }, + { + "name": "-[GPBDuration(GBPWellKnownTypes) timeIntervalSince1970]", + "coverage": 0 + }, + { + "name": "-[GPBDuration(GBPWellKnownTypes) setTimeIntervalSince1970:]", + "coverage": 0 + }, + { + "name": "+[GPBAny(GBPWellKnownTypes) anyWithMessage:error:]", + "coverage": 0 + }, + { + "name": "+[GPBAny(GBPWellKnownTypes) anyWithMessage:typeURLPrefix:error:]", + "coverage": 0 + }, + { + "name": "-[GPBAny(GBPWellKnownTypes) initWithMessage:error:]", + "coverage": 0 + }, + { + "name": "-[GPBAny(GBPWellKnownTypes) initWithMessage:typeURLPrefix:error:]", + "coverage": 0 + }, + { + "name": "-[GPBAny(GBPWellKnownTypes) packWithMessage:error:]", + "coverage": 0 + }, + { + "name": "-[GPBAny(GBPWellKnownTypes) packWithMessage:typeURLPrefix:error:]", + "coverage": 0 + }, + { + "name": "-[GPBAny(GBPWellKnownTypes) unpackMessageClass:error:]", + "coverage": 0 + } + ] + }, + { + "name": "GPBArray.m", + "coverage": 0.01252723311546841, + "type": "objc", + "functions": [ + { + "name": "ArrayDefault_IsValidValue", + "coverage": 0 + }, + { + "name": "+[GPBInt32Array array]", + "coverage": 0 + }, + { + "name": "+[GPBInt32Array arrayWithValue:]", + "coverage": 0 + }, + { + "name": "+[GPBInt32Array arrayWithValueArray:]", + "coverage": 0 + }, + { + "name": "+[GPBInt32Array arrayWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array init]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array initWithValueArray:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array initWithValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array description]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array enumerateValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array enumerateValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array valueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array internalResizeToCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array addValue:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array addValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array insertValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array replaceValueAtIndex:withValue:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array addValuesFromArray:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array removeValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt32Array exchangeValueAtIndex:withValueAtIndex:]", + "coverage": 0 + }, + { + "name": "+[GPBUInt32Array array]", + "coverage": 0 + }, + { + "name": "+[GPBUInt32Array arrayWithValue:]", + "coverage": 0 + }, + { + "name": "+[GPBUInt32Array arrayWithValueArray:]", + "coverage": 0 + }, + { + "name": "+[GPBUInt32Array arrayWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array initWithValueArray:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array initWithValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array enumerateValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array enumerateValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array valueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array internalResizeToCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array addValue:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array addValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array insertValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array replaceValueAtIndex:withValue:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array addValuesFromArray:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array removeValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt32Array exchangeValueAtIndex:withValueAtIndex:]", + "coverage": 0 + }, + { + "name": "+[GPBInt64Array array]", + "coverage": 0 + }, + { + "name": "+[GPBInt64Array arrayWithValue:]", + "coverage": 0 + }, + { + "name": "+[GPBInt64Array arrayWithValueArray:]", + "coverage": 0 + }, + { + "name": "+[GPBInt64Array arrayWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array init]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array initWithValueArray:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array initWithValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array hash]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array description]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array enumerateValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array enumerateValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array valueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array internalResizeToCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array addValue:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array addValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array insertValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array replaceValueAtIndex:withValue:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array addValuesFromArray:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array removeValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBInt64Array exchangeValueAtIndex:withValueAtIndex:]", + "coverage": 0 + }, + { + "name": "+[GPBUInt64Array array]", + "coverage": 0 + }, + { + "name": "+[GPBUInt64Array arrayWithValue:]", + "coverage": 0 + }, + { + "name": "+[GPBUInt64Array arrayWithValueArray:]", + "coverage": 0 + }, + { + "name": "+[GPBUInt64Array arrayWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array init]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array initWithValueArray:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array initWithValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array hash]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array description]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array enumerateValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array enumerateValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array valueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array internalResizeToCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array addValue:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array addValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array insertValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array replaceValueAtIndex:withValue:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array addValuesFromArray:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array removeValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBUInt64Array exchangeValueAtIndex:withValueAtIndex:]", + "coverage": 0 + }, + { + "name": "+[GPBFloatArray array]", + "coverage": 0 + }, + { + "name": "+[GPBFloatArray arrayWithValue:]", + "coverage": 0 + }, + { + "name": "+[GPBFloatArray arrayWithValueArray:]", + "coverage": 0 + }, + { + "name": "+[GPBFloatArray arrayWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray init]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray initWithValueArray:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray initWithValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray hash]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray description]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray enumerateValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray enumerateValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray valueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray internalResizeToCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray addValue:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray addValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray insertValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray replaceValueAtIndex:withValue:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray addValuesFromArray:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray removeValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBFloatArray exchangeValueAtIndex:withValueAtIndex:]", + "coverage": 0 + }, + { + "name": "+[GPBDoubleArray array]", + "coverage": 0 + }, + { + "name": "+[GPBDoubleArray arrayWithValue:]", + "coverage": 0 + }, + { + "name": "+[GPBDoubleArray arrayWithValueArray:]", + "coverage": 0 + }, + { + "name": "+[GPBDoubleArray arrayWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray init]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray initWithValueArray:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray initWithValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray hash]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray description]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray enumerateValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray enumerateValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray valueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray internalResizeToCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray addValue:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray addValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray insertValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray replaceValueAtIndex:withValue:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray addValuesFromArray:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray removeValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBDoubleArray exchangeValueAtIndex:withValueAtIndex:]", + "coverage": 0 + }, + { + "name": "+[GPBBoolArray array]", + "coverage": 0 + }, + { + "name": "+[GPBBoolArray arrayWithValue:]", + "coverage": 0 + }, + { + "name": "+[GPBBoolArray arrayWithValueArray:]", + "coverage": 0 + }, + { + "name": "+[GPBBoolArray arrayWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray init]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray initWithValueArray:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray initWithValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray initWithCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray hash]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray description]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray enumerateValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray enumerateValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray valueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray internalResizeToCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray addValue:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray addValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray insertValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray replaceValueAtIndex:withValue:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray addValuesFromArray:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray removeValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBBoolArray exchangeValueAtIndex:withValueAtIndex:]", + "coverage": 0 + }, + { + "name": "+[GPBEnumArray array]", + "coverage": 0 + }, + { + "name": "+[GPBEnumArray arrayWithValidationFunction:]", + "coverage": 0 + }, + { + "name": "+[GPBEnumArray arrayWithValidationFunction:rawValue:]", + "coverage": 0 + }, + { + "name": "+[GPBEnumArray arrayWithValueArray:]", + "coverage": 0 + }, + { + "name": "+[GPBEnumArray arrayWithValidationFunction:capacity:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray init]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray initWithValueArray:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray initWithValidationFunction:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray initWithValidationFunction:rawValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray initWithValidationFunction:capacity:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray hash]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray description]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray enumerateRawValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray enumerateRawValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray valueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray rawValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray enumerateValuesWithBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray enumerateValuesWithOptions:usingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray internalResizeToCapacity:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray addRawValue:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray addRawValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray insertRawValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray replaceValueAtIndex:withRawValue:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray addRawValuesFromArray:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray removeValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray removeAll]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray exchangeValueAtIndex:withValueAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray addValue:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray addValues:count:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray insertValue:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumArray replaceValueAtIndex:withValue:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray dealloc]", + "coverage": 1 + }, + { + "name": "-[GPBAutocreatedArray count]", + "coverage": 1 + }, + { + "name": "-[GPBAutocreatedArray objectAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray insertObject:atIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray removeObject:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray removeObjectAtIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray addObject:]", + "coverage": 1 + }, + { + "name": "-[GPBAutocreatedArray removeLastObject]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray replaceObjectAtIndex:withObject:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray mutableCopyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray countByEnumeratingWithState:objects:count:]", + "coverage": 1 + }, + { + "name": "-[GPBAutocreatedArray enumerateObjectsUsingBlock:]", + "coverage": 0 + }, + { + "name": "-[GPBAutocreatedArray enumerateObjectsWithOptions:usingBlock:]", + "coverage": 0 + } + ] + }, + { + "name": "GPBExtensionRegistry.m", + "coverage": 0.06666666666666667, + "type": "objc", + "functions": [ + { + "name": "-[GPBExtensionRegistry init]", + "coverage": 1 + }, + { + "name": "-[GPBExtensionRegistry dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionRegistry copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionRegistry addExtension:]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionRegistry extensionForDescriptor:fieldNumber:]", + "coverage": 0 + }, + { + "name": "CopyKeyValue", + "coverage": 0 + }, + { + "name": "-[GPBExtensionRegistry addExtensions:]", + "coverage": 0 + }, + { + "name": "__38-[GPBExtensionRegistry addExtensions:]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "GPBUnknownFieldSet.m", + "coverage": 0.11974110032362459, + "type": "objc", + "functions": [ + { + "name": "checkNumber", + "coverage": 0 + }, + { + "name": "CopyWorker", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet dealloc]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[GPBUnknownFieldSet isEqual:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet hash]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet hasField:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet getField:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet countOfFields]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet sortedFields]", + "coverage": 0 + }, + { + "name": "__34-[GPBUnknownFieldSet sortedFields]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet writeToCodedOutputStream:]", + "coverage": 0 + }, + { + "name": "__47-[GPBUnknownFieldSet writeToCodedOutputStream:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet description]", + "coverage": 0 + }, + { + "name": "GPBUnknownFieldSetSerializedSize", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet serializedSize]", + "coverage": 0 + }, + { + "name": "GPBUnknownFieldSetWriteAsMessageSetTo", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet writeAsMessageSetTo:]", + "coverage": 0 + }, + { + "name": "GPBUnknownFieldSetSerializedSizeAsMessageSet", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet serializedSizeAsMessageSet]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet data]", + "coverage": 0 + }, + { + "name": "+[GPBUnknownFieldSet isFieldTag:]", + "coverage": 1 + }, + { + "name": "-[GPBUnknownFieldSet addField:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet mutableFieldForNumber:create:]", + "coverage": 0 + }, + { + "name": "GPBUnknownFieldSetMergeUnknownFields", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet mergeUnknownFields:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet mergeFromData:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet mergeVarintField:value:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet mergeFieldFrom:input:]", + "coverage": 0.6153846153846154 + }, + { + "name": "-[GPBUnknownFieldSet mergeMessageSetMessage:data:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet addUnknownMapEntry:value:]", + "coverage": 0 + }, + { + "name": "-[GPBUnknownFieldSet mergeFromCodedInputStream:]", + "coverage": 0.75 + }, + { + "name": "-[GPBUnknownFieldSet getTags:]", + "coverage": 0 + } + ] + }, + { + "name": "GPBUtilities.m", + "coverage": 0.15045674368619022, + "type": "objc", + "functions": [ + { + "name": "GPBEmptyNSData", + "coverage": 1 + }, + { + "name": "__GPBEmptyNSData_block_invoke", + "coverage": 1 + }, + { + "name": "GPBMessageDropUnknownFieldsRecursively", + "coverage": 0 + }, + { + "name": "__GPBMessageDropUnknownFieldsRecursively_block_invoke", + "coverage": 0 + }, + { + "name": "__GPBMessageDropUnknownFieldsRecursively_block_invoke.32", + "coverage": 0 + }, + { + "name": "__GPBMessageDropUnknownFieldsRecursively_block_invoke.37", + "coverage": 0 + }, + { + "name": "__GPBMessageDropUnknownFieldsRecursively_block_invoke.42", + "coverage": 0 + }, + { + "name": "__GPBMessageDropUnknownFieldsRecursively_block_invoke.47", + "coverage": 0 + }, + { + "name": "__GPBMessageDropUnknownFieldsRecursively_block_invoke.52", + "coverage": 0 + }, + { + "name": "GPBCheckRuntimeVersionSupport", + "coverage": 0.36363636363636365 + }, + { + "name": "GPBCheckRuntimeVersionInternal", + "coverage": 0 + }, + { + "name": "GPBMessageHasFieldNumberSet", + "coverage": 0 + }, + { + "name": "GPBMessageHasFieldSet", + "coverage": 0 + }, + { + "name": "GPBClearMessageField", + "coverage": 0 + }, + { + "name": "GPBGetHasIvar", + "coverage": 0.8235294117647058 + }, + { + "name": "GPBGetHasOneof", + "coverage": 0 + }, + { + "name": "GPBSetHasIvar", + "coverage": 0.7058823529411765 + }, + { + "name": "GPBMaybeClearOneof", + "coverage": 0 + }, + { + "name": "GPBSetAutocreatedRetainedObjectIvarWithField", + "coverage": 1 + }, + { + "name": "GPBClearAutocreatedMessageIvarWithField", + "coverage": 0.8181818181818182 + }, + { + "name": "GPBSetObjectIvarWithField", + "coverage": 0 + }, + { + "name": "GPBSetCopyObjectIvarWithField", + "coverage": 0 + }, + { + "name": "GPBSetObjectIvarWithFieldInternal", + "coverage": 1 + }, + { + "name": "GPBSetRetainedObjectIvarWithFieldInternal", + "coverage": 0.2892561983471074 + }, + { + "name": "GPBGetObjectIvarWithFieldNoAutocreate", + "coverage": 0.75 + }, + { + "name": "GPBGetMessageEnumField", + "coverage": 0 + }, + { + "name": "GPBGetEnumIvarWithFieldInternal", + "coverage": 0 + }, + { + "name": "GPBSetMessageEnumField", + "coverage": 0 + }, + { + "name": "GPBSetEnumIvarWithFieldInternal", + "coverage": 0.75 + }, + { + "name": "GPBGetMessageRawEnumField", + "coverage": 0 + }, + { + "name": "GPBSetMessageRawEnumField", + "coverage": 0 + }, + { + "name": "GPBGetMessageBoolField", + "coverage": 1 + }, + { + "name": "GPBSetMessageBoolField", + "coverage": 0 + }, + { + "name": "GPBSetBoolIvarWithFieldInternal", + "coverage": 0.9285714285714286 + }, + { + "name": "GPBGetMessageInt32Field", + "coverage": 0.8823529411764706 + }, + { + "name": "GPBSetMessageInt32Field", + "coverage": 0 + }, + { + "name": "GPBSetInt32IvarWithFieldInternal", + "coverage": 0.896551724137931 + }, + { + "name": "GPBGetMessageUInt32Field", + "coverage": 0 + }, + { + "name": "GPBSetMessageUInt32Field", + "coverage": 0 + }, + { + "name": "GPBSetUInt32IvarWithFieldInternal", + "coverage": 0 + }, + { + "name": "GPBGetMessageInt64Field", + "coverage": 1 + }, + { + "name": "GPBSetMessageInt64Field", + "coverage": 0 + }, + { + "name": "GPBSetInt64IvarWithFieldInternal", + "coverage": 0.896551724137931 + }, + { + "name": "GPBGetMessageUInt64Field", + "coverage": 0 + }, + { + "name": "GPBSetMessageUInt64Field", + "coverage": 0 + }, + { + "name": "GPBSetUInt64IvarWithFieldInternal", + "coverage": 0 + }, + { + "name": "GPBGetMessageFloatField", + "coverage": 0 + }, + { + "name": "GPBSetMessageFloatField", + "coverage": 0 + }, + { + "name": "GPBSetFloatIvarWithFieldInternal", + "coverage": 0 + }, + { + "name": "GPBGetMessageDoubleField", + "coverage": 0 + }, + { + "name": "GPBSetMessageDoubleField", + "coverage": 0 + }, + { + "name": "GPBSetDoubleIvarWithFieldInternal", + "coverage": 0 + }, + { + "name": "GPBGetMessageStringField", + "coverage": 0 + }, + { + "name": "GPBSetMessageStringField", + "coverage": 0 + }, + { + "name": "GPBGetMessageBytesField", + "coverage": 0 + }, + { + "name": "GPBSetMessageBytesField", + "coverage": 0 + }, + { + "name": "GPBGetMessageMessageField", + "coverage": 0 + }, + { + "name": "GPBSetMessageMessageField", + "coverage": 0 + }, + { + "name": "GPBGetMessageGroupField", + "coverage": 0 + }, + { + "name": "GPBSetMessageGroupField", + "coverage": 0 + }, + { + "name": "GPBSetMessageRepeatedField", + "coverage": 0 + }, + { + "name": "BaseDataType", + "coverage": 0.75 + }, + { + "name": "DataTypesEquivalent", + "coverage": 1 + }, + { + "name": "TypeToString", + "coverage": 0 + }, + { + "name": "GPBSetMessageMapField", + "coverage": 0 + }, + { + "name": "GPBMessageEncodingForSelector", + "coverage": 1 + }, + { + "name": "AppendStringEscaped", + "coverage": 0 + }, + { + "name": "AppendBufferAsString", + "coverage": 0 + }, + { + "name": "AppendTextFormatForMapMessageField", + "coverage": 0 + }, + { + "name": "__AppendTextFormatForMapMessageField_block_invoke", + "coverage": 0 + }, + { + "name": "__AppendTextFormatForMapMessageField_block_invoke.408", + "coverage": 0 + }, + { + "name": "AppendTextFormatForMessageField", + "coverage": 0 + }, + { + "name": "AppendTextFormatForMessageExtensionRange", + "coverage": 0 + }, + { + "name": "AppendTextFormatForMessage", + "coverage": 0 + }, + { + "name": "GPBTextFormatForMessage", + "coverage": 0 + }, + { + "name": "GPBTextFormatForUnknownFieldSet", + "coverage": 0 + }, + { + "name": "Definition at 2012:76", + "coverage": 0 + }, + { + "name": "ReadRawByteFromData", + "coverage": 0 + }, + { + "name": "ReadRawVarint32FromData", + "coverage": 0 + }, + { + "name": "GPBDecodeTextFormatName", + "coverage": 0 + }, + { + "name": "GPBClassHasSel", + "coverage": 0 + } + ] + }, + { + "name": "GPBRootObject.m", + "coverage": 0.19254658385093168, + "type": "objc", + "functions": [ + { + "name": "jenkins_one_at_a_time_hash", + "coverage": 0 + }, + { + "name": "GPBRootExtensionKeyRetain", + "coverage": 0 + }, + { + "name": "GPBRootExtensionKeyRelease", + "coverage": 0 + }, + { + "name": "GPBRootExtensionCopyKeyDescription", + "coverage": 0 + }, + { + "name": "GPBRootExtensionKeyEqual", + "coverage": 0 + }, + { + "name": "GPBRootExtensionKeyHash", + "coverage": 0 + }, + { + "name": "+[GPBRootObject initialize]", + "coverage": 1 + }, + { + "name": "+[GPBRootObject extensionRegistry]", + "coverage": 1 + }, + { + "name": "+[GPBRootObject globallyRegisterExtension:]", + "coverage": 0 + }, + { + "name": "ExtensionForName", + "coverage": 0 + }, + { + "name": "GPBResolveExtensionClassMethod", + "coverage": 0 + }, + { + "name": "__GPBResolveExtensionClassMethod_block_invoke", + "coverage": 0 + }, + { + "name": "+[GPBRootObject resolveClassMethod:]", + "coverage": 0 + } + ] + }, + { + "name": "GPBCodedOutputStream.m", + "coverage": 0.21133525456292027, + "type": "objc", + "functions": [ + { + "name": "GPBRefreshBuffer", + "coverage": 0 + }, + { + "name": "GPBWriteRawByte", + "coverage": 0.6666666666666666 + }, + { + "name": "GPBWriteRawVarint32", + "coverage": 1 + }, + { + "name": "GPBWriteRawVarint64", + "coverage": 1 + }, + { + "name": "GPBWriteInt32NoTag", + "coverage": 1 + }, + { + "name": "GPBWriteUInt32", + "coverage": 0 + }, + { + "name": "GPBWriteTagWithFormat", + "coverage": 1 + }, + { + "name": "GPBWriteRawLittleEndian32", + "coverage": 0 + }, + { + "name": "GPBWriteRawLittleEndian64", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream dealloc]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream initWithOutputStream:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream initWithData:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream initWithOutputStream:data:]", + "coverage": 1 + }, + { + "name": "+[GPBCodedOutputStream streamWithOutputStream:]", + "coverage": 0 + }, + { + "name": "+[GPBCodedOutputStream streamWithData:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeDoubleNoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeDouble:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeFloatNoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeFloat:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUInt64NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUInt64:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeInt64NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeInt64:value:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeInt32NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeInt32:value:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeFixed64NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeFixed64:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeFixed32NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeFixed32:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeBoolNoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeBool:value:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeStringNoTag:]", + "coverage": 0.7872340425531915 + }, + { + "name": "-[GPBCodedOutputStream writeString:value:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeGroupNoTag:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeGroup:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUnknownGroupNoTag:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUnknownGroup:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeMessageNoTag:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeMessage:value:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeBytesNoTag:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeBytes:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUInt32NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUInt32:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeEnumNoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeEnum:value:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeSFixed32NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSFixed32:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSFixed64NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSFixed64:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSInt32NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSInt32:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSInt64NoTag:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSInt64:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeDoubleArray:values:tag:]", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeDoubleArray:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeDoubleArray:values:tag:]_block_invoke.73", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeDoubleArray:values:tag:]_block_invoke.79", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeFloatArray:values:tag:]", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeFloatArray:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeFloatArray:values:tag:]_block_invoke.89", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeFloatArray:values:tag:]_block_invoke.95", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUInt64Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeUInt64Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeUInt64Array:values:tag:]_block_invoke.105", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeUInt64Array:values:tag:]_block_invoke.111", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeInt64Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeInt64Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeInt64Array:values:tag:]_block_invoke.121", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeInt64Array:values:tag:]_block_invoke.127", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeInt32Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeInt32Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeInt32Array:values:tag:]_block_invoke.137", + "coverage": 0 + }, + { + "name": "__51-[GPBCodedOutputStream writeInt32Array:values:tag:]_block_invoke.143", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUInt32Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeUInt32Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeUInt32Array:values:tag:]_block_invoke.153", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeUInt32Array:values:tag:]_block_invoke.159", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeFixed64Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__53-[GPBCodedOutputStream writeFixed64Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__53-[GPBCodedOutputStream writeFixed64Array:values:tag:]_block_invoke.168", + "coverage": 0 + }, + { + "name": "__53-[GPBCodedOutputStream writeFixed64Array:values:tag:]_block_invoke.174", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeFixed32Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__53-[GPBCodedOutputStream writeFixed32Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__53-[GPBCodedOutputStream writeFixed32Array:values:tag:]_block_invoke.183", + "coverage": 0 + }, + { + "name": "__53-[GPBCodedOutputStream writeFixed32Array:values:tag:]_block_invoke.189", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSInt32Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeSInt32Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeSInt32Array:values:tag:]_block_invoke.198", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeSInt32Array:values:tag:]_block_invoke.204", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSInt64Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeSInt64Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeSInt64Array:values:tag:]_block_invoke.213", + "coverage": 0 + }, + { + "name": "__52-[GPBCodedOutputStream writeSInt64Array:values:tag:]_block_invoke.219", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSFixed64Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__54-[GPBCodedOutputStream writeSFixed64Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__54-[GPBCodedOutputStream writeSFixed64Array:values:tag:]_block_invoke.228", + "coverage": 0 + }, + { + "name": "__54-[GPBCodedOutputStream writeSFixed64Array:values:tag:]_block_invoke.234", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeSFixed32Array:values:tag:]", + "coverage": 0 + }, + { + "name": "__54-[GPBCodedOutputStream writeSFixed32Array:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__54-[GPBCodedOutputStream writeSFixed32Array:values:tag:]_block_invoke.243", + "coverage": 0 + }, + { + "name": "__54-[GPBCodedOutputStream writeSFixed32Array:values:tag:]_block_invoke.249", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeBoolArray:values:tag:]", + "coverage": 0 + }, + { + "name": "__50-[GPBCodedOutputStream writeBoolArray:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__50-[GPBCodedOutputStream writeBoolArray:values:tag:]_block_invoke.259", + "coverage": 0 + }, + { + "name": "__50-[GPBCodedOutputStream writeBoolArray:values:tag:]_block_invoke.265", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeEnumArray:values:tag:]", + "coverage": 0 + }, + { + "name": "__50-[GPBCodedOutputStream writeEnumArray:values:tag:]_block_invoke", + "coverage": 0 + }, + { + "name": "__50-[GPBCodedOutputStream writeEnumArray:values:tag:]_block_invoke.276", + "coverage": 0 + }, + { + "name": "__50-[GPBCodedOutputStream writeEnumArray:values:tag:]_block_invoke.282", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeStringArray:values:]", + "coverage": 0.6 + }, + { + "name": "-[GPBCodedOutputStream writeMessageArray:values:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeBytesArray:values:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeGroupArray:values:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeUnknownGroupArray:values:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeMessageSetExtension:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeRawMessageSetExtension:value:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream flush]", + "coverage": 0.6 + }, + { + "name": "-[GPBCodedOutputStream writeRawByte:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeRawData:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeRawPtr:offset:length:]", + "coverage": 0.28205128205128205 + }, + { + "name": "-[GPBCodedOutputStream writeTag:format:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeRawVarint32:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedOutputStream writeRawVarintSizeTAs32:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeRawVarint64:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeRawLittleEndian32:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedOutputStream writeRawLittleEndian64:]", + "coverage": 0 + }, + { + "name": "GPBComputeDoubleSizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeFloatSizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeUInt64SizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeInt64SizeNoTag", + "coverage": 1 + }, + { + "name": "GPBComputeInt32SizeNoTag", + "coverage": 1 + }, + { + "name": "GPBComputeSizeTSizeAsInt32NoTag", + "coverage": 0 + }, + { + "name": "GPBComputeFixed64SizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeFixed32SizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeBoolSizeNoTag", + "coverage": 1 + }, + { + "name": "GPBComputeStringSizeNoTag", + "coverage": 1 + }, + { + "name": "GPBComputeGroupSizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeUnknownGroupSizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeMessageSizeNoTag", + "coverage": 1 + }, + { + "name": "GPBComputeBytesSizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeUInt32SizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeEnumSizeNoTag", + "coverage": 1 + }, + { + "name": "GPBComputeSFixed32SizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeSFixed64SizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeSInt32SizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeSInt64SizeNoTag", + "coverage": 0 + }, + { + "name": "GPBComputeDoubleSize", + "coverage": 0 + }, + { + "name": "GPBComputeFloatSize", + "coverage": 0 + }, + { + "name": "GPBComputeUInt64Size", + "coverage": 0 + }, + { + "name": "GPBComputeInt64Size", + "coverage": 1 + }, + { + "name": "GPBComputeInt32Size", + "coverage": 1 + }, + { + "name": "GPBComputeFixed64Size", + "coverage": 0 + }, + { + "name": "GPBComputeFixed32Size", + "coverage": 0 + }, + { + "name": "GPBComputeBoolSize", + "coverage": 1 + }, + { + "name": "GPBComputeStringSize", + "coverage": 1 + }, + { + "name": "GPBComputeGroupSize", + "coverage": 0 + }, + { + "name": "GPBComputeUnknownGroupSize", + "coverage": 0 + }, + { + "name": "GPBComputeMessageSize", + "coverage": 0 + }, + { + "name": "GPBComputeBytesSize", + "coverage": 0 + }, + { + "name": "GPBComputeUInt32Size", + "coverage": 0 + }, + { + "name": "GPBComputeEnumSize", + "coverage": 1 + }, + { + "name": "GPBComputeSFixed32Size", + "coverage": 0 + }, + { + "name": "GPBComputeSFixed64Size", + "coverage": 0 + }, + { + "name": "GPBComputeSInt32Size", + "coverage": 0 + }, + { + "name": "GPBComputeSInt64Size", + "coverage": 0 + }, + { + "name": "GPBComputeMessageSetExtensionSize", + "coverage": 0 + }, + { + "name": "GPBComputeRawMessageSetExtensionSize", + "coverage": 0 + }, + { + "name": "GPBComputeTagSize", + "coverage": 1 + }, + { + "name": "GPBComputeWireFormatTagSize", + "coverage": 0 + }, + { + "name": "GPBComputeRawVarint32Size", + "coverage": 0.625 + }, + { + "name": "GPBComputeRawVarint32SizeForInteger", + "coverage": 1 + }, + { + "name": "GPBComputeRawVarint64Size", + "coverage": 0.5 + } + ] + }, + { + "name": "GPBDescriptor.m", + "coverage": 0.2474460839954597, + "type": "objc", + "functions": [ + { + "name": "SelFromStrings", + "coverage": 0.9375 + }, + { + "name": "NewFieldsArrayForHasIndex", + "coverage": 0 + }, + { + "name": "+[GPBDescriptor allocDescriptorForClass:rootClass:file:fields:fieldCount:storageSize:flags:]", + "coverage": 0.9487179487179487 + }, + { + "name": "-[GPBDescriptor initWithClass:file:fields:storageSize:wireFormat:]", + "coverage": 1 + }, + { + "name": "-[GPBDescriptor dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor setupOneofs:count:firstHasIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor setupExtraTextInfo:]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor setupExtensionRanges:count:]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor setupContainingMessageClassName:]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor setupMessageClassNameSuffix:]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor name]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor containingType]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor fullName]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor fieldWithNumber:]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor fieldWithName:]", + "coverage": 0 + }, + { + "name": "-[GPBDescriptor oneofWithName:]", + "coverage": 0 + }, + { + "name": "-[GPBFileDescriptor initWithPackage:objcPrefix:syntax:]", + "coverage": 1 + }, + { + "name": "-[GPBFileDescriptor initWithPackage:syntax:]", + "coverage": 1 + }, + { + "name": "-[GPBFileDescriptor dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBOneofDescriptor initWithName:fields:]", + "coverage": 0 + }, + { + "name": "-[GPBOneofDescriptor dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBOneofDescriptor name]", + "coverage": 0 + }, + { + "name": "-[GPBOneofDescriptor fieldWithNumber:]", + "coverage": 0 + }, + { + "name": "-[GPBOneofDescriptor fieldWithName:]", + "coverage": 0 + }, + { + "name": "GPBFieldTag", + "coverage": 0.8333333333333334 + }, + { + "name": "GPBFieldAlternateTag", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor init]", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor initWithFieldDescription:includesDefault:syntax:]", + "coverage": 0.7397260273972602 + }, + { + "name": "-[GPBFieldDescriptor dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor dataType]", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor hasDefaultValue]", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor number]", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor name]", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor isRequired]", + "coverage": 1 + }, + { + "name": "-[GPBFieldDescriptor isOptional]", + "coverage": 1 + }, + { + "name": "-[GPBFieldDescriptor fieldType]", + "coverage": 0.9 + }, + { + "name": "-[GPBFieldDescriptor mapKeyDataType]", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor isPackable]", + "coverage": 1 + }, + { + "name": "-[GPBFieldDescriptor isValidEnumValue:]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[GPBFieldDescriptor enumDescriptor]", + "coverage": 0 + }, + { + "name": "-[GPBFieldDescriptor defaultValue]", + "coverage": 1 + }, + { + "name": "-[GPBFieldDescriptor textFormatName]", + "coverage": 0 + }, + { + "name": "+[GPBEnumDescriptor allocDescriptorForName:valueNames:values:count:enumVerifier:]", + "coverage": 1 + }, + { + "name": "+[GPBEnumDescriptor allocDescriptorForName:valueNames:values:count:enumVerifier:extraTextFormatInfo:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor initWithName:valueNames:values:count:enumVerifier:]", + "coverage": 1 + }, + { + "name": "-[GPBEnumDescriptor dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor calcValueNameOffsets]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor enumNameForValue:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor getValue:forEnumName:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor getValue:forEnumTextFormatName:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor textFormatNameForValue:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor enumNameCount]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor getEnumNameForIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBEnumDescriptor getEnumTextFormatNameForIndex:]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor initWithExtensionDescription:]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor dealloc]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor singletonName]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor singletonNameC]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor fieldNumber]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor dataType]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor wireType]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor alternateWireType]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor isRepeated]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor isPackable]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor msgClass]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor enumDescriptor]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor defaultValue]", + "coverage": 0 + }, + { + "name": "-[GPBExtensionDescriptor compareByFieldNumber:]", + "coverage": 0 + } + ] + }, + { + "name": "GPBMessage.m", + "coverage": 0.3391755923401493, + "type": "objc", + "functions": [ + { + "name": "MessageError", + "coverage": 0 + }, + { + "name": "ErrorFromException", + "coverage": 0.5238095238095238 + }, + { + "name": "CheckExtension", + "coverage": 0 + }, + { + "name": "CloneExtensionMap", + "coverage": 0 + }, + { + "name": "CreateArrayForField", + "coverage": 0.4426229508196721 + }, + { + "name": "CreateMapForField", + "coverage": 0 + }, + { + "name": "GetOrCreateArrayIvarWithField", + "coverage": 1 + }, + { + "name": "GetArrayIvarWithField", + "coverage": 1 + }, + { + "name": "GetOrCreateMapIvarWithField", + "coverage": 0 + }, + { + "name": "GetMapIvarWithField", + "coverage": 0 + }, + { + "name": "GPBCreateMessageWithAutocreator", + "coverage": 0 + }, + { + "name": "CreateMessageWithAutocreatorForExtension", + "coverage": 0 + }, + { + "name": "GPBWasMessageAutocreatedBy", + "coverage": 0 + }, + { + "name": "GPBBecomeVisibleToAutocreator", + "coverage": 0.375 + }, + { + "name": "GPBAutocreatedArrayModified", + "coverage": 0.8095238095238095 + }, + { + "name": "GPBAutocreatedDictionaryModified", + "coverage": 0 + }, + { + "name": "GPBClearMessageAutocreator", + "coverage": 0.13333333333333333 + }, + { + "name": "GPBPrepareReadOnlySemaphore", + "coverage": 0.875 + }, + { + "name": "GetOrMakeUnknownFields", + "coverage": 1 + }, + { + "name": "+[GPBMessage initialize]", + "coverage": 1 + }, + { + "name": "+[GPBMessage allocWithZone:]", + "coverage": 1 + }, + { + "name": "+[GPBMessage alloc]", + "coverage": 1 + }, + { + "name": "+[GPBMessage descriptor]", + "coverage": 1 + }, + { + "name": "+[GPBMessage message]", + "coverage": 0 + }, + { + "name": "-[GPBMessage init]", + "coverage": 1 + }, + { + "name": "-[GPBMessage initWithData:error:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage initWithData:extensionRegistry:error:]", + "coverage": 0.7777777777777778 + }, + { + "name": "-[GPBMessage initWithCodedInputStream:extensionRegistry:error:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage dealloc]", + "coverage": 1 + }, + { + "name": "-[GPBMessage copyFieldsInto:zone:descriptor:]", + "coverage": 0 + }, + { + "name": "__45-[GPBMessage copyFieldsInto:zone:descriptor:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBMessage copyWithZone:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage clear]", + "coverage": 0 + }, + { + "name": "-[GPBMessage internalClear:]", + "coverage": 0.620253164556962 + }, + { + "name": "-[GPBMessage isInitialized]", + "coverage": 0.6794871794871795 + }, + { + "name": "__27-[GPBMessage isInitialized]_block_invoke", + "coverage": 0 + }, + { + "name": "-[GPBMessage descriptor]", + "coverage": 1 + }, + { + "name": "-[GPBMessage data]", + "coverage": 1 + }, + { + "name": "-[GPBMessage delimitedData]", + "coverage": 0 + }, + { + "name": "-[GPBMessage writeToOutputStream:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage writeToCodedOutputStream:]", + "coverage": 0.7142857142857143 + }, + { + "name": "-[GPBMessage writeDelimitedToOutputStream:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage writeDelimitedToCodedOutputStream:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage writeField:toCodedOutputStream:]", + "coverage": 0.4308755760368664 + }, + { + "name": "-[GPBMessage getExtension:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage getExistingExtension:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage hasExtension:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage extensionsCurrentlySet]", + "coverage": 0 + }, + { + "name": "-[GPBMessage writeExtensionsToCodedOutputStream:range:sortedExtensions:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage setExtension:value:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage addExtension:value:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage setExtension:index:value:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage clearExtension:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage mergeFromData:extensionRegistry:]", + "coverage": 1 + }, + { + "name": "-[GPBMessage mergeDelimitedFromCodedInputStream:extensionRegistry:]", + "coverage": 0 + }, + { + "name": "+[GPBMessage parseFromData:error:]", + "coverage": 1 + }, + { + "name": "+[GPBMessage parseFromData:extensionRegistry:error:]", + "coverage": 1 + }, + { + "name": "+[GPBMessage parseFromCodedInputStream:extensionRegistry:error:]", + "coverage": 0 + }, + { + "name": "+[GPBMessage parseDelimitedFromCodedInputStream:extensionRegistry:error:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage unknownFields]", + "coverage": 0 + }, + { + "name": "-[GPBMessage setUnknownFields:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage parseMessageSet:extensionRegistry:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage parseUnknownField:extensionRegistry:tag:]", + "coverage": 0.38095238095238093 + }, + { + "name": "-[GPBMessage addUnknownMapEntry:value:]", + "coverage": 0 + }, + { + "name": "MergeSingleFieldFromCodedInputStream", + "coverage": 0.6075949367088608 + }, + { + "name": "MergeRepeatedPackedFieldFromCodedInputStream", + "coverage": 0 + }, + { + "name": "MergeRepeatedNotPackedFieldFromCodedInputStream", + "coverage": 0.746031746031746 + }, + { + "name": "-[GPBMessage mergeFromCodedInputStream:extensionRegistry:]", + "coverage": 0.6804123711340206 + }, + { + "name": "-[GPBMessage mergeFrom:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage isEqual:]", + "coverage": 0.04032258064516129 + }, + { + "name": "-[GPBMessage hash]", + "coverage": 0 + }, + { + "name": "-[GPBMessage description]", + "coverage": 0 + }, + { + "name": "-[GPBMessage debugQuickLookObject]", + "coverage": 0 + }, + { + "name": "-[GPBMessage serializedSize]", + "coverage": 0.8441558441558441 + }, + { + "name": "Definition at 2945:108", + "coverage": 0 + }, + { + "name": "ResolveIvarGet", + "coverage": 1 + }, + { + "name": "Definition at 3044:64", + "coverage": 1 + }, + { + "name": "Definition at 3052:64", + "coverage": 1 + }, + { + "name": "ResolveIvarSet", + "coverage": 1 + }, + { + "name": "Definition at 3088:76", + "coverage": 1 + }, + { + "name": "Definition at 3096:74", + "coverage": 1 + }, + { + "name": "+[GPBMessage resolveInstanceMethod:]", + "coverage": 0.6448598130841121 + }, + { + "name": "__36+[GPBMessage resolveInstanceMethod:]_block_invoke", + "coverage": 1 + }, + { + "name": "__36+[GPBMessage resolveInstanceMethod:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__36+[GPBMessage resolveInstanceMethod:]_block_invoke_3", + "coverage": 0 + }, + { + "name": "__36+[GPBMessage resolveInstanceMethod:]_block_invoke_4", + "coverage": 1 + }, + { + "name": "__36+[GPBMessage resolveInstanceMethod:]_block_invoke_5", + "coverage": 0 + }, + { + "name": "__36+[GPBMessage resolveInstanceMethod:]_block_invoke_6", + "coverage": 1 + }, + { + "name": "__36+[GPBMessage resolveInstanceMethod:]_block_invoke_7", + "coverage": 0 + }, + { + "name": "+[GPBMessage resolveClassMethod:]", + "coverage": 0 + }, + { + "name": "+[GPBMessage supportsSecureCoding]", + "coverage": 0 + }, + { + "name": "-[GPBMessage initWithCoder:]", + "coverage": 0 + }, + { + "name": "-[GPBMessage encodeWithCoder:]", + "coverage": 0 + }, + { + "name": "+[GPBMessage accessInstanceVariablesDirectly]", + "coverage": 0 + }, + { + "name": "GPBGetMessageRepeatedField", + "coverage": 0 + }, + { + "name": "GPBGetMessageMapField", + "coverage": 0 + }, + { + "name": "GPBGetObjectIvarWithField", + "coverage": 0.48148148148148145 + } + ] + }, + { + "name": "GPBCodedInputStream.m", + "coverage": 0.39452054794520547, + "type": "objc", + "functions": [ + { + "name": "RaiseException", + "coverage": 1 + }, + { + "name": "CheckRecursionLimit", + "coverage": 0.6 + }, + { + "name": "CheckSize", + "coverage": 0.45454545454545453 + }, + { + "name": "ReadRawByte", + "coverage": 1 + }, + { + "name": "ReadRawLittleEndian32", + "coverage": 0 + }, + { + "name": "ReadRawLittleEndian64", + "coverage": 0 + }, + { + "name": "ReadRawVarint64", + "coverage": 0.9285714285714286 + }, + { + "name": "ReadRawVarint32", + "coverage": 1 + }, + { + "name": "SkipRawData", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadDouble", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadFloat", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadUInt64", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadUInt32", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadInt64", + "coverage": 1 + }, + { + "name": "GPBCodedInputStreamReadInt32", + "coverage": 1 + }, + { + "name": "GPBCodedInputStreamReadFixed64", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadFixed32", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadEnum", + "coverage": 1 + }, + { + "name": "GPBCodedInputStreamReadSFixed32", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadSFixed64", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadSInt32", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadSInt64", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadBool", + "coverage": 1 + }, + { + "name": "GPBCodedInputStreamReadTag", + "coverage": 0.8421052631578947 + }, + { + "name": "GPBCodedInputStreamReadRetainedString", + "coverage": 0.6818181818181818 + }, + { + "name": "GPBCodedInputStreamReadRetainedBytes", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamReadRetainedBytesNoCopy", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamPushLimit", + "coverage": 0.7777777777777778 + }, + { + "name": "GPBCodedInputStreamPopLimit", + "coverage": 1 + }, + { + "name": "GPBCodedInputStreamBytesUntilLimit", + "coverage": 0 + }, + { + "name": "GPBCodedInputStreamIsAtEnd", + "coverage": 1 + }, + { + "name": "GPBCodedInputStreamCheckLastTagWas", + "coverage": 0.6 + }, + { + "name": "+[GPBCodedInputStream streamWithData:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream initWithData:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedInputStream dealloc]", + "coverage": 1 + }, + { + "name": "-[GPBCodedInputStream readTag]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream checkLastTagWas:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedInputStream skipField:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream skipMessage]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream isAtEnd]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream position]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream pushLimit:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream popLimit:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readDouble]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readFloat]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readUInt64]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readInt64]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readInt32]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readFixed64]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readFixed32]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readBool]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readString]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readGroup:message:extensionRegistry:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readUnknownGroup:message:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedInputStream readMessage:extensionRegistry:]", + "coverage": 1 + }, + { + "name": "-[GPBCodedInputStream readMapEntry:extensionRegistry:field:parentMessage:]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readBytes]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readUInt32]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readEnum]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readSFixed32]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readSFixed64]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readSInt32]", + "coverage": 0 + }, + { + "name": "-[GPBCodedInputStream readSInt64]", + "coverage": 0 + } + ] + }, + { + "name": "GPBUtilities_PackagePrivate.h", + "coverage": 0.4166666666666667, + "type": "objc", + "functions": [ + { + "name": "Definition at 54:52", + "coverage": 0 + }, + { + "name": "Definition at 65:47", + "coverage": 0 + }, + { + "name": "Definition at 73:54", + "coverage": 0 + }, + { + "name": "Definition at 79:52", + "coverage": 0 + }, + { + "name": "Definition at 85:54", + "coverage": 0 + }, + { + "name": "Definition at 91:52", + "coverage": 0 + }, + { + "name": "Definition at 97:74", + "coverage": 1 + }, + { + "name": "Definition at 101:74", + "coverage": 1 + }, + { + "name": "Definition at 109:50", + "coverage": 0 + }, + { + "name": "Definition at 117:50", + "coverage": 0 + }, + { + "name": "Definition at 125:50", + "coverage": 0 + }, + { + "name": "Definition at 134:50", + "coverage": 0 + }, + { + "name": "Definition at 143:55", + "coverage": 1 + }, + { + "name": "Definition at 155:56", + "coverage": 1 + }, + { + "name": "Definition at 165:70", + "coverage": 1 + }, + { + "name": "Definition at 169:69", + "coverage": 1 + }, + { + "name": "Definition at 173:68", + "coverage": 0 + }, + { + "name": "Definition at 178:65", + "coverage": 0 + }, + { + "name": "Definition at 192:65", + "coverage": 1 + }, + { + "name": "Definition at 197:48", + "coverage": 1 + } + ] + }, + { + "name": "GPBDescriptor_PackagePrivate.h", + "coverage": 0.64, + "type": "objc", + "functions": [ + { + "name": "Definition at 262:65", + "coverage": 1 + }, + { + "name": "Definition at 267:71", + "coverage": 1 + }, + { + "name": "Definition at 271:64", + "coverage": 1 + }, + { + "name": "Definition at 275:63", + "coverage": 1 + }, + { + "name": "Definition at 289:76", + "coverage": 1 + }, + { + "name": "Definition at 293:78", + "coverage": 0 + }, + { + "name": "Definition at 297:76", + "coverage": 0 + }, + { + "name": "Definition at 301:80", + "coverage": 0 + } + ] + }, + { + "name": "GPBWireFormat.m", + "coverage": 0.9523809523809523, + "type": "objc", + "functions": [ + { + "name": "GPBWireFormatMakeTag", + "coverage": 1 + }, + { + "name": "GPBWireFormatGetTagWireType", + "coverage": 1 + }, + { + "name": "GPBWireFormatGetTagFieldNumber", + "coverage": 1 + }, + { + "name": "GPBWireFormatIsValidTag", + "coverage": 1 + }, + { + "name": "GPBWireFormatForType", + "coverage": 0.9259259259259259 + } + ] + } + ] + }, + { + "name": "Storage_Example_iOS.app", + "coverage": 0.6872727272727273, + "files": [ + { + "name": "FIRStorageDownloadTask.m", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageDownloadTask initWithReference:fetcherService:dispatchQueue:file:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageDownloadTask dealloc]", + "coverage": 0 + }, + { + "name": "-[FIRStorageDownloadTask enqueue]", + "coverage": 0 + }, + { + "name": "-[FIRStorageDownloadTask enqueueWithData:]", + "coverage": 0 + }, + { + "name": "__42-[FIRStorageDownloadTask enqueueWithData:]_block_invoke", + "coverage": 0 + }, + { + "name": "__42-[FIRStorageDownloadTask enqueueWithData:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__42-[FIRStorageDownloadTask enqueueWithData:]_block_invoke.58", + "coverage": 0 + }, + { + "name": "__42-[FIRStorageDownloadTask enqueueWithData:]_block_invoke.75", + "coverage": 0 + }, + { + "name": "__42-[FIRStorageDownloadTask enqueueWithData:]_block_invoke.88", + "coverage": 0 + }, + { + "name": "__42-[FIRStorageDownloadTask enqueueWithData:]_block_invoke.100", + "coverage": 0 + }, + { + "name": "-[FIRStorageDownloadTask cancel]", + "coverage": 0 + }, + { + "name": "-[FIRStorageDownloadTask cancelWithError:]", + "coverage": 0 + }, + { + "name": "__42-[FIRStorageDownloadTask cancelWithError:]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRStorageDownloadTask pause]", + "coverage": 0 + }, + { + "name": "__31-[FIRStorageDownloadTask pause]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRStorageDownloadTask resume]", + "coverage": 0 + }, + { + "name": "__32-[FIRStorageDownloadTask resume]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "FIRStorageGetDownloadURLTask.m", + "coverage": 0.1569767441860465, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageGetDownloadURLTask initWithReference:fetcherService:dispatchQueue:completion:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageGetDownloadURLTask dealloc]", + "coverage": 0 + }, + { + "name": "+[FIRStorageGetDownloadURLTask downloadURLFromMetadataDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageGetDownloadURLTask enqueue]", + "coverage": 0 + }, + { + "name": "__39-[FIRStorageGetDownloadURLTask enqueue]_block_invoke", + "coverage": 0 + }, + { + "name": "__39-[FIRStorageGetDownloadURLTask enqueue]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__39-[FIRStorageGetDownloadURLTask enqueue]_block_invoke.91", + "coverage": 0 + } + ] + }, + { + "name": "FIRAppDelegate.m", + "coverage": 0.27586206896551724, + "type": "objc", + "functions": [ + { + "name": "-[FIRAppDelegate application:didFinishLaunchingWithOptions:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillResignActive:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidEnterBackground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationWillEnterForeground:]", + "coverage": 0 + }, + { + "name": "-[FIRAppDelegate applicationDidBecomeActive:]", + "coverage": 1 + }, + { + "name": "-[FIRAppDelegate applicationWillTerminate:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRStorageTaskSnapshot.m", + "coverage": 0.29310344827586204, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageTaskSnapshot initWithTask:state:metadata:reference:progress:error:]", + "coverage": 0.40476190476190477 + }, + { + "name": "-[FIRStorageTaskSnapshot description]", + "coverage": 0 + } + ] + }, + { + "name": "FIRStorageReference.m", + "coverage": 0.3220338983050847, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageReference init]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference initWithStorage:path:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference isEqual:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRStorageReference isEqualToFIRStorageReference:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference hash]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference description]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference stringValue]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference bucket]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference fullPath]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference name]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference root]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference parent]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference child:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference putData:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference putData:metadata:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference putData:metadata:completion:]", + "coverage": 0 + }, + { + "name": "__51-[FIRStorageReference putData:metadata:completion:]_block_invoke", + "coverage": 0 + }, + { + "name": "__51-[FIRStorageReference putData:metadata:completion:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__51-[FIRStorageReference putData:metadata:completion:]_block_invoke.75", + "coverage": 0 + }, + { + "name": "__51-[FIRStorageReference putData:metadata:completion:]_block_invoke_2.76", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference putFile:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference putFile:metadata:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference putFile:metadata:completion:]", + "coverage": 0.9444444444444444 + }, + { + "name": "__51-[FIRStorageReference putFile:metadata:completion:]_block_invoke", + "coverage": 0 + }, + { + "name": "__51-[FIRStorageReference putFile:metadata:completion:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__51-[FIRStorageReference putFile:metadata:completion:]_block_invoke.97", + "coverage": 1 + }, + { + "name": "__51-[FIRStorageReference putFile:metadata:completion:]_block_invoke_2.98", + "coverage": 1 + }, + { + "name": "-[FIRStorageReference dataWithMaxSize:completion:]", + "coverage": 0 + }, + { + "name": "__50-[FIRStorageReference dataWithMaxSize:completion:]_block_invoke", + "coverage": 0 + }, + { + "name": "__50-[FIRStorageReference dataWithMaxSize:completion:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__50-[FIRStorageReference dataWithMaxSize:completion:]_block_invoke.118", + "coverage": 0 + }, + { + "name": "__50-[FIRStorageReference dataWithMaxSize:completion:]_block_invoke_2.119", + "coverage": 0 + }, + { + "name": "__50-[FIRStorageReference dataWithMaxSize:completion:]_block_invoke.126", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference writeToFile:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference writeToFile:completion:]", + "coverage": 0 + }, + { + "name": "__46-[FIRStorageReference writeToFile:completion:]_block_invoke", + "coverage": 0 + }, + { + "name": "__46-[FIRStorageReference writeToFile:completion:]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__46-[FIRStorageReference writeToFile:completion:]_block_invoke.157", + "coverage": 0 + }, + { + "name": "__46-[FIRStorageReference writeToFile:completion:]_block_invoke_2.158", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference downloadURLWithCompletion:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference metadataWithCompletion:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference updateMetadata:completion:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageReference deleteWithCompletion:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRStorageUploadTask.m", + "coverage": 0.42782152230971127, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageUploadTask initWithReference:fetcherService:dispatchQueue:data:metadata:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageUploadTask initWithReference:fetcherService:dispatchQueue:file:metadata:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageUploadTask dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRStorageUploadTask enqueue]", + "coverage": 1 + }, + { + "name": "__31-[FIRStorageUploadTask enqueue]_block_invoke", + "coverage": 0.102803738317757 + }, + { + "name": "__31-[FIRStorageUploadTask enqueue]_block_invoke_2", + "coverage": 0 + }, + { + "name": "__31-[FIRStorageUploadTask enqueue]_block_invoke.130", + "coverage": 0 + }, + { + "name": "__31-[FIRStorageUploadTask enqueue]_block_invoke.149", + "coverage": 0 + }, + { + "name": "-[FIRStorageUploadTask finishTaskWithStatus:snapshot:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageUploadTask isContentToUploadValid:]", + "coverage": 0.782608695652174 + }, + { + "name": "-[FIRStorageUploadTask cancel]", + "coverage": 0 + }, + { + "name": "__30-[FIRStorageUploadTask cancel]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRStorageUploadTask pause]", + "coverage": 0 + }, + { + "name": "__29-[FIRStorageUploadTask pause]_block_invoke", + "coverage": 0 + }, + { + "name": "-[FIRStorageUploadTask resume]", + "coverage": 0 + }, + { + "name": "__30-[FIRStorageUploadTask resume]_block_invoke", + "coverage": 0 + } + ] + }, + { + "name": "FIRViewController.m", + "coverage": 0.5, + "type": "objc", + "functions": [ + { + "name": "-[FIRViewController viewDidLoad]", + "coverage": 1 + }, + { + "name": "-[FIRViewController didReceiveMemoryWarning]", + "coverage": 0 + } + ] + }, + { + "name": "FIRStorageObservableTask.m", + "coverage": 0.622093023255814, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageObservableTask initWithReference:fetcherService:dispatchQueue:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageObservableTask observeStatus:handler:]", + "coverage": 0.5441176470588235 + }, + { + "name": "-[FIRStorageObservableTask removeObserverWithHandle:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageObservableTask removeAllObserversForStatus:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageObservableTask removeAllObservers]", + "coverage": 1 + }, + { + "name": "-[FIRStorageObservableTask handlerDictionaryForStatus:]", + "coverage": 0.6923076923076923 + }, + { + "name": "-[FIRStorageObservableTask removeHandlersFromStatusMapForDictionary:]", + "coverage": 0 + }, + { + "name": "-[FIRStorageObservableTask fireHandlersForStatus:snapshot:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageObservableTask fireHandlers:snapshot:]", + "coverage": 0.8947368421052632 + }, + { + "name": "__50-[FIRStorageObservableTask fireHandlers:snapshot:]_block_invoke", + "coverage": 1 + }, + { + "name": "__50-[FIRStorageObservableTask fireHandlers:snapshot:]_block_invoke_2", + "coverage": 1 + } + ] + }, + { + "name": "FIRStorageErrors.m", + "coverage": 0.6981132075471698, + "type": "objc", + "functions": [ + { + "name": "+[FIRStorageErrors errorWithCode:]", + "coverage": 0 + }, + { + "name": "+[FIRStorageErrors errorWithCode:infoDictionary:]", + "coverage": 0.6222222222222222 + }, + { + "name": "+[FIRStorageErrors errorWithServerError:reference:]", + "coverage": 0.8823529411764706 + }, + { + "name": "+[FIRStorageErrors errorWithInvalidRequest:]", + "coverage": 1 + }, + { + "name": "+[FIRStorageErrors errorWithCustomMessage:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRDependency.m", + "coverage": 0.7857142857142857, + "type": "objc", + "functions": [ + { + "name": "+[FIRDependency dependencyWithProtocol:]", + "coverage": 0 + }, + { + "name": "+[FIRDependency dependencyWithProtocol:isRequired:]", + "coverage": 1 + }, + { + "name": "-[FIRDependency initWithProtocol:isRequired:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRBundleUtil.m", + "coverage": 0.7872340425531915, + "type": "objc", + "functions": [ + { + "name": "+[FIRBundleUtil relevantBundles]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil optionsDictionaryPathWithResourceName:andFileType:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil relevantURLSchemes]", + "coverage": 0 + }, + { + "name": "+[FIRBundleUtil hasBundleIdentifierPrefix:inBundles:]", + "coverage": 1 + }, + { + "name": "+[FIRBundleUtil bundleIdentifierByRemovingLastPartFrom:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRConfiguration.m", + "coverage": 0.8076923076923077, + "type": "objc", + "functions": [ + { + "name": "+[FIRConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__34+[FIRConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration init]", + "coverage": 1 + }, + { + "name": "-[FIRConfiguration setLoggerLevel:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRLogger.m", + "coverage": 0.8118811881188119, + "type": "objc", + "functions": [ + { + "name": "FIRLoggerInitializeASL", + "coverage": 1 + }, + { + "name": "__FIRLoggerInitializeASL_block_invoke", + "coverage": 0.9333333333333333 + }, + { + "name": "FIRSetAnalyticsDebugMode", + "coverage": 0 + }, + { + "name": "FIRSetLoggerLevel", + "coverage": 0 + }, + { + "name": "FIRResetLogger", + "coverage": 1 + }, + { + "name": "FIRSetLoggerUserDefaults", + "coverage": 1 + }, + { + "name": "FIRIsLoggableLevel", + "coverage": 0 + }, + { + "name": "FIRLogBasic", + "coverage": 1 + }, + { + "name": "Definition at 158:95", + "coverage": 1 + }, + { + "name": "+[FIRLoggerWrapper logWithLevel:withService:withCode:withMessage:withArgs:]", + "coverage": 0 + } + ] + }, + { + "name": "FIRStorage.m", + "coverage": 0.8274111675126904, + "type": "objc", + "functions": [ + { + "name": "+[FIRStorage initialize]", + "coverage": 1 + }, + { + "name": "__24+[FIRStorage initialize]_block_invoke", + "coverage": 1 + }, + { + "name": "__24+[FIRStorage initialize]_block_invoke_2", + "coverage": 0 + }, + { + "name": "+[FIRStorage fetcherServiceForApp:bucket:auth:]", + "coverage": 1 + }, + { + "name": "+[FIRStorage setGTMSessionFetcherLoggingEnabled:]", + "coverage": 0 + }, + { + "name": "+[FIRStorage storage]", + "coverage": 0 + }, + { + "name": "+[FIRStorage storageForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRStorage storageWithURL:]", + "coverage": 0 + }, + { + "name": "+[FIRStorage storageForApp:URL:]", + "coverage": 1 + }, + { + "name": "-[FIRStorage initWithApp:bucket:auth:]", + "coverage": 1 + }, + { + "name": "-[FIRStorage copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIRStorage isEqual:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRStorage isEqualToFIRStorage:]", + "coverage": 1 + }, + { + "name": "-[FIRStorage hash]", + "coverage": 1 + }, + { + "name": "-[FIRStorage description]", + "coverage": 0 + }, + { + "name": "-[FIRStorage reference]", + "coverage": 1 + }, + { + "name": "-[FIRStorage referenceForURL:]", + "coverage": 1 + }, + { + "name": "-[FIRStorage referenceWithPath:]", + "coverage": 1 + }, + { + "name": "-[FIRStorage setCallbackQueue:]", + "coverage": 1 + }, + { + "name": "+[FIRStorage enableBackgroundTasks:]", + "coverage": 0 + }, + { + "name": "-[FIRStorage uploadTasks]", + "coverage": 0 + }, + { + "name": "-[FIRStorage downloadTasks]", + "coverage": 0 + } + ] + }, + { + "name": "FIRStorageTask.m", + "coverage": 0.8484848484848485, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageTask init]", + "coverage": 0 + }, + { + "name": "-[FIRStorageTask initWithReference:fetcherService:dispatchQueue:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageTask snapshot]", + "coverage": 1 + }, + { + "name": "-[FIRStorageTask dispatchAsync:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRApp.m", + "coverage": 0.8848, + "type": "objc", + "functions": [ + { + "name": "+[FIRApp configure]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithOptions:]", + "coverage": 1 + }, + { + "name": "+[FIRApp configureWithName:options:]", + "coverage": 0.9333333333333333 + }, + { + "name": "+[FIRApp defaultApp]", + "coverage": 1 + }, + { + "name": "+[FIRApp appNamed:]", + "coverage": 1 + }, + { + "name": "+[FIRApp allApps]", + "coverage": 1 + }, + { + "name": "+[FIRApp resetApps]", + "coverage": 1 + }, + { + "name": "-[FIRApp deleteApp:]", + "coverage": 0.7916666666666666 + }, + { + "name": "+[FIRApp addAppToAppDictionary:]", + "coverage": 0.6666666666666666 + }, + { + "name": "-[FIRApp initInstanceWithName:options:]", + "coverage": 1 + }, + { + "name": "-[FIRApp configureCore]", + "coverage": 0.7962962962962963 + }, + { + "name": "-[FIRApp options]", + "coverage": 1 + }, + { + "name": "-[FIRApp setDataCollectionDefaultEnabled:]", + "coverage": 0.9354838709677419 + }, + { + "name": "-[FIRApp isDataCollectionDefaultEnabled]", + "coverage": 1 + }, + { + "name": "+[FIRApp sendNotificationsToSDKs:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForMissingOptions]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForSubspecConfigurationFailureWithDomain:errorCode:service:reason:]", + "coverage": 1 + }, + { + "name": "+[FIRApp errorForInvalidAppID]", + "coverage": 0 + }, + { + "name": "+[FIRApp isDefaultAppConfigured]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerLibrary:withVersion:]", + "coverage": 1 + }, + { + "name": "+[FIRApp registerInternalLibrary:withName:withVersion:]", + "coverage": 1 + }, + { + "name": "__55+[FIRApp registerInternalLibrary:withName:withVersion:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRApp firebaseUserAgent]", + "coverage": 1 + }, + { + "name": "-[FIRApp checkExpectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp isAppIDValid]", + "coverage": 1 + }, + { + "name": "+[FIRApp validateAppID:]", + "coverage": 0.9473684210526315 + }, + { + "name": "+[FIRApp actualBundleID]", + "coverage": 0 + }, + { + "name": "+[FIRApp validateAppIDFormat:withVersion:]", + "coverage": 0.8450704225352113 + }, + { + "name": "+[FIRApp validateAppIDFingerprint:withVersion:]", + "coverage": 0.8620689655172413 + }, + { + "name": "-[FIRApp expectedBundleID]", + "coverage": 1 + }, + { + "name": "-[FIRApp clearDataCollectionSwitchFromUserDefaults]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromUserDefaultsForApp:]", + "coverage": 1 + }, + { + "name": "+[FIRApp readDataCollectionSwitchFromPlist]", + "coverage": 1 + }, + { + "name": "__43+[FIRApp readDataCollectionSwitchFromPlist]_block_invoke", + "coverage": 0.75 + }, + { + "name": "-[FIRApp sendLogsWithServiceName:version:error:]", + "coverage": 0.2631578947368421 + } + ] + }, + { + "name": "FIRStoragePath.m", + "coverage": 0.9285714285714286, + "type": "objc", + "functions": [ + { + "name": "+[FIRStoragePath pathFromString:]", + "coverage": 1 + }, + { + "name": "+[FIRStoragePath pathFromGSURI:]", + "coverage": 1 + }, + { + "name": "+[FIRStoragePath pathFromHTTPURL:]", + "coverage": 1 + }, + { + "name": "-[FIRStoragePath initWithBucket:object:]", + "coverage": 1 + }, + { + "name": "-[FIRStoragePath copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIRStoragePath isEqual:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRStoragePath isEqualToFIRStoragePath:]", + "coverage": 1 + }, + { + "name": "-[FIRStoragePath hash]", + "coverage": 0 + }, + { + "name": "-[FIRStoragePath description]", + "coverage": 0 + }, + { + "name": "-[FIRStoragePath stringValue]", + "coverage": 1 + }, + { + "name": "-[FIRStoragePath child:]", + "coverage": 1 + }, + { + "name": "-[FIRStoragePath parent]", + "coverage": 1 + }, + { + "name": "-[FIRStoragePath root]", + "coverage": 1 + }, + { + "name": "-[FIRStoragePath standardizedPathForString:]", + "coverage": 1 + }, + { + "name": "__44-[FIRStoragePath standardizedPathForString:]_block_invoke", + "coverage": 1 + } + ] + }, + { + "name": "FIROptions.m", + "coverage": 0.9489795918367347, + "type": "objc", + "functions": [ + { + "name": "+[FIROptions defaultOptions]", + "coverage": 0.8461538461538461 + }, + { + "name": "+[FIROptions initialize]", + "coverage": 1 + }, + { + "name": "+[FIROptions defaultOptionsDictionary]", + "coverage": 1 + }, + { + "name": "+[FIROptions plistFilePathWithName:]", + "coverage": 1 + }, + { + "name": "+[FIROptions resetDefaultOptions]", + "coverage": 1 + }, + { + "name": "-[FIROptions initInternalWithOptionsDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithContentsOfFile:]", + "coverage": 1 + }, + { + "name": "-[FIROptions initWithGoogleAppID:GCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions APIKey]", + "coverage": 1 + }, + { + "name": "-[FIROptions checkEditingLocked]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAPIKey:]", + "coverage": 1 + }, + { + "name": "-[FIROptions clientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions trackingID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setTrackingID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions GCMSenderID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGCMSenderID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions projectID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setProjectID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions androidClientID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setAndroidClientID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions googleAppID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setGoogleAppID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions libraryVersionID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setLibraryVersionID:]", + "coverage": 0 + }, + { + "name": "-[FIROptions databaseURL]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDatabaseURL:]", + "coverage": 1 + }, + { + "name": "-[FIROptions storageBucket]", + "coverage": 1 + }, + { + "name": "-[FIROptions setStorageBucket:]", + "coverage": 1 + }, + { + "name": "-[FIROptions setDeepLinkURLScheme:]", + "coverage": 1 + }, + { + "name": "-[FIROptions bundleID]", + "coverage": 1 + }, + { + "name": "-[FIROptions setBundleID:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionaryWithInfoDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIROptions analyticsOptionsDictionary]", + "coverage": 1 + }, + { + "name": "-[FIROptions isMeasurementEnabled]", + "coverage": 0.8181818181818182 + }, + { + "name": "-[FIROptions isAnalyticsCollectionExpicitlySet]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionEnabled]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsCollectionDeactivated]", + "coverage": 1 + }, + { + "name": "-[FIROptions isAnalyticsEnabled]", + "coverage": 0 + }, + { + "name": "-[FIROptions isSignInEnabled]", + "coverage": 0 + } + ] + }, + { + "name": "FIRAnalyticsConfiguration.m", + "coverage": 0.9545454545454546, + "type": "objc", + "functions": [ + { + "name": "+[FIRAnalyticsConfiguration sharedInstance]", + "coverage": 1 + }, + { + "name": "__43+[FIRAnalyticsConfiguration sharedInstance]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration postNotificationName:value:]", + "coverage": 0.75 + }, + { + "name": "-[FIRAnalyticsConfiguration setMinimumSessionInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setSessionTimeoutInterval:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:]", + "coverage": 1 + }, + { + "name": "-[FIRAnalyticsConfiguration setAnalyticsCollectionEnabled:persistSetting:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRStorageMetadata.m", + "coverage": 0.9664804469273743, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageMetadata init]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata initWithDictionary:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata copyWithZone:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata isEqual:]", + "coverage": 0.8333333333333334 + }, + { + "name": "-[FIRStorageMetadata isEqualToFIRStorageMetadata:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata hash]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata description]", + "coverage": 0 + }, + { + "name": "-[FIRStorageMetadata dictionaryRepresentation]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata isFile]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata isFolder]", + "coverage": 1 + }, + { + "name": "+[FIRStorageMetadata removeMatchingMetadata:oldMetadata:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata updatedMetadata]", + "coverage": 1 + }, + { + "name": "setupDateFormatterOnce", + "coverage": 1 + }, + { + "name": "__setupDateFormatterOnce_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata dateFromRFC3339String:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageMetadata RFC3339StringFromDate:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRStorageUtils.m", + "coverage": 0.9746835443037974, + "type": "objc", + "functions": [ + { + "name": "+[FIRStorageUtils GCSEscapedString:]", + "coverage": 1 + }, + { + "name": "+[FIRStorageUtils MIMETypeForExtension:]", + "coverage": 0.8823529411764706 + }, + { + "name": "+[FIRStorageUtils queryStringForDictionary:]", + "coverage": 1 + }, + { + "name": "__44+[FIRStorageUtils queryStringForDictionary:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRStorageUtils defaultRequestForPath:]", + "coverage": 1 + }, + { + "name": "+[FIRStorageUtils encodedURLForPath:]", + "coverage": 1 + }, + { + "name": "+[NSDictionary(FIRStorageNSDictionaryJSONHelpers) frs_dictionaryFromJSONData:]", + "coverage": 1 + }, + { + "name": "+[NSData(FIRStorageNSDataJSONHelpers) frs_dataFromJSONDictionary:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRStorageDeleteTask.m", + "coverage": 0.9795918367346939, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageDeleteTask dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRStorageDeleteTask initWithReference:fetcherService:dispatchQueue:completion:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageDeleteTask enqueue]", + "coverage": 1 + }, + { + "name": "__31-[FIRStorageDeleteTask enqueue]_block_invoke", + "coverage": 0.9444444444444444 + }, + { + "name": "__31-[FIRStorageDeleteTask enqueue]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__31-[FIRStorageDeleteTask enqueue]_block_invoke.36", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentContainer.m", + "coverage": 0.9818181818181818, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:]", + "coverage": 1 + }, + { + "name": "__55+[FIRComponentContainer registerAsComponentRegistrant:]_block_invoke", + "coverage": 1 + }, + { + "name": "+[FIRComponentContainer registerAsComponentRegistrant:inSet:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer initWithApp:registrants:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer populateComponentsFromRegisteredClasses:forApp:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer instantiateInstanceForProtocol:withBlock:]", + "coverage": 0.9285714285714286 + }, + { + "name": "-[FIRComponentContainer instanceForProtocol:]", + "coverage": 1 + }, + { + "name": "-[FIRComponentContainer removeAllCachedInstances]", + "coverage": 1 + } + ] + }, + { + "name": "FIRStorageGetMetadataTask.m", + "coverage": 0.9848484848484849, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageGetMetadataTask initWithReference:fetcherService:dispatchQueue:completion:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageGetMetadataTask dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRStorageGetMetadataTask enqueue]", + "coverage": 1 + }, + { + "name": "__36-[FIRStorageGetMetadataTask enqueue]_block_invoke", + "coverage": 0.9574468085106383 + }, + { + "name": "__36-[FIRStorageGetMetadataTask enqueue]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__36-[FIRStorageGetMetadataTask enqueue]_block_invoke.48", + "coverage": 1 + } + ] + }, + { + "name": "FIRStorageUpdateMetadataTask.m", + "coverage": 0.9865771812080537, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageUpdateMetadataTask initWithReference:fetcherService:dispatchQueue:metadata:completion:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageUpdateMetadataTask dealloc]", + "coverage": 1 + }, + { + "name": "-[FIRStorageUpdateMetadataTask enqueue]", + "coverage": 1 + }, + { + "name": "__39-[FIRStorageUpdateMetadataTask enqueue]_block_invoke", + "coverage": 0.9636363636363636 + }, + { + "name": "__39-[FIRStorageUpdateMetadataTask enqueue]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__39-[FIRStorageUpdateMetadataTask enqueue]_block_invoke.70", + "coverage": 1 + } + ] + }, + { + "name": "FIRStorageTokenAuthorizer.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageTokenAuthorizer initWithGoogleAppID:fetcherService:authProvider:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageTokenAuthorizer authorizeRequest:delegate:didFinishSelector:]", + "coverage": 1 + }, + { + "name": "__73-[FIRStorageTokenAuthorizer authorizeRequest:delegate:didFinishSelector:]_block_invoke", + "coverage": 1 + }, + { + "name": "__73-[FIRStorageTokenAuthorizer authorizeRequest:delegate:didFinishSelector:]_block_invoke_2", + "coverage": 1 + }, + { + "name": "__73-[FIRStorageTokenAuthorizer authorizeRequest:delegate:didFinishSelector:]_block_invoke.54", + "coverage": 1 + }, + { + "name": "-[FIRStorageTokenAuthorizer stopAuthorization]", + "coverage": 1 + }, + { + "name": "-[FIRStorageTokenAuthorizer stopAuthorizationForRequest:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageTokenAuthorizer isAuthorizingRequest:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageTokenAuthorizer isAuthorizedRequest:]", + "coverage": 1 + }, + { + "name": "-[FIRStorageTokenAuthorizer userEmail]", + "coverage": 1 + } + ] + }, + { + "name": "FIRAppAssociationRegistration.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRAppAssociationRegistration registeredObjectWithHost:key:creationBlock:]", + "coverage": 1 + } + ] + }, + { + "name": "main.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "main", + "coverage": 1 + } + ] + }, + { + "name": "FIRStorageComponent.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "-[FIRStorageComponent initWithApp:]", + "coverage": 1 + }, + { + "name": "+[FIRStorageComponent load]", + "coverage": 1 + }, + { + "name": "+[FIRStorageComponent componentsToRegister]", + "coverage": 1 + }, + { + "name": "__43+[FIRStorageComponent componentsToRegister]_block_invoke", + "coverage": 1 + }, + { + "name": "-[FIRStorageComponent storageForBucket:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponentType.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponentType instanceForProtocol:inContainer:]", + "coverage": 1 + } + ] + }, + { + "name": "FIRComponent.m", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "+[FIRComponent componentWithProtocol:creationBlock:]", + "coverage": 1 + }, + { + "name": "+[FIRComponent componentWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + }, + { + "name": "-[FIRComponent initWithProtocol:instantiationTiming:dependencies:creationBlock:]", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "leveldb.framework", + "coverage": 0.5077523967333413, + "files": [ + { + "name": "bloom.cc", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::(anonymous namespace)::BloomHash(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::BloomFilterPolicy::BloomFilterPolicy(int)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::BloomFilterPolicy::Name() const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::BloomFilterPolicy::CreateFilter(leveldb::Slice const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::BloomFilterPolicy::KeyMayMatch(leveldb::Slice const&, leveldb::Slice const&) const", + "coverage": 0 + }, + { + "name": "leveldb::NewBloomFilterPolicy(int)", + "coverage": 0 + } + ] + }, + { + "name": "c.cc", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb_comparator_t::~leveldb_comparator_t()", + "coverage": 0 + }, + { + "name": "leveldb_comparator_t::Compare(leveldb::Slice const&, leveldb::Slice const&) const", + "coverage": 0 + }, + { + "name": "leveldb_comparator_t::Name() const", + "coverage": 0 + }, + { + "name": "leveldb_comparator_t::FindShortestSeparator(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::Slice const&) const", + "coverage": 0 + }, + { + "name": "leveldb_comparator_t::FindShortSuccessor(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_t::~leveldb_filterpolicy_t()", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_t::Name() const", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_t::CreateFilter(leveldb::Slice const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_t::KeyMayMatch(leveldb::Slice const&, leveldb::Slice const&) const", + "coverage": 0 + }, + { + "name": "SaveError(char**, leveldb::Status const&)", + "coverage": 0 + }, + { + "name": "CopyString(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb_open", + "coverage": 0 + }, + { + "name": "leveldb_close", + "coverage": 0 + }, + { + "name": "leveldb_put", + "coverage": 0 + }, + { + "name": "leveldb_delete", + "coverage": 0 + }, + { + "name": "leveldb_write", + "coverage": 0 + }, + { + "name": "leveldb_get", + "coverage": 0 + }, + { + "name": "leveldb_create_iterator", + "coverage": 0 + }, + { + "name": "leveldb_create_snapshot", + "coverage": 0 + }, + { + "name": "leveldb_release_snapshot", + "coverage": 0 + }, + { + "name": "leveldb_property_value", + "coverage": 0 + }, + { + "name": "leveldb_approximate_sizes", + "coverage": 0 + }, + { + "name": "leveldb_compact_range", + "coverage": 0 + }, + { + "name": "leveldb_destroy_db", + "coverage": 0 + }, + { + "name": "leveldb_repair_db", + "coverage": 0 + }, + { + "name": "leveldb_iter_destroy", + "coverage": 0 + }, + { + "name": "leveldb_iter_valid", + "coverage": 0 + }, + { + "name": "leveldb_iter_seek_to_first", + "coverage": 0 + }, + { + "name": "leveldb_iter_seek_to_last", + "coverage": 0 + }, + { + "name": "leveldb_iter_seek", + "coverage": 0 + }, + { + "name": "leveldb_iter_next", + "coverage": 0 + }, + { + "name": "leveldb_iter_prev", + "coverage": 0 + }, + { + "name": "leveldb_iter_key", + "coverage": 0 + }, + { + "name": "leveldb_iter_value", + "coverage": 0 + }, + { + "name": "leveldb_iter_get_error", + "coverage": 0 + }, + { + "name": "leveldb_writebatch_create", + "coverage": 0 + }, + { + "name": "leveldb_writebatch_destroy", + "coverage": 0 + }, + { + "name": "leveldb_writebatch_clear", + "coverage": 0 + }, + { + "name": "leveldb_writebatch_put", + "coverage": 0 + }, + { + "name": "leveldb_writebatch_delete", + "coverage": 0 + }, + { + "name": "leveldb_writebatch_iterate", + "coverage": 0 + }, + { + "name": "leveldb_writebatch_iterate::H::Put(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb_writebatch_iterate::H::Delete(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb_options_create", + "coverage": 0 + }, + { + "name": "leveldb_options_destroy", + "coverage": 0 + }, + { + "name": "leveldb_options_set_comparator", + "coverage": 0 + }, + { + "name": "leveldb_options_set_filter_policy", + "coverage": 0 + }, + { + "name": "leveldb_options_set_create_if_missing", + "coverage": 0 + }, + { + "name": "leveldb_options_set_error_if_exists", + "coverage": 0 + }, + { + "name": "leveldb_options_set_paranoid_checks", + "coverage": 0 + }, + { + "name": "leveldb_options_set_env", + "coverage": 0 + }, + { + "name": "leveldb_options_set_info_log", + "coverage": 0 + }, + { + "name": "leveldb_options_set_write_buffer_size", + "coverage": 0 + }, + { + "name": "leveldb_options_set_max_open_files", + "coverage": 0 + }, + { + "name": "leveldb_options_set_cache", + "coverage": 0 + }, + { + "name": "leveldb_options_set_block_size", + "coverage": 0 + }, + { + "name": "leveldb_options_set_block_restart_interval", + "coverage": 0 + }, + { + "name": "leveldb_options_set_compression", + "coverage": 0 + }, + { + "name": "leveldb_comparator_create", + "coverage": 0 + }, + { + "name": "leveldb_comparator_destroy", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_create", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_destroy", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_create_bloom", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_create_bloom::Wrapper::~Wrapper()", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_create_bloom::Wrapper::Name() const", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_create_bloom::Wrapper::CreateFilter(leveldb::Slice const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_create_bloom::Wrapper::KeyMayMatch(leveldb::Slice const&, leveldb::Slice const&) const", + "coverage": 0 + }, + { + "name": "leveldb_filterpolicy_create_bloom::Wrapper::DoNothing(void*)", + "coverage": 0 + }, + { + "name": "leveldb_readoptions_create", + "coverage": 0 + }, + { + "name": "leveldb_readoptions_destroy", + "coverage": 0 + }, + { + "name": "leveldb_readoptions_set_verify_checksums", + "coverage": 0 + }, + { + "name": "leveldb_readoptions_set_fill_cache", + "coverage": 0 + }, + { + "name": "leveldb_readoptions_set_snapshot", + "coverage": 0 + }, + { + "name": "leveldb_writeoptions_create", + "coverage": 0 + }, + { + "name": "leveldb_writeoptions_destroy", + "coverage": 0 + }, + { + "name": "leveldb_writeoptions_set_sync", + "coverage": 0 + }, + { + "name": "leveldb_cache_create_lru", + "coverage": 0 + }, + { + "name": "leveldb_cache_destroy", + "coverage": 0 + }, + { + "name": "leveldb_create_default_env", + "coverage": 0 + }, + { + "name": "leveldb_env_destroy", + "coverage": 0 + }, + { + "name": "leveldb_free", + "coverage": 0 + }, + { + "name": "leveldb_major_version", + "coverage": 0 + }, + { + "name": "leveldb_minor_version", + "coverage": 0 + } + ] + }, + { + "name": "testutil.h", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::test::ErrorEnv::ErrorEnv()", + "coverage": 0 + }, + { + "name": "leveldb::test::ErrorEnv::NewWritableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile**)", + "coverage": 0 + }, + { + "name": "leveldb::test::ErrorEnv::NewAppendableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile**)", + "coverage": 0 + } + ] + }, + { + "name": "testutil.cc", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::test::RandomString(leveldb::Random*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 0 + }, + { + "name": "leveldb::test::RandomKey(leveldb::Random*, int)", + "coverage": 0 + }, + { + "name": "leveldb::test::CompressibleString(leveldb::Random*, double, unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 0 + } + ] + }, + { + "name": "testharness.h", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::test::Tester::Tester(char const*, int)", + "coverage": 0 + }, + { + "name": "leveldb::test::Tester::~Tester()", + "coverage": 0 + }, + { + "name": "leveldb::test::Tester::Is(bool, char const*)", + "coverage": 0 + }, + { + "name": "leveldb::test::Tester::IsOk(leveldb::Status const&)", + "coverage": 0 + }, + { + "name": "leveldb::test::Tester& leveldb::test::Tester::operator<<<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + } + ] + }, + { + "name": "block.h", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::Block::size() const", + "coverage": 0 + } + ] + }, + { + "name": "testharness.cc", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::test::RegisterTest(char const*, char const*, void (*)())", + "coverage": 0 + }, + { + "name": "leveldb::test::RunAllTests()", + "coverage": 0 + }, + { + "name": "leveldb::test::TmpDir()", + "coverage": 0 + }, + { + "name": "leveldb::test::RandomSeed()", + "coverage": 0 + } + ] + }, + { + "name": "dumpfile.cc", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::(anonymous namespace)::GuessType(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::FileType*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::CorruptionReporter::Corruption(unsigned long, leveldb::Status const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PrintLogContents(leveldb::Env*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, void (*)(unsigned long long, leveldb::Slice, leveldb::WritableFile*), leveldb::WritableFile*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::WriteBatchItemPrinter::Put(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::WriteBatchItemPrinter::Delete(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::WriteBatchPrinter(unsigned long long, leveldb::Slice, leveldb::WritableFile*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::DumpLog(leveldb::Env*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::VersionEditPrinter(unsigned long long, leveldb::Slice, leveldb::WritableFile*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::DumpDescriptor(leveldb::Env*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::DumpTable(leveldb::Env*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile*)", + "coverage": 0 + }, + { + "name": "leveldb::DumpFile(leveldb::Env*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile*)", + "coverage": 0 + } + ] + }, + { + "name": "filter_block.cc", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::FilterBlockBuilder::FilterBlockBuilder(leveldb::FilterPolicy const*)", + "coverage": 0 + }, + { + "name": "leveldb::FilterBlockBuilder::StartBlock(unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::FilterBlockBuilder::AddKey(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::FilterBlockBuilder::Finish()", + "coverage": 0 + }, + { + "name": "leveldb::FilterBlockBuilder::GenerateFilter()", + "coverage": 0 + }, + { + "name": "leveldb::FilterBlockReader::FilterBlockReader(leveldb::FilterPolicy const*, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::FilterBlockReader::KeyMayMatch(unsigned long long, leveldb::Slice const&)", + "coverage": 0 + } + ] + }, + { + "name": "histogram.h", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::Histogram::Histogram()", + "coverage": 0 + }, + { + "name": "leveldb::Histogram::~Histogram()", + "coverage": 0 + } + ] + }, + { + "name": "histogram.cc", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::Histogram::Clear()", + "coverage": 0 + }, + { + "name": "leveldb::Histogram::Add(double)", + "coverage": 0 + }, + { + "name": "leveldb::Histogram::Merge(leveldb::Histogram const&)", + "coverage": 0 + }, + { + "name": "leveldb::Histogram::Median() const", + "coverage": 0 + }, + { + "name": "leveldb::Histogram::Percentile(double) const", + "coverage": 0 + }, + { + "name": "leveldb::Histogram::Average() const", + "coverage": 0 + }, + { + "name": "leveldb::Histogram::StandardDeviation() const", + "coverage": 0 + }, + { + "name": "leveldb::Histogram::ToString() const", + "coverage": 0 + } + ] + }, + { + "name": "repair.cc", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "leveldb::(anonymous namespace)::Repairer::Repairer(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::Options const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::~Repairer()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::Run()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::FindFiles()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::ConvertLogFilesToTables()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::ConvertLogToTable(unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::ConvertLogToTable(unsigned long long)::LogReporter::Corruption(unsigned long, leveldb::Status const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::ExtractMetaData()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::NewTableIterator(leveldb::FileMetaData const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::ScanTable(unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::RepairTable(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::(anonymous namespace)::Repairer::TableInfo)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::WriteDescriptor()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::Repairer::ArchiveFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb::RepairDB(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::Options const&)", + "coverage": 0 + } + ] + }, + { + "name": "env.h", + "coverage": 0.10909090909090909, + "type": "objc", + "functions": [ + { + "name": "leveldb::Env::Env()", + "coverage": 1 + }, + { + "name": "leveldb::SequentialFile::SequentialFile()", + "coverage": 1 + }, + { + "name": "leveldb::RandomAccessFile::RandomAccessFile()", + "coverage": 1 + }, + { + "name": "leveldb::WritableFile::WritableFile()", + "coverage": 1 + }, + { + "name": "leveldb::Logger::Logger()", + "coverage": 1 + }, + { + "name": "leveldb::FileLock::FileLock()", + "coverage": 1 + }, + { + "name": "leveldb::EnvWrapper::EnvWrapper(leveldb::Env*)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::target() const", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::NewSequentialFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::SequentialFile**)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::NewRandomAccessFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::RandomAccessFile**)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::NewWritableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile**)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::NewAppendableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile**)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::FileExists(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::GetChildren(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >*)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::DeleteFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::CreateDir(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::DeleteDir(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::GetFileSize(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long*)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::RenameFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::LockFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::FileLock**)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::UnlockFile(leveldb::FileLock*)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::Schedule(void (*)(void*), void*)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::StartThread(void (*)(void*), void*)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::GetTestDirectory(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::NewLogger(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::Logger**)", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::NowMicros()", + "coverage": 0 + }, + { + "name": "leveldb::EnvWrapper::SleepForMicroseconds(int)", + "coverage": 0 + } + ] + }, + { + "name": "snapshot.h", + "coverage": 0.17391304347826086, + "type": "objc", + "functions": [ + { + "name": "leveldb::SnapshotList::SnapshotList()", + "coverage": 1 + }, + { + "name": "leveldb::SnapshotList::empty() const", + "coverage": 0 + }, + { + "name": "leveldb::SnapshotList::oldest() const", + "coverage": 0 + }, + { + "name": "leveldb::SnapshotList::newest() const", + "coverage": 0 + }, + { + "name": "leveldb::SnapshotList::New(unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::SnapshotList::Delete(leveldb::SnapshotImpl const*)", + "coverage": 0 + } + ] + }, + { + "name": "port_posix.h", + "coverage": 0.3076923076923077, + "type": "objc", + "functions": [ + { + "name": "leveldb::port::Mutex::AssertHeld()", + "coverage": 1 + }, + { + "name": "leveldb::port::Snappy_Compress(char const*, unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 1 + }, + { + "name": "leveldb::port::Snappy_GetUncompressedLength(char const*, unsigned long, unsigned long*)", + "coverage": 0 + }, + { + "name": "leveldb::port::Snappy_Uncompress(char const*, unsigned long, char*)", + "coverage": 0 + }, + { + "name": "leveldb::port::GetHeapProfile(void (*)(void*, char const*, int), void*)", + "coverage": 0 + } + ] + }, + { + "name": "db.h", + "coverage": 0.3333333333333333, + "type": "objc", + "functions": [ + { + "name": "leveldb::Range::Range()", + "coverage": 0 + }, + { + "name": "leveldb::Range::Range(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::DB::DB()", + "coverage": 1 + } + ] + }, + { + "name": "logging.cc", + "coverage": 0.39215686274509803, + "type": "objc", + "functions": [ + { + "name": "leveldb::AppendNumberTo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::AppendEscapedStringTo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::NumberToString(unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::EscapeString(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::ConsumeDecimalNumber(leveldb::Slice*, unsigned long long*)", + "coverage": 0.8695652173913043 + } + ] + }, + { + "name": "dbformat.cc", + "coverage": 0.4247787610619469, + "type": "objc", + "functions": [ + { + "name": "leveldb::PackSequenceAndType(unsigned long long, leveldb::ValueType)", + "coverage": 1 + }, + { + "name": "leveldb::AppendInternalKey(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::ParsedInternalKey const&)", + "coverage": 1 + }, + { + "name": "leveldb::ParsedInternalKey::DebugString() const", + "coverage": 0 + }, + { + "name": "leveldb::InternalKey::DebugString() const", + "coverage": 0 + }, + { + "name": "leveldb::InternalKeyComparator::Name() const", + "coverage": 0 + }, + { + "name": "leveldb::InternalKeyComparator::Compare(leveldb::Slice const&, leveldb::Slice const&) const", + "coverage": 1 + }, + { + "name": "leveldb::InternalKeyComparator::FindShortestSeparator(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::Slice const&) const", + "coverage": 0.5625 + }, + { + "name": "leveldb::InternalKeyComparator::FindShortSuccessor(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 1 + }, + { + "name": "leveldb::InternalFilterPolicy::Name() const", + "coverage": 0 + }, + { + "name": "leveldb::InternalFilterPolicy::CreateFilter(leveldb::Slice const*, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 0 + }, + { + "name": "leveldb::InternalFilterPolicy::KeyMayMatch(leveldb::Slice const&, leveldb::Slice const&) const", + "coverage": 0 + }, + { + "name": "leveldb::LookupKey::LookupKey(leveldb::Slice const&, unsigned long long)", + "coverage": 0 + } + ] + }, + { + "name": "iterator.cc", + "coverage": 0.4444444444444444, + "type": "objc", + "functions": [ + { + "name": "leveldb::Iterator::Iterator()", + "coverage": 1 + }, + { + "name": "leveldb::Iterator::~Iterator()", + "coverage": 0.5454545454545454 + }, + { + "name": "leveldb::Iterator::RegisterCleanup(void (*)(void*, void*), void*, void*)", + "coverage": 0.7142857142857143 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::EmptyIterator(leveldb::Status const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::Valid() const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::Seek(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::SeekToFirst()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::Next()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::key() const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::value() const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::EmptyIterator::status() const", + "coverage": 0 + }, + { + "name": "leveldb::NewEmptyIterator()", + "coverage": 0 + }, + { + "name": "leveldb::NewErrorIterator(leveldb::Status const&)", + "coverage": 0 + } + ] + }, + { + "name": "table.cc", + "coverage": 0.4458874458874459, + "type": "objc", + "functions": [ + { + "name": "leveldb::Table::Rep::~Rep()", + "coverage": 1 + }, + { + "name": "leveldb::Table::Open(leveldb::Options const&, leveldb::RandomAccessFile*, unsigned long long, leveldb::Table**)", + "coverage": 0.8775510204081632 + }, + { + "name": "leveldb::Table::ReadMeta(leveldb::Footer const&)", + "coverage": 0.14285714285714285 + }, + { + "name": "leveldb::Table::ReadFilter(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Table::~Table()", + "coverage": 1 + }, + { + "name": "leveldb::DeleteBlock(void*, void*)", + "coverage": 1 + }, + { + "name": "leveldb::DeleteCachedBlock(leveldb::Slice const&, void*)", + "coverage": 0 + }, + { + "name": "leveldb::ReleaseBlock(void*, void*)", + "coverage": 0 + }, + { + "name": "leveldb::Table::BlockReader(void*, leveldb::ReadOptions const&, leveldb::Slice const&)", + "coverage": 0.7547169811320755 + }, + { + "name": "leveldb::Table::NewIterator(leveldb::ReadOptions const&) const", + "coverage": 1 + }, + { + "name": "leveldb::Table::InternalGet(leveldb::ReadOptions const&, leveldb::Slice const&, void*, void (*)(void*, leveldb::Slice const&, leveldb::Slice const&))", + "coverage": 0 + }, + { + "name": "leveldb::Table::ApproximateOffsetOf(leveldb::Slice const&) const", + "coverage": 0 + } + ] + }, + { + "name": "db_impl.cc", + "coverage": 0.4696969696969697, + "type": "objc", + "functions": [ + { + "name": "leveldb::DBImpl::Writer::Writer(leveldb::port::Mutex*)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::CompactionState::current_output()", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::CompactionState::CompactionState(leveldb::Compaction*)", + "coverage": 0 + }, + { + "name": "Definition at 86:57", + "coverage": 1 + }, + { + "name": "leveldb::SanitizeOptions(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::InternalKeyComparator const*, leveldb::InternalFilterPolicy const*, leveldb::Options const&)", + "coverage": 0.8695652173913043 + }, + { + "name": "leveldb::DBImpl::DBImpl(leveldb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::~DBImpl()", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::NewDB()", + "coverage": 0.8709677419354839 + }, + { + "name": "leveldb::DBImpl::MaybeIgnoreError(leveldb::Status*) const", + "coverage": 0.625 + }, + { + "name": "leveldb::DBImpl::DeleteObsoleteFiles()", + "coverage": 0.8181818181818182 + }, + { + "name": "leveldb::DBImpl::Recover(leveldb::VersionEdit*, bool*)", + "coverage": 0.7777777777777778 + }, + { + "name": "leveldb::DBImpl::RecoverLogFile(unsigned long long, bool, bool*, leveldb::VersionEdit*, unsigned long long*)", + "coverage": 0.7333333333333333 + }, + { + "name": "leveldb::DBImpl::RecoverLogFile(unsigned long long, bool, bool*, leveldb::VersionEdit*, unsigned long long*)::LogReporter::Corruption(unsigned long, leveldb::Status const&)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::WriteLevel0Table(leveldb::MemTable*, leveldb::VersionEdit*, leveldb::Version*)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::CompactMemTable()", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::CompactRange(leveldb::Slice const*, leveldb::Slice const*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::TEST_CompactRange(int, leveldb::Slice const*, leveldb::Slice const*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::TEST_CompactMemTable()", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::RecordBackgroundError(leveldb::Status const&)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::MaybeScheduleCompaction()", + "coverage": 0.8823529411764706 + }, + { + "name": "leveldb::DBImpl::BGWork(void*)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::BackgroundCall()", + "coverage": 0.8888888888888888 + }, + { + "name": "leveldb::DBImpl::BackgroundCompaction()", + "coverage": 0.08333333333333333 + }, + { + "name": "leveldb::DBImpl::CleanupCompaction(leveldb::DBImpl::CompactionState*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::OpenCompactionOutputFile(leveldb::DBImpl::CompactionState*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::FinishCompactionOutputFile(leveldb::DBImpl::CompactionState*, leveldb::Iterator*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::InstallCompactionResults(leveldb::DBImpl::CompactionState*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::DoCompactionWork(leveldb::DBImpl::CompactionState*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::CleanupIteratorState(void*, void*)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::NewInternalIterator(leveldb::ReadOptions const&, unsigned long long*, unsigned int*)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::TEST_NewInternalIterator()", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::TEST_MaxNextLevelOverlappingBytes()", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::Get(leveldb::ReadOptions const&, leveldb::Slice const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::NewIterator(leveldb::ReadOptions const&)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::RecordReadSample(leveldb::Slice)", + "coverage": 0.6666666666666666 + }, + { + "name": "leveldb::DBImpl::GetSnapshot()", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::ReleaseSnapshot(leveldb::Snapshot const*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::Put(leveldb::WriteOptions const&, leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::Delete(leveldb::WriteOptions const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::Write(leveldb::WriteOptions const&, leveldb::WriteBatch*)", + "coverage": 0.7222222222222222 + }, + { + "name": "leveldb::DBImpl::BuildBatchGroup(leveldb::DBImpl::Writer**)", + "coverage": 0.4782608695652174 + }, + { + "name": "leveldb::DBImpl::MakeRoomForWrite(bool)", + "coverage": 0.6774193548387096 + }, + { + "name": "leveldb::DBImpl::GetProperty(leveldb::Slice const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 0 + }, + { + "name": "leveldb::DBImpl::GetApproximateSizes(leveldb::Range const*, int, unsigned long long*)", + "coverage": 0 + }, + { + "name": "leveldb::DB::Put(leveldb::WriteOptions const&, leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::DB::Delete(leveldb::WriteOptions const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::DB::~DB()", + "coverage": 1 + }, + { + "name": "leveldb::DB::Open(leveldb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::DB**)", + "coverage": 1 + }, + { + "name": "leveldb::Snapshot::~Snapshot()", + "coverage": 1 + }, + { + "name": "leveldb::DestroyDB(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::Options const&)", + "coverage": 0 + } + ] + }, + { + "name": "log_reader.cc", + "coverage": 0.47580645161290325, + "type": "objc", + "functions": [ + { + "name": "leveldb::log::Reader::Reporter::~Reporter()", + "coverage": 1 + }, + { + "name": "leveldb::log::Reader::Reader(leveldb::SequentialFile*, leveldb::log::Reader::Reporter*, bool, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::log::Reader::~Reader()", + "coverage": 1 + }, + { + "name": "leveldb::log::Reader::SkipToInitialBlock()", + "coverage": 0 + }, + { + "name": "leveldb::log::Reader::ReadRecord(leveldb::Slice*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 0.4959349593495935 + }, + { + "name": "leveldb::log::Reader::LastRecordOffset()", + "coverage": 0 + }, + { + "name": "leveldb::log::Reader::ReportCorruption(unsigned long long, char const*)", + "coverage": 0 + }, + { + "name": "leveldb::log::Reader::ReportDrop(unsigned long long, leveldb::Status const&)", + "coverage": 0 + }, + { + "name": "leveldb::log::Reader::ReadPhysicalRecord(leveldb::Slice*)", + "coverage": 0.6024096385542169 + } + ] + }, + { + "name": "merger.cc", + "coverage": 0.48936170212765956, + "type": "objc", + "functions": [ + { + "name": "leveldb::(anonymous namespace)::MergingIterator::MergingIterator(leveldb::Comparator const*, leveldb::Iterator**, int)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::~MergingIterator()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::Valid() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::SeekToFirst()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::Seek(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::Next()", + "coverage": 0.52 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::key() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::value() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::status() const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::FindSmallest()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MergingIterator::FindLargest()", + "coverage": 0 + }, + { + "name": "leveldb::NewMergingIterator(leveldb::Comparator const*, leveldb::Iterator**, int)", + "coverage": 0.9 + } + ] + }, + { + "name": "version_set.cc", + "coverage": 0.4955022488755622, + "type": "objc", + "functions": [ + { + "name": "leveldb::TargetFileSize(leveldb::Options const*)", + "coverage": 1 + }, + { + "name": "leveldb::MaxGrandParentOverlapBytes(leveldb::Options const*)", + "coverage": 1 + }, + { + "name": "leveldb::ExpandedCompactionByteSizeLimit(leveldb::Options const*)", + "coverage": 0 + }, + { + "name": "leveldb::MaxBytesForLevel(leveldb::Options const*, int)", + "coverage": 1 + }, + { + "name": "leveldb::MaxFileSizeForLevel(leveldb::Options const*, int)", + "coverage": 0 + }, + { + "name": "leveldb::TotalFileSize(std::__1::vector<leveldb::FileMetaData*, std::__1::allocator<leveldb::FileMetaData*> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::Version::~Version()", + "coverage": 1 + }, + { + "name": "leveldb::FindFile(leveldb::InternalKeyComparator const&, std::__1::vector<leveldb::FileMetaData*, std::__1::allocator<leveldb::FileMetaData*> > const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::AfterFile(leveldb::Comparator const*, leveldb::Slice const*, leveldb::FileMetaData const*)", + "coverage": 0 + }, + { + "name": "leveldb::BeforeFile(leveldb::Comparator const*, leveldb::Slice const*, leveldb::FileMetaData const*)", + "coverage": 0 + }, + { + "name": "leveldb::SomeFileOverlapsRange(leveldb::InternalKeyComparator const&, bool, std::__1::vector<leveldb::FileMetaData*, std::__1::allocator<leveldb::FileMetaData*> > const&, leveldb::Slice const*, leveldb::Slice const*)", + "coverage": 0.6451612903225806 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::LevelFileNumIterator(leveldb::InternalKeyComparator const&, std::__1::vector<leveldb::FileMetaData*, std::__1::allocator<leveldb::FileMetaData*> > const*)", + "coverage": 1 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::Valid() const", + "coverage": 1 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::Seek(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::SeekToFirst()", + "coverage": 1 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::Next()", + "coverage": 1 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::key() const", + "coverage": 1 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::value() const", + "coverage": 1 + }, + { + "name": "leveldb::Version::LevelFileNumIterator::status() const", + "coverage": 0 + }, + { + "name": "leveldb::GetFileIterator(void*, leveldb::ReadOptions const&, leveldb::Slice const&)", + "coverage": 0.8181818181818182 + }, + { + "name": "leveldb::Version::NewConcatenatingIterator(leveldb::ReadOptions const&, int) const", + "coverage": 1 + }, + { + "name": "leveldb::Version::AddIterators(leveldb::ReadOptions const&, std::__1::vector<leveldb::Iterator*, std::__1::allocator<leveldb::Iterator*> >*)", + "coverage": 0.7647058823529411 + }, + { + "name": "leveldb::SaveValue(void*, leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::NewestFirst(leveldb::FileMetaData*, leveldb::FileMetaData*)", + "coverage": 0 + }, + { + "name": "leveldb::Version::ForEachOverlapping(leveldb::Slice, leveldb::Slice, void*, bool (*)(void*, int, leveldb::FileMetaData*))", + "coverage": 0.6190476190476191 + }, + { + "name": "leveldb::Version::Get(leveldb::ReadOptions const&, leveldb::LookupKey const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::Version::GetStats*)", + "coverage": 0 + }, + { + "name": "leveldb::Version::UpdateStats(leveldb::Version::GetStats const&)", + "coverage": 0 + }, + { + "name": "leveldb::Version::RecordReadSample(leveldb::Slice)", + "coverage": 0.8648648648648649 + }, + { + "name": "leveldb::Version::RecordReadSample(leveldb::Slice)::State::Match(void*, int, leveldb::FileMetaData*)", + "coverage": 1 + }, + { + "name": "leveldb::Version::Ref()", + "coverage": 1 + }, + { + "name": "leveldb::Version::Unref()", + "coverage": 1 + }, + { + "name": "leveldb::Version::OverlapInLevel(int, leveldb::Slice const*, leveldb::Slice const*)", + "coverage": 1 + }, + { + "name": "leveldb::Version::PickLevelForMemTableOutput(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0.84 + }, + { + "name": "leveldb::Version::GetOverlappingInputs(int, leveldb::InternalKey const*, leveldb::InternalKey const*, std::__1::vector<leveldb::FileMetaData*, std::__1::allocator<leveldb::FileMetaData*> >*)", + "coverage": 0.5526315789473685 + }, + { + "name": "leveldb::Version::DebugString() const", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::Builder::BySmallestKey::operator()(leveldb::FileMetaData*, leveldb::FileMetaData*) const", + "coverage": 0.6666666666666666 + }, + { + "name": "leveldb::VersionSet::Builder::Builder(leveldb::VersionSet*, leveldb::Version*)", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::Builder::~Builder()", + "coverage": 0.9 + }, + { + "name": "leveldb::VersionSet::Builder::Apply(leveldb::VersionEdit*)", + "coverage": 0.8181818181818182 + }, + { + "name": "leveldb::VersionSet::Builder::SaveTo(leveldb::Version*)", + "coverage": 0.7659574468085106 + }, + { + "name": "leveldb::VersionSet::Builder::MaybeAddFile(leveldb::Version*, int, leveldb::FileMetaData*)", + "coverage": 0.6428571428571429 + }, + { + "name": "leveldb::VersionSet::VersionSet(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::Options const*, leveldb::TableCache*, leveldb::InternalKeyComparator const*)", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::~VersionSet()", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::AppendVersion(leveldb::Version*)", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::LogAndApply(leveldb::VersionEdit*, leveldb::port::Mutex*)", + "coverage": 0.8214285714285714 + }, + { + "name": "leveldb::VersionSet::Recover(bool*)", + "coverage": 0.8925619834710744 + }, + { + "name": "leveldb::VersionSet::Recover(bool*)::LogReporter::Corruption(unsigned long, leveldb::Status const&)", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::ReuseManifest(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0.13793103448275862 + }, + { + "name": "leveldb::VersionSet::MarkFileNumberUsed(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::Finalize(leveldb::Version*)", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::WriteSnapshot(leveldb::log::Writer*)", + "coverage": 0.8620689655172413 + }, + { + "name": "leveldb::VersionSet::NumLevelFiles(int) const", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::LevelSummary(leveldb::VersionSet::LevelSummaryStorage*) const", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::ApproximateOffsetOf(leveldb::Version*, leveldb::InternalKey const&)", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::AddLiveFiles(std::__1::set<unsigned long long, std::__1::less<unsigned long long>, std::__1::allocator<unsigned long long> >*)", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::NumLevelBytes(int) const", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::MaxNextLevelOverlappingBytes()", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::GetRange(std::__1::vector<leveldb::FileMetaData*, std::__1::allocator<leveldb::FileMetaData*> > const&, leveldb::InternalKey*, leveldb::InternalKey*)", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::GetRange2(std::__1::vector<leveldb::FileMetaData*, std::__1::allocator<leveldb::FileMetaData*> > const&, std::__1::vector<leveldb::FileMetaData*, std::__1::allocator<leveldb::FileMetaData*> > const&, leveldb::InternalKey*, leveldb::InternalKey*)", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::MakeInputIterator(leveldb::Compaction*)", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::PickCompaction()", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::SetupOtherInputs(leveldb::Compaction*)", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::CompactRange(int, leveldb::InternalKey const*, leveldb::InternalKey const*)", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::Compaction(leveldb::Options const*, int)", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::~Compaction()", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::IsTrivialMove() const", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::AddInputDeletions(leveldb::VersionEdit*)", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::IsBaseLevelForKey(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::ShouldStopBefore(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::ReleaseInputs()", + "coverage": 0 + } + ] + }, + { + "name": "cache.h", + "coverage": 0.5, + "type": "objc", + "functions": [ + { + "name": "leveldb::Cache::Cache()", + "coverage": 1 + }, + { + "name": "leveldb::Cache::Prune()", + "coverage": 0 + } + ] + }, + { + "name": "db_iter.cc", + "coverage": 0.5145631067961165, + "type": "objc", + "functions": [ + { + "name": "leveldb::(anonymous namespace)::DBIter::DBIter(leveldb::DBImpl*, leveldb::Comparator const*, leveldb::Iterator*, unsigned long long, unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::~DBIter()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::Valid() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::key() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::value() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::status() const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::SaveKey(leveldb::Slice const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::ClearSavedValue()", + "coverage": 0.75 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::RandomPeriod()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::ParseKey(leveldb::ParsedInternalKey*)", + "coverage": 0.8666666666666667 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::Next()", + "coverage": 0.46153846153846156 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::FindNextUserEntry(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::FindPrevUserEntry()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::Seek(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::SeekToFirst()", + "coverage": 0.8 + }, + { + "name": "leveldb::(anonymous namespace)::DBIter::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::NewDBIterator(leveldb::DBImpl*, leveldb::Comparator const*, leveldb::Iterator*, unsigned long long, unsigned int)", + "coverage": 1 + } + ] + }, + { + "name": "version_edit.cc", + "coverage": 0.5854700854700855, + "type": "objc", + "functions": [ + { + "name": "leveldb::VersionEdit::Clear()", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::EncodeTo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 0.8260869565217391 + }, + { + "name": "leveldb::GetInternalKey(leveldb::Slice*, leveldb::InternalKey*)", + "coverage": 0.7777777777777778 + }, + { + "name": "leveldb::GetLevel(leveldb::Slice*, int*)", + "coverage": 0.8 + }, + { + "name": "leveldb::VersionEdit::DecodeFrom(leveldb::Slice const&)", + "coverage": 0.6862745098039216 + }, + { + "name": "leveldb::VersionEdit::DebugString() const", + "coverage": 0 + } + ] + }, + { + "name": "version_set.h", + "coverage": 0.5925925925925926, + "type": "objc", + "functions": [ + { + "name": "leveldb::Version::NumFiles(int) const", + "coverage": 0 + }, + { + "name": "leveldb::Version::Version(leveldb::VersionSet*)", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::current() const", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::ManifestFileNumber() const", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::NewFileNumber()", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::ReuseFileNumber(unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::VersionSet::LastSequence() const", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::SetLastSequence(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::LogNumber() const", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::PrevLogNumber() const", + "coverage": 1 + }, + { + "name": "leveldb::VersionSet::NeedsCompaction() const", + "coverage": 1 + }, + { + "name": "leveldb::Compaction::level() const", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::edit()", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::num_input_files(int) const", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::input(int, int) const", + "coverage": 0 + }, + { + "name": "leveldb::Compaction::MaxOutputFileSize() const", + "coverage": 0 + } + ] + }, + { + "name": "dbformat.h", + "coverage": 0.6037735849056604, + "type": "objc", + "functions": [ + { + "name": "leveldb::ParsedInternalKey::ParsedInternalKey()", + "coverage": 1 + }, + { + "name": "leveldb::ParsedInternalKey::ParsedInternalKey(leveldb::Slice const&, unsigned long long const&, leveldb::ValueType)", + "coverage": 1 + }, + { + "name": "leveldb::InternalKeyEncodingLength(leveldb::ParsedInternalKey const&)", + "coverage": 0 + }, + { + "name": "leveldb::ExtractUserKey(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::ExtractValueType(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::InternalKeyComparator::InternalKeyComparator(leveldb::Comparator const*)", + "coverage": 1 + }, + { + "name": "leveldb::InternalKeyComparator::user_comparator() const", + "coverage": 1 + }, + { + "name": "leveldb::InternalFilterPolicy::InternalFilterPolicy(leveldb::FilterPolicy const*)", + "coverage": 1 + }, + { + "name": "leveldb::InternalKey::InternalKey()", + "coverage": 1 + }, + { + "name": "leveldb::InternalKey::InternalKey(leveldb::Slice const&, unsigned long long, leveldb::ValueType)", + "coverage": 1 + }, + { + "name": "leveldb::InternalKey::DecodeFrom(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::InternalKey::Encode() const", + "coverage": 1 + }, + { + "name": "leveldb::InternalKey::user_key() const", + "coverage": 1 + }, + { + "name": "leveldb::InternalKey::SetFrom(leveldb::ParsedInternalKey const&)", + "coverage": 0 + }, + { + "name": "leveldb::InternalKey::Clear()", + "coverage": 0 + }, + { + "name": "leveldb::InternalKeyComparator::Compare(leveldb::InternalKey const&, leveldb::InternalKey const&) const", + "coverage": 1 + }, + { + "name": "leveldb::ParseInternalKey(leveldb::Slice const&, leveldb::ParsedInternalKey*)", + "coverage": 1 + }, + { + "name": "leveldb::LookupKey::memtable_key() const", + "coverage": 0 + }, + { + "name": "leveldb::LookupKey::internal_key() const", + "coverage": 0 + }, + { + "name": "leveldb::LookupKey::user_key() const", + "coverage": 0 + }, + { + "name": "leveldb::LookupKey::~LookupKey()", + "coverage": 0 + } + ] + }, + { + "name": "memtable.cc", + "coverage": 0.6161616161616161, + "type": "objc", + "functions": [ + { + "name": "leveldb::GetLengthPrefixedSlice(char const*)", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::MemTable(leveldb::InternalKeyComparator const&)", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::~MemTable()", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::ApproximateMemoryUsage()", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::KeyComparator::operator()(char const*, char const*) const", + "coverage": 1 + }, + { + "name": "leveldb::EncodeKey(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::MemTableIterator::MemTableIterator(leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>*)", + "coverage": 1 + }, + { + "name": "leveldb::MemTableIterator::Valid() const", + "coverage": 1 + }, + { + "name": "leveldb::MemTableIterator::Seek(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::MemTableIterator::SeekToFirst()", + "coverage": 1 + }, + { + "name": "leveldb::MemTableIterator::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::MemTableIterator::Next()", + "coverage": 1 + }, + { + "name": "leveldb::MemTableIterator::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::MemTableIterator::key() const", + "coverage": 1 + }, + { + "name": "leveldb::MemTableIterator::value() const", + "coverage": 1 + }, + { + "name": "leveldb::MemTableIterator::status() const", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::NewIterator()", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::Add(unsigned long long, leveldb::ValueType, leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::Get(leveldb::LookupKey const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::Status*)", + "coverage": 0 + } + ] + }, + { + "name": "status.h", + "coverage": 0.631578947368421, + "type": "objc", + "functions": [ + { + "name": "leveldb::Status::Status()", + "coverage": 1 + }, + { + "name": "leveldb::Status::~Status()", + "coverage": 1 + }, + { + "name": "leveldb::Status::OK()", + "coverage": 1 + }, + { + "name": "leveldb::Status::NotFound(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Status::Corruption(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::Status::NotSupported(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Status::InvalidArgument(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::Status::IOError(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::Status::ok() const", + "coverage": 1 + }, + { + "name": "leveldb::Status::IsNotFound() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::IsCorruption() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::IsIOError() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::IsNotSupportedError() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::IsInvalidArgument() const", + "coverage": 0 + }, + { + "name": "leveldb::Status::code() const", + "coverage": 1 + }, + { + "name": "leveldb::Status::Status(leveldb::Status const&)", + "coverage": 1 + }, + { + "name": "leveldb::Status::operator=(leveldb::Status const&)", + "coverage": 1 + } + ] + }, + { + "name": "block.cc", + "coverage": 0.6354166666666666, + "type": "objc", + "functions": [ + { + "name": "leveldb::Block::NumRestarts() const", + "coverage": 1 + }, + { + "name": "leveldb::Block::Block(leveldb::BlockContents const&)", + "coverage": 0.7692307692307693 + }, + { + "name": "leveldb::Block::~Block()", + "coverage": 0.6 + }, + { + "name": "leveldb::DecodeEntry(char const*, char const*, unsigned int*, unsigned int*, unsigned int*)", + "coverage": 0.8947368421052632 + }, + { + "name": "leveldb::Block::Iter::Compare(leveldb::Slice const&, leveldb::Slice const&) const", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::NextEntryOffset() const", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::GetRestartPoint(unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::SeekToRestartPoint(unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::Iter(leveldb::Comparator const*, char const*, unsigned int, unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::Valid() const", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::status() const", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::key() const", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::value() const", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::Next()", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::Block::Iter::Seek(leveldb::Slice const&)", + "coverage": 0.41025641025641024 + }, + { + "name": "leveldb::Block::Iter::SeekToFirst()", + "coverage": 1 + }, + { + "name": "leveldb::Block::Iter::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::Block::Iter::CorruptionError()", + "coverage": 0 + }, + { + "name": "leveldb::Block::Iter::ParseNextKey()", + "coverage": 0.8571428571428571 + }, + { + "name": "leveldb::Block::NewIterator(leveldb::Comparator const*)", + "coverage": 0.7272727272727273 + } + ] + }, + { + "name": "table_cache.cc", + "coverage": 0.6470588235294118, + "type": "objc", + "functions": [ + { + "name": "leveldb::DeleteEntry(leveldb::Slice const&, void*)", + "coverage": 1 + }, + { + "name": "leveldb::UnrefEntry(void*, void*)", + "coverage": 1 + }, + { + "name": "leveldb::TableCache::TableCache(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::Options const*, int)", + "coverage": 1 + }, + { + "name": "leveldb::TableCache::~TableCache()", + "coverage": 1 + }, + { + "name": "leveldb::TableCache::FindTable(unsigned long long, unsigned long long, leveldb::Cache::Handle**)", + "coverage": 0.7428571428571429 + }, + { + "name": "leveldb::TableCache::NewIterator(leveldb::ReadOptions const&, unsigned long long, unsigned long long, leveldb::Table**)", + "coverage": 0.6842105263157895 + }, + { + "name": "leveldb::TableCache::Get(leveldb::ReadOptions const&, unsigned long long, unsigned long long, leveldb::Slice const&, void*, void (*)(void*, leveldb::Slice const&, leveldb::Slice const&))", + "coverage": 0 + }, + { + "name": "leveldb::TableCache::Evict(unsigned long long)", + "coverage": 0 + } + ] + }, + { + "name": "env_posix.cc", + "coverage": 0.6597077244258872, + "type": "objc", + "functions": [ + { + "name": "leveldb::(anonymous namespace)::IOError(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::Limiter::Limiter(long)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::Limiter::Acquire()", + "coverage": 0.7692307692307693 + }, + { + "name": "leveldb::(anonymous namespace)::Limiter::Release()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::Limiter::GetAllowed() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::Limiter::SetAllowed(long)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixSequentialFile::PosixSequentialFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, __sFILE*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixSequentialFile::~PosixSequentialFile()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixSequentialFile::Read(unsigned long, leveldb::Slice*, char*)", + "coverage": 0.7857142857142857 + }, + { + "name": "leveldb::(anonymous namespace)::PosixSequentialFile::Skip(unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixRandomAccessFile::PosixRandomAccessFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, leveldb::(anonymous namespace)::Limiter*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixRandomAccessFile::~PosixRandomAccessFile()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixRandomAccessFile::Read(unsigned long long, unsigned long, leveldb::Slice*, char*) const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixMmapReadableFile::PosixMmapReadableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, void*, unsigned long, leveldb::(anonymous namespace)::Limiter*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixMmapReadableFile::~PosixMmapReadableFile()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixMmapReadableFile::Read(unsigned long long, unsigned long, leveldb::Slice*, char*) const", + "coverage": 0.8 + }, + { + "name": "leveldb::(anonymous namespace)::PosixWritableFile::PosixWritableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, __sFILE*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixWritableFile::~PosixWritableFile()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixWritableFile::Append(leveldb::Slice const&)", + "coverage": 0.7142857142857143 + }, + { + "name": "leveldb::(anonymous namespace)::PosixWritableFile::Close()", + "coverage": 0.75 + }, + { + "name": "leveldb::(anonymous namespace)::PosixWritableFile::Flush()", + "coverage": 0.6666666666666666 + }, + { + "name": "leveldb::(anonymous namespace)::PosixWritableFile::SyncDirIfManifest()", + "coverage": 0.8076923076923077 + }, + { + "name": "leveldb::(anonymous namespace)::PosixWritableFile::Sync()", + "coverage": 0.6666666666666666 + }, + { + "name": "leveldb::(anonymous namespace)::LockOrUnlock(int, bool)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixLockTable::Insert(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixLockTable::Remove(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::~PosixEnv()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::NewSequentialFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::SequentialFile**)", + "coverage": 0.8 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::NewRandomAccessFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::RandomAccessFile**)", + "coverage": 0.7307692307692307 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::NewWritableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile**)", + "coverage": 0.8181818181818182 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::NewAppendableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile**)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::FileExists(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::GetChildren(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >*)", + "coverage": 0.8461538461538461 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::DeleteFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0.7142857142857143 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::CreateDir(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::DeleteDir(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::GetFileSize(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long*)", + "coverage": 0.8181818181818182 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::RenameFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::LockFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::FileLock**)", + "coverage": 0.7142857142857143 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::UnlockFile(leveldb::FileLock*)", + "coverage": 0.8181818181818182 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::GetTestDirectory(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::gettid()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::NewLogger(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::Logger**)", + "coverage": 0.8 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::NowMicros()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::SleepForMicroseconds(int)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::PthreadCall(char const*, int)", + "coverage": 0.5 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::BGThreadWrapper(void*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MaxMmaps()", + "coverage": 0.75 + }, + { + "name": "leveldb::(anonymous namespace)::MaxOpenFiles()", + "coverage": 0.6875 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::PosixEnv()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::Schedule(void (*)(void*), void*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::BGThread()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::StartThreadWrapper(void*)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::PosixEnv::StartThread(void (*)(void*), void*)", + "coverage": 0 + }, + { + "name": "leveldb::InitDefaultEnv()", + "coverage": 1 + }, + { + "name": "leveldb::EnvPosixTestHelper::SetReadOnlyFDLimit(int)", + "coverage": 0 + }, + { + "name": "leveldb::EnvPosixTestHelper::SetReadOnlyMMapLimit(int)", + "coverage": 0 + }, + { + "name": "leveldb::Env::Default()", + "coverage": 1 + } + ] + }, + { + "name": "format.cc", + "coverage": 0.6611570247933884, + "type": "objc", + "functions": [ + { + "name": "leveldb::BlockHandle::EncodeTo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 1 + }, + { + "name": "leveldb::BlockHandle::DecodeFrom(leveldb::Slice*)", + "coverage": 0.75 + }, + { + "name": "leveldb::Footer::EncodeTo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 1 + }, + { + "name": "leveldb::Footer::DecodeFrom(leveldb::Slice*)", + "coverage": 0.9047619047619048 + }, + { + "name": "leveldb::ReadBlock(leveldb::RandomAccessFile*, leveldb::ReadOptions const&, leveldb::BlockHandle const&, leveldb::BlockContents*)", + "coverage": 0.5066666666666667 + } + ] + }, + { + "name": "hash.cc", + "coverage": 0.6875, + "type": "objc", + "functions": [ + { + "name": "leveldb::Hash(char const*, unsigned long, unsigned int)", + "coverage": 0.6875 + } + ] + }, + { + "name": "cache.cc", + "coverage": 0.6882591093117408, + "type": "objc", + "functions": [ + { + "name": "leveldb::Cache::~Cache()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUHandle::key() const", + "coverage": 0.8888888888888888 + }, + { + "name": "leveldb::(anonymous namespace)::HandleTable::HandleTable()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::HandleTable::~HandleTable()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::HandleTable::Lookup(leveldb::Slice const&, unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::HandleTable::Insert(leveldb::(anonymous namespace)::LRUHandle*)", + "coverage": 0.7333333333333333 + }, + { + "name": "leveldb::(anonymous namespace)::HandleTable::Remove(leveldb::Slice const&, unsigned int)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::HandleTable::FindPointer(leveldb::Slice const&, unsigned int)", + "coverage": 0.75 + }, + { + "name": "leveldb::(anonymous namespace)::HandleTable::Resize()", + "coverage": 0.48 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::SetCapacity(unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::TotalCharge() const", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::LRUCache()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::~LRUCache()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::Ref(leveldb::(anonymous namespace)::LRUHandle*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::Unref(leveldb::(anonymous namespace)::LRUHandle*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::LRU_Remove(leveldb::(anonymous namespace)::LRUHandle*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::LRU_Append(leveldb::(anonymous namespace)::LRUHandle*, leveldb::(anonymous namespace)::LRUHandle*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::Lookup(leveldb::Slice const&, unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::Release(leveldb::Cache::Handle*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::Insert(leveldb::Slice const&, unsigned int, void*, unsigned long, void (*)(leveldb::Slice const&, void*))", + "coverage": 0.7878787878787878 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::FinishErase(leveldb::(anonymous namespace)::LRUHandle*)", + "coverage": 0.4 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::Erase(leveldb::Slice const&, unsigned int)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::LRUCache::Prune()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::HashSlice(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::Shard(unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::ShardedLRUCache(unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::~ShardedLRUCache()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::Insert(leveldb::Slice const&, void*, unsigned long, void (*)(leveldb::Slice const&, void*))", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::Lookup(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::Release(leveldb::Cache::Handle*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::Erase(leveldb::Slice const&)", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::Value(leveldb::Cache::Handle*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::NewId()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::Prune()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::ShardedLRUCache::TotalCharge() const", + "coverage": 0 + }, + { + "name": "leveldb::NewLRUCache(unsigned long)", + "coverage": 1 + } + ] + }, + { + "name": "skiplist.h", + "coverage": 0.7142857142857143, + "type": "objc", + "functions": [ + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::GetMaxHeight() const", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Equal(char const* const&, char const* const&) const", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node::Node(char const* const&)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node::Next(int)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node::SetNext(int, leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node*)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node::NoBarrier_Next(int)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node::NoBarrier_SetNext(int, leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node*)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::NewNode(char const* const&, int)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Iterator::Iterator(leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator> const*)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Iterator::Valid() const", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Iterator::key() const", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Iterator::Next()", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Iterator::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Iterator::Seek(char const* const&)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Iterator::SeekToFirst()", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Iterator::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::RandomHeight()", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::KeyIsAfterNode(char const* const&, leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node*) const", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::FindGreaterOrEqual(char const* const&, leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Node**) const", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::FindLessThan(char const* const&) const", + "coverage": 0 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::FindLast() const", + "coverage": 0 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::SkipList(leveldb::MemTable::KeyComparator, leveldb::Arena*)", + "coverage": 1 + }, + { + "name": "leveldb::SkipList<char const*, leveldb::MemTable::KeyComparator>::Insert(char const* const&)", + "coverage": 1 + } + ] + }, + { + "name": "two_level_iterator.cc", + "coverage": 0.7352941176470589, + "type": "objc", + "functions": [ + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::Valid() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::key() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::value() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::status() const", + "coverage": 0.8 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::SaveError(leveldb::Status const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::TwoLevelIterator(leveldb::Iterator*, leveldb::Iterator* (*)(void*, leveldb::ReadOptions const&, leveldb::Slice const&), void*, leveldb::ReadOptions const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::~TwoLevelIterator()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::Seek(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::SeekToFirst()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::Next()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::SkipEmptyDataBlocksForward()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::SkipEmptyDataBlocksBackward()", + "coverage": 0 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::SetDataIterator(leveldb::Iterator*)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::TwoLevelIterator::InitDataBlock()", + "coverage": 0.8666666666666667 + }, + { + "name": "leveldb::NewTwoLevelIterator(leveldb::Iterator*, leveldb::Iterator* (*)(void*, leveldb::ReadOptions const&, leveldb::Slice const&), void*, leveldb::ReadOptions const&)", + "coverage": 1 + } + ] + }, + { + "name": "random.h", + "coverage": 0.75, + "type": "objc", + "functions": [ + { + "name": "leveldb::Random::Random(unsigned int)", + "coverage": 0.6666666666666666 + }, + { + "name": "leveldb::Random::Next()", + "coverage": 0.9047619047619048 + }, + { + "name": "leveldb::Random::Uniform(int)", + "coverage": 1 + }, + { + "name": "leveldb::Random::OneIn(int)", + "coverage": 0 + }, + { + "name": "leveldb::Random::Skewed(int)", + "coverage": 0 + } + ] + }, + { + "name": "env.cc", + "coverage": 0.7567567567567568, + "type": "objc", + "functions": [ + { + "name": "leveldb::Env::~Env()", + "coverage": 0 + }, + { + "name": "leveldb::Env::NewAppendableFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::WritableFile**)", + "coverage": 0 + }, + { + "name": "leveldb::SequentialFile::~SequentialFile()", + "coverage": 1 + }, + { + "name": "leveldb::RandomAccessFile::~RandomAccessFile()", + "coverage": 1 + }, + { + "name": "leveldb::WritableFile::~WritableFile()", + "coverage": 1 + }, + { + "name": "leveldb::Logger::~Logger()", + "coverage": 1 + }, + { + "name": "leveldb::FileLock::~FileLock()", + "coverage": 1 + }, + { + "name": "leveldb::Log(leveldb::Logger*, char const*, ...)", + "coverage": 1 + }, + { + "name": "leveldb::DoWriteStringToFile(leveldb::Env*, leveldb::Slice const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)", + "coverage": 0.7894736842105263 + }, + { + "name": "leveldb::WriteStringToFile(leveldb::Env*, leveldb::Slice const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 0 + }, + { + "name": "leveldb::WriteStringToFileSync(leveldb::Env*, leveldb::Slice const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::ReadFileToString(leveldb::Env*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", + "coverage": 0.8333333333333334 + }, + { + "name": "leveldb::EnvWrapper::~EnvWrapper()", + "coverage": 0 + } + ] + }, + { + "name": "port_posix.cc", + "coverage": 0.7692307692307693, + "type": "objc", + "functions": [ + { + "name": "leveldb::port::PthreadCall(char const*, int)", + "coverage": 0.5 + }, + { + "name": "leveldb::port::Mutex::Mutex()", + "coverage": 1 + }, + { + "name": "leveldb::port::Mutex::~Mutex()", + "coverage": 1 + }, + { + "name": "leveldb::port::Mutex::Lock()", + "coverage": 1 + }, + { + "name": "leveldb::port::Mutex::Unlock()", + "coverage": 1 + }, + { + "name": "leveldb::port::CondVar::CondVar(leveldb::port::Mutex*)", + "coverage": 1 + }, + { + "name": "leveldb::port::CondVar::~CondVar()", + "coverage": 1 + }, + { + "name": "leveldb::port::CondVar::Wait()", + "coverage": 1 + }, + { + "name": "leveldb::port::CondVar::Signal()", + "coverage": 0 + }, + { + "name": "leveldb::port::CondVar::SignalAll()", + "coverage": 1 + }, + { + "name": "leveldb::port::InitOnce(_opaque_pthread_once_t*, void (*)())", + "coverage": 1 + } + ] + }, + { + "name": "status.cc", + "coverage": 0.7704918032786885, + "type": "objc", + "functions": [ + { + "name": "leveldb::Status::CopyState(char const*)", + "coverage": 1 + }, + { + "name": "leveldb::Status::Status(leveldb::Status::Code, leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::Status::ToString() const", + "coverage": 0.631578947368421 + } + ] + }, + { + "name": "table_builder.cc", + "coverage": 0.7846153846153846, + "type": "objc", + "functions": [ + { + "name": "leveldb::TableBuilder::Rep::Rep(leveldb::Options const&, leveldb::WritableFile*)", + "coverage": 1 + }, + { + "name": "leveldb::TableBuilder::TableBuilder(leveldb::Options const&, leveldb::WritableFile*)", + "coverage": 0.6 + }, + { + "name": "leveldb::TableBuilder::~TableBuilder()", + "coverage": 1 + }, + { + "name": "leveldb::TableBuilder::ChangeOptions(leveldb::Options const&)", + "coverage": 0 + }, + { + "name": "leveldb::TableBuilder::Add(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 0.9333333333333333 + }, + { + "name": "leveldb::TableBuilder::Flush()", + "coverage": 0.8666666666666667 + }, + { + "name": "leveldb::TableBuilder::WriteBlock(leveldb::BlockBuilder*, leveldb::BlockHandle*)", + "coverage": 0.9142857142857143 + }, + { + "name": "leveldb::TableBuilder::WriteRawBlock(leveldb::Slice const&, leveldb::CompressionType, leveldb::BlockHandle*)", + "coverage": 1 + }, + { + "name": "leveldb::TableBuilder::status() const", + "coverage": 1 + }, + { + "name": "leveldb::TableBuilder::Finish()", + "coverage": 0.8214285714285714 + }, + { + "name": "leveldb::TableBuilder::Abandon()", + "coverage": 0 + }, + { + "name": "leveldb::TableBuilder::NumEntries() const", + "coverage": 0 + }, + { + "name": "leveldb::TableBuilder::FileSize() const", + "coverage": 1 + } + ] + }, + { + "name": "coding.cc", + "coverage": 0.8132530120481928, + "type": "objc", + "functions": [ + { + "name": "leveldb::EncodeFixed32(char*, unsigned int)", + "coverage": 0.5 + }, + { + "name": "leveldb::EncodeFixed64(char*, unsigned long long)", + "coverage": 0.35714285714285715 + }, + { + "name": "leveldb::PutFixed32(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::PutFixed64(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::EncodeVarint32(char*, unsigned int)", + "coverage": 0.7777777777777778 + }, + { + "name": "leveldb::PutVarint32(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::EncodeVarint64(char*, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::PutVarint64(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::PutLengthPrefixedSlice(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::VarintLength(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::GetVarint32PtrFallback(char const*, char const*, unsigned int*)", + "coverage": 1 + }, + { + "name": "leveldb::GetVarint32(leveldb::Slice*, unsigned int*)", + "coverage": 1 + }, + { + "name": "leveldb::GetVarint64Ptr(char const*, char const*, unsigned long long*)", + "coverage": 1 + }, + { + "name": "leveldb::GetVarint64(leveldb::Slice*, unsigned long long*)", + "coverage": 0.9090909090909091 + }, + { + "name": "leveldb::GetLengthPrefixedSlice(char const*, char const*, leveldb::Slice*)", + "coverage": 0 + }, + { + "name": "leveldb::GetLengthPrefixedSlice(leveldb::Slice*, leveldb::Slice*)", + "coverage": 0.8181818181818182 + } + ] + }, + { + "name": "version_edit.h", + "coverage": 0.8378378378378378, + "type": "objc", + "functions": [ + { + "name": "leveldb::FileMetaData::FileMetaData()", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::VersionEdit()", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::~VersionEdit()", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::SetComparatorName(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::SetLogNumber(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::SetPrevLogNumber(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::SetNextFile(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::SetLastSequence(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::SetCompactPointer(int, leveldb::InternalKey const&)", + "coverage": 0 + }, + { + "name": "leveldb::VersionEdit::AddFile(int, unsigned long long, unsigned long long, leveldb::InternalKey const&, leveldb::InternalKey const&)", + "coverage": 1 + }, + { + "name": "leveldb::VersionEdit::DeleteFile(int, unsigned long long)", + "coverage": 0 + } + ] + }, + { + "name": "posix_logger.h", + "coverage": 0.8428571428571429, + "type": "objc", + "functions": [ + { + "name": "leveldb::PosixLogger::PosixLogger(__sFILE*, unsigned long long (*)())", + "coverage": 1 + }, + { + "name": "leveldb::PosixLogger::~PosixLogger()", + "coverage": 1 + }, + { + "name": "leveldb::PosixLogger::Logv(char const*, __va_list_tag*)", + "coverage": 0.8333333333333334 + } + ] + }, + { + "name": "filename.cc", + "coverage": 0.8613861386138614, + "type": "objc", + "functions": [ + { + "name": "leveldb::MakeFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long, char const*)", + "coverage": 1 + }, + { + "name": "leveldb::LogFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::TableFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::SSTTableFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::DescriptorFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::CurrentFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::LockFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::TempFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::InfoLogFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::OldInfoLogFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::ParseFileName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long*, leveldb::FileType*)", + "coverage": 0.813953488372093 + }, + { + "name": "leveldb::SetCurrentFile(leveldb::Env*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long long)", + "coverage": 0.875 + } + ] + }, + { + "name": "write_batch.cc", + "coverage": 0.8617021276595744, + "type": "objc", + "functions": [ + { + "name": "leveldb::WriteBatch::WriteBatch()", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatch::~WriteBatch()", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatch::Handler::~Handler()", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatch::Clear()", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatch::Iterate(leveldb::WriteBatch::Handler*) const", + "coverage": 0.7948717948717948 + }, + { + "name": "leveldb::WriteBatchInternal::Count(leveldb::WriteBatch const*)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatchInternal::SetCount(leveldb::WriteBatch*, int)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatchInternal::Sequence(leveldb::WriteBatch const*)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatchInternal::SetSequence(leveldb::WriteBatch*, unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatch::Put(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatch::Delete(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MemTableInserter::Put(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::MemTableInserter::Delete(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatchInternal::InsertInto(leveldb::WriteBatch const*, leveldb::MemTable*)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatchInternal::SetContents(leveldb::WriteBatch*, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatchInternal::Append(leveldb::WriteBatch*, leveldb::WriteBatch const*)", + "coverage": 0 + } + ] + }, + { + "name": "builder.cc", + "coverage": 0.8769230769230769, + "type": "objc", + "functions": [ + { + "name": "leveldb::BuildTable(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, leveldb::Env*, leveldb::Options const&, leveldb::TableCache*, leveldb::Iterator*, leveldb::FileMetaData*)", + "coverage": 0.8769230769230769 + } + ] + }, + { + "name": "coding.h", + "coverage": 0.8857142857142857, + "type": "objc", + "functions": [ + { + "name": "leveldb::DecodeFixed32(char const*)", + "coverage": 1 + }, + { + "name": "leveldb::DecodeFixed64(char const*)", + "coverage": 0.6666666666666666 + }, + { + "name": "leveldb::GetVarint32Ptr(char const*, char const*, unsigned int*)", + "coverage": 1 + } + ] + }, + { + "name": "comparator.cc", + "coverage": 0.9183673469387755, + "type": "objc", + "functions": [ + { + "name": "leveldb::Comparator::~Comparator()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::BytewiseComparatorImpl::BytewiseComparatorImpl()", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::BytewiseComparatorImpl::Name() const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::BytewiseComparatorImpl::Compare(leveldb::Slice const&, leveldb::Slice const&) const", + "coverage": 1 + }, + { + "name": "leveldb::(anonymous namespace)::BytewiseComparatorImpl::FindShortestSeparator(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, leveldb::Slice const&) const", + "coverage": 0.8095238095238095 + }, + { + "name": "leveldb::(anonymous namespace)::BytewiseComparatorImpl::FindShortSuccessor(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const", + "coverage": 1 + }, + { + "name": "leveldb::InitModule()", + "coverage": 1 + }, + { + "name": "leveldb::BytewiseComparator()", + "coverage": 1 + } + ] + }, + { + "name": "log_writer.cc", + "coverage": 0.9195402298850575, + "type": "objc", + "functions": [ + { + "name": "leveldb::log::InitTypeCrc(unsigned int*)", + "coverage": 1 + }, + { + "name": "leveldb::log::Writer::Writer(leveldb::WritableFile*)", + "coverage": 1 + }, + { + "name": "leveldb::log::Writer::Writer(leveldb::WritableFile*, unsigned long long)", + "coverage": 0 + }, + { + "name": "leveldb::log::Writer::~Writer()", + "coverage": 1 + }, + { + "name": "leveldb::log::Writer::AddRecord(leveldb::Slice const&)", + "coverage": 0.9148936170212766 + }, + { + "name": "leveldb::log::Writer::EmitPhysicalRecord(leveldb::log::RecordType, char const*, unsigned long)", + "coverage": 1 + } + ] + }, + { + "name": "iterator_wrapper.h", + "coverage": 0.9333333333333333, + "type": "objc", + "functions": [ + { + "name": "leveldb::IteratorWrapper::IteratorWrapper()", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::IteratorWrapper(leveldb::Iterator*)", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::~IteratorWrapper()", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::iter() const", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::Set(leveldb::Iterator*)", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::Valid() const", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::key() const", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::value() const", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::status() const", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::Next()", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::Prev()", + "coverage": 0 + }, + { + "name": "leveldb::IteratorWrapper::Seek(leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::SeekToFirst()", + "coverage": 1 + }, + { + "name": "leveldb::IteratorWrapper::SeekToLast()", + "coverage": 0 + }, + { + "name": "leveldb::IteratorWrapper::Update()", + "coverage": 1 + } + ] + }, + { + "name": "crc32c.cc", + "coverage": 0.9666666666666667, + "type": "objc", + "functions": [ + { + "name": "leveldb::crc32c::LE_LOAD32(unsigned char const*)", + "coverage": 1 + }, + { + "name": "leveldb::crc32c::CanAccelerateCRC32C()", + "coverage": 1 + }, + { + "name": "leveldb::crc32c::Extend(unsigned int, char const*, unsigned long)", + "coverage": 0.9591836734693877 + } + ] + }, + { + "name": "mutexlock.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::MutexLock::MutexLock(leveldb::port::Mutex*)", + "coverage": 1 + }, + { + "name": "leveldb::MutexLock::~MutexLock()", + "coverage": 1 + } + ] + }, + { + "name": "options.cc", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::Options::Options()", + "coverage": 1 + } + ] + }, + { + "name": "options.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::ReadOptions::ReadOptions()", + "coverage": 1 + }, + { + "name": "leveldb::WriteOptions::WriteOptions()", + "coverage": 1 + } + ] + }, + { + "name": "write_batch_internal.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::WriteBatchInternal::Contents(leveldb::WriteBatch const*)", + "coverage": 1 + }, + { + "name": "leveldb::WriteBatchInternal::ByteSize(leveldb::WriteBatch const*)", + "coverage": 1 + } + ] + }, + { + "name": "db_impl.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::DBImpl::CompactionStats::CompactionStats()", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::CompactionStats::Add(leveldb::DBImpl::CompactionStats const&)", + "coverage": 1 + }, + { + "name": "leveldb::DBImpl::user_comparator() const", + "coverage": 1 + } + ] + }, + { + "name": "atomic_pointer.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::port::MemoryBarrier()", + "coverage": 1 + }, + { + "name": "leveldb::port::AtomicPointer::AtomicPointer()", + "coverage": 1 + }, + { + "name": "leveldb::port::AtomicPointer::AtomicPointer(void*)", + "coverage": 1 + }, + { + "name": "leveldb::port::AtomicPointer::NoBarrier_Load() const", + "coverage": 1 + }, + { + "name": "leveldb::port::AtomicPointer::NoBarrier_Store(void*)", + "coverage": 1 + }, + { + "name": "leveldb::port::AtomicPointer::Acquire_Load() const", + "coverage": 1 + }, + { + "name": "leveldb::port::AtomicPointer::Release_Store(void*)", + "coverage": 1 + } + ] + }, + { + "name": "format.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::BlockHandle::offset() const", + "coverage": 1 + }, + { + "name": "leveldb::BlockHandle::set_offset(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::BlockHandle::size() const", + "coverage": 1 + }, + { + "name": "leveldb::BlockHandle::set_size(unsigned long long)", + "coverage": 1 + }, + { + "name": "leveldb::Footer::Footer()", + "coverage": 1 + }, + { + "name": "leveldb::Footer::metaindex_handle() const", + "coverage": 1 + }, + { + "name": "leveldb::Footer::set_metaindex_handle(leveldb::BlockHandle const&)", + "coverage": 1 + }, + { + "name": "leveldb::Footer::index_handle() const", + "coverage": 1 + }, + { + "name": "leveldb::Footer::set_index_handle(leveldb::BlockHandle const&)", + "coverage": 1 + }, + { + "name": "leveldb::BlockHandle::BlockHandle()", + "coverage": 1 + } + ] + }, + { + "name": "block_builder.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::BlockBuilder::empty() const", + "coverage": 1 + } + ] + }, + { + "name": "arena.cc", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::Arena::Arena()", + "coverage": 1 + }, + { + "name": "leveldb::Arena::~Arena()", + "coverage": 1 + }, + { + "name": "leveldb::Arena::AllocateFallback(unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::Arena::AllocateAligned(unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::Arena::AllocateNewBlock(unsigned long)", + "coverage": 1 + } + ] + }, + { + "name": "arena.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::Arena::MemoryUsage() const", + "coverage": 1 + }, + { + "name": "leveldb::Arena::Allocate(unsigned long)", + "coverage": 1 + } + ] + }, + { + "name": "block_builder.cc", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::BlockBuilder::BlockBuilder(leveldb::Options const*)", + "coverage": 1 + }, + { + "name": "leveldb::BlockBuilder::Reset()", + "coverage": 1 + }, + { + "name": "leveldb::BlockBuilder::CurrentSizeEstimate() const", + "coverage": 1 + }, + { + "name": "leveldb::BlockBuilder::Finish()", + "coverage": 1 + }, + { + "name": "leveldb::BlockBuilder::Add(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + } + ] + }, + { + "name": "port_posix_sse.cc", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::port::AcceleratedCRC32C(unsigned int, char const*, unsigned long)", + "coverage": 1 + } + ] + }, + { + "name": "crc32c.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::crc32c::Value(char const*, unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::crc32c::Mask(unsigned int)", + "coverage": 1 + }, + { + "name": "leveldb::crc32c::Unmask(unsigned int)", + "coverage": 1 + } + ] + }, + { + "name": "memtable.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::MemTable::Ref()", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::Unref()", + "coverage": 1 + }, + { + "name": "leveldb::MemTable::KeyComparator::KeyComparator(leveldb::InternalKeyComparator const&)", + "coverage": 1 + } + ] + }, + { + "name": "table_builder.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::TableBuilder::ok() const", + "coverage": 1 + } + ] + }, + { + "name": "filter_policy.cc", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::FilterPolicy::~FilterPolicy()", + "coverage": 1 + } + ] + }, + { + "name": "table.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::Table::Table(leveldb::Table::Rep*)", + "coverage": 1 + } + ] + }, + { + "name": "slice.h", + "coverage": 1, + "type": "objc", + "functions": [ + { + "name": "leveldb::Slice::Slice()", + "coverage": 1 + }, + { + "name": "leveldb::Slice::Slice(char const*, unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::Slice(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::Slice(char const*)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::data() const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::size() const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::empty() const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::operator[](unsigned long) const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::clear()", + "coverage": 1 + }, + { + "name": "leveldb::Slice::remove_prefix(unsigned long)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::ToString() const", + "coverage": 1 + }, + { + "name": "leveldb::Slice::starts_with(leveldb::Slice const&) const", + "coverage": 1 + }, + { + "name": "leveldb::operator==(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::operator!=(leveldb::Slice const&, leveldb::Slice const&)", + "coverage": 1 + }, + { + "name": "leveldb::Slice::compare(leveldb::Slice const&) const", + "coverage": 1 + } + ] + } + ] + }, + { + "name": "nanopb.framework", + "coverage": 0.15256797583081572, + "files": [ + { + "name": "pb_decode.c", + "coverage": 0, + "type": "objc", + "functions": [ + { + "name": "buf_read", + "coverage": 0 + }, + { + "name": "pb_read", + "coverage": 0 + }, + { + "name": "pb_readbyte", + "coverage": 0 + }, + { + "name": "pb_istream_from_buffer", + "coverage": 0 + }, + { + "name": "pb_decode_varint32_eof", + "coverage": 0 + }, + { + "name": "pb_decode_varint32", + "coverage": 0 + }, + { + "name": "pb_decode_varint", + "coverage": 0 + }, + { + "name": "pb_skip_varint", + "coverage": 0 + }, + { + "name": "pb_skip_string", + "coverage": 0 + }, + { + "name": "pb_decode_tag", + "coverage": 0 + }, + { + "name": "pb_skip_field", + "coverage": 0 + }, + { + "name": "read_raw_value", + "coverage": 0 + }, + { + "name": "pb_make_string_substream", + "coverage": 0 + }, + { + "name": "pb_close_string_substream", + "coverage": 0 + }, + { + "name": "decode_static_field", + "coverage": 0 + }, + { + "name": "allocate_field", + "coverage": 0 + }, + { + "name": "initialize_pointer_field", + "coverage": 0 + }, + { + "name": "decode_pointer_field", + "coverage": 0 + }, + { + "name": "decode_callback_field", + "coverage": 0 + }, + { + "name": "decode_field", + "coverage": 0 + }, + { + "name": "iter_from_extension", + "coverage": 0 + }, + { + "name": "default_extension_decoder", + "coverage": 0 + }, + { + "name": "decode_extension", + "coverage": 0 + }, + { + "name": "find_extension_field", + "coverage": 0 + }, + { + "name": "pb_field_set_to_default", + "coverage": 0 + }, + { + "name": "pb_message_set_to_defaults", + "coverage": 0 + }, + { + "name": "pb_decode_noinit", + "coverage": 0 + }, + { + "name": "pb_decode", + "coverage": 0 + }, + { + "name": "pb_decode_delimited_noinit", + "coverage": 0 + }, + { + "name": "pb_decode_delimited", + "coverage": 0 + }, + { + "name": "pb_decode_nullterminated", + "coverage": 0 + }, + { + "name": "pb_release_union_field", + "coverage": 0 + }, + { + "name": "pb_release_single_field", + "coverage": 0 + }, + { + "name": "pb_release", + "coverage": 0 + }, + { + "name": "pb_decode_svarint", + "coverage": 0 + }, + { + "name": "pb_decode_fixed32", + "coverage": 0 + }, + { + "name": "pb_decode_fixed64", + "coverage": 0 + }, + { + "name": "pb_dec_varint", + "coverage": 0 + }, + { + "name": "pb_dec_uvarint", + "coverage": 0 + }, + { + "name": "pb_dec_svarint", + "coverage": 0 + }, + { + "name": "pb_dec_fixed32", + "coverage": 0 + }, + { + "name": "pb_dec_fixed64", + "coverage": 0 + }, + { + "name": "pb_dec_bytes", + "coverage": 0 + }, + { + "name": "pb_dec_string", + "coverage": 0 + }, + { + "name": "pb_dec_submessage", + "coverage": 0 + }, + { + "name": "pb_dec_fixed_length_bytes", + "coverage": 0 + } + ] + }, + { + "name": "pb_encode.c", + "coverage": 0.3941717791411043, + "type": "objc", + "functions": [ + { + "name": "buf_write", + "coverage": 1 + }, + { + "name": "pb_ostream_from_buffer", + "coverage": 1 + }, + { + "name": "pb_write", + "coverage": 1 + }, + { + "name": "encode_array", + "coverage": 0 + }, + { + "name": "pb_check_proto3_default_value", + "coverage": 0 + }, + { + "name": "encode_basic_field", + "coverage": 0.39285714285714285 + }, + { + "name": "encode_callback_field", + "coverage": 1 + }, + { + "name": "encode_field", + "coverage": 0.9285714285714286 + }, + { + "name": "default_extension_encoder", + "coverage": 0 + }, + { + "name": "encode_extension_field", + "coverage": 0.4 + }, + { + "name": "pb_const_cast", + "coverage": 1 + }, + { + "name": "pb_encode", + "coverage": 0.8636363636363636 + }, + { + "name": "pb_encode_delimited", + "coverage": 0 + }, + { + "name": "pb_encode_nullterminated", + "coverage": 0 + }, + { + "name": "pb_get_encoded_size", + "coverage": 0 + }, + { + "name": "pb_encode_varint", + "coverage": 1 + }, + { + "name": "pb_encode_svarint", + "coverage": 0 + }, + { + "name": "pb_encode_fixed32", + "coverage": 0 + }, + { + "name": "pb_encode_fixed64", + "coverage": 0 + }, + { + "name": "pb_encode_tag", + "coverage": 1 + }, + { + "name": "pb_encode_tag_for_field", + "coverage": 0.8387096774193549 + }, + { + "name": "pb_encode_string", + "coverage": 0.8333333333333334 + }, + { + "name": "pb_encode_submessage", + "coverage": 0.8541666666666666 + }, + { + "name": "pb_enc_varint", + "coverage": 0.9375 + }, + { + "name": "pb_enc_uvarint", + "coverage": 0.625 + }, + { + "name": "pb_enc_svarint", + "coverage": 0 + }, + { + "name": "pb_enc_fixed64", + "coverage": 0 + }, + { + "name": "pb_enc_fixed32", + "coverage": 0 + }, + { + "name": "pb_enc_bytes", + "coverage": 0 + }, + { + "name": "pb_enc_string", + "coverage": 0 + }, + { + "name": "pb_enc_submessage", + "coverage": 1 + }, + { + "name": "pb_enc_fixed_length_bytes", + "coverage": 0 + } + ] + }, + { + "name": "pb_common.c", + "coverage": 0.5542168674698795, + "type": "objc", + "functions": [ + { + "name": "pb_field_iter_begin", + "coverage": 1 + }, + { + "name": "pb_field_iter_next", + "coverage": 0.6428571428571429 + }, + { + "name": "pb_field_iter_find", + "coverage": 0 + } + ] + } + ] + } + ] +} \ No newline at end of file From 7d9df0fb3f1c6d32db199c01f80d666ed3dbfc18 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Tue, 26 Mar 2019 10:21:30 -0700 Subject: [PATCH 093/214] Revert "Disable Facebook API tests (#2547)" (#2628) This reverts commit ed2b99a26c740bdc0f268ac3244b94271828a16d. --- Example/Auth/ApiTests/FacebookAuthTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/Auth/ApiTests/FacebookAuthTests.m b/Example/Auth/ApiTests/FacebookAuthTests.m index ba865bbb5f0..cb74fe71461 100644 --- a/Example/Auth/ApiTests/FacebookAuthTests.m +++ b/Example/Auth/ApiTests/FacebookAuthTests.m @@ -36,7 +36,7 @@ @interface FacebookAuthTests : FIRAuthApiTestsBase @implementation FacebookAuthTests -- (void)DISABLE_testSignInWithFaceboook { +- (void)testSignInWithFaceboook { FIRAuth *auth = [FIRAuth auth]; if (!auth) { XCTFail(@"Could not obtain auth object."); @@ -76,7 +76,7 @@ - (void)DISABLE_testSignInWithFaceboook { [self deleteFacebookTestingAccountbyId:facebookAccountId]; } -- (void)DISABLE_testLinkAnonymousAccountToFacebookAccount { +- (void)testLinkAnonymousAccountToFacebookAccount { FIRAuth *auth = [FIRAuth auth]; if (!auth) { XCTFail(@"Could not obtain auth object."); From 67c35d2db73caca68fe33815da097a2ff93368f0 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Tue, 26 Mar 2019 13:45:20 -0400 Subject: [PATCH 094/214] FIRApp: thread safety fixes (#2639) --- Firebase/Core/FIRApp.m | 59 +++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index 01324cea98e..a38e8f3366c 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -166,9 +166,11 @@ + (void)configureWithName:(NSString *)name options:(FIROptions *)options { } } - if (sAllApps && sAllApps[name]) { - [NSException raise:kFirebaseCoreErrorDomain - format:@"App named %@ has already been configured.", name]; + @synchronized(self) { + if (sAllApps && sAllApps[name]) { + [NSException raise:kFirebaseCoreErrorDomain + format:@"App named %@ has already been configured.", name]; + } } FIRLogDebug(kFIRLoggerCore, @"I-COR000002", @"Configuring app named %@", name); @@ -214,18 +216,19 @@ + (NSDictionary *)allApps { if (!sAllApps) { FIRLogError(kFIRLoggerCore, @"I-COR000005", @"No app has been configured yet."); } - NSDictionary *dict = [NSDictionary dictionaryWithDictionary:sAllApps]; - return dict; + return [sAllApps copy]; } } // Public only for tests + (void)resetApps { - sDefaultApp = nil; - [sAllApps removeAllObjects]; - sAllApps = nil; - [sLibraryVersions removeAllObjects]; - sLibraryVersions = nil; + @synchronized(self) { + sDefaultApp = nil; + [sAllApps removeAllObjects]; + sAllApps = nil; + [sLibraryVersions removeAllObjects]; + sLibraryVersions = nil; + } } - (void)deleteApp:(FIRAppVoidBoolCallback)completion { @@ -423,8 +426,10 @@ + (void)sendNotificationsToSDKs:(FIRApp *)app { // This is the new way of sending information to SDKs. // TODO: Do we want this on a background thread, maybe? - for (Class library in sRegisteredAsConfigurable) { - [library configureWithApp:app]; + @synchronized(self) { + for (Class library in sRegisteredAsConfigurable) { + [library configureWithApp:app]; + } } } @@ -476,10 +481,12 @@ + (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString * // add the name/version pair to the dictionary. if ([name rangeOfCharacterFromSet:disallowedSet].location == NSNotFound && [version rangeOfCharacterFromSet:disallowedSet].location == NSNotFound) { - if (!sLibraryVersions) { - sLibraryVersions = [[NSMutableDictionary alloc] init]; + @synchronized(self) { + if (!sLibraryVersions) { + sLibraryVersions = [[NSMutableDictionary alloc] init]; + } + sLibraryVersions[name] = version; } - sLibraryVersions[name] = version; } else { FIRLogError(kFIRLoggerCore, @"I-COR000027", @"The library name (%@) or version number (%@) contain invalid characters. " @@ -508,20 +515,24 @@ + (void)registerInternalLibrary:(nonnull Class)library dispatch_once(&onceToken, ^{ sRegisteredAsConfigurable = [[NSMutableArray alloc] init]; }); - [sRegisteredAsConfigurable addObject:library]; + @synchronized(self) { + [sRegisteredAsConfigurable addObject:library]; + } } [self registerLibrary:name withVersion:version]; } + (NSString *)firebaseUserAgent { - NSMutableArray *libraries = - [[NSMutableArray alloc] initWithCapacity:sLibraryVersions.count]; - for (NSString *libraryName in sLibraryVersions) { - [libraries - addObject:[NSString stringWithFormat:@"%@/%@", libraryName, sLibraryVersions[libraryName]]]; - } - [libraries sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - return [libraries componentsJoinedByString:@" "]; + @synchronized(self) { + NSMutableArray *libraries = + [[NSMutableArray alloc] initWithCapacity:sLibraryVersions.count]; + for (NSString *libraryName in sLibraryVersions) { + [libraries addObject:[NSString stringWithFormat:@"%@/%@", libraryName, + sLibraryVersions[libraryName]]]; + } + [libraries sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + return [libraries componentsJoinedByString:@" "]; + } } - (void)checkExpectedBundleID { From fd5ea77b202cc6414550d302bef8b410123ed743 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 26 Mar 2019 10:56:36 -0700 Subject: [PATCH 095/214] Prepare for macOS test support (#2623) * Add deployment targets for osx * Fix bundle path to make spec tests work On iOS, resources are directly within the bundle so the bundlePath and the resourcePath are the same. On macOS, resources are in a Contents/Resources subfolder so enumerating from bundlePath causes us to look for names like Contents/Resources/offline_spec_test, which doesn't work. * Fix documentation warnings that show up under the macOS build --- Firestore/Example/GoogleBenchmark.podspec | 2 ++ Firestore/Example/GoogleTest.podspec | 2 ++ Firestore/Example/LibFuzzer.podspec | 1 + Firestore/Example/ProtobufCpp.podspec | 3 +++ .../Example/Tests/SpecTests/FSTSpecTests.mm | 2 +- .../firebase/firestore/FSTGoogleTestTests.mm | 17 +++++++++-------- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Firestore/Example/GoogleBenchmark.podspec b/Firestore/Example/GoogleBenchmark.podspec index d29e77048da..62f4d5c59e5 100644 --- a/Firestore/Example/GoogleBenchmark.podspec +++ b/Firestore/Example/GoogleBenchmark.podspec @@ -34,6 +34,8 @@ Google's C++ benchmark framework. } s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.10' + s.requires_arc = false s.public_header_files = [ diff --git a/Firestore/Example/GoogleTest.podspec b/Firestore/Example/GoogleTest.podspec index 0ecba3d6aa7..63dce9b2321 100644 --- a/Firestore/Example/GoogleTest.podspec +++ b/Firestore/Example/GoogleTest.podspec @@ -34,6 +34,8 @@ Google's C++ test framework. } s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.10' + s.requires_arc = false # Exclude include/gtest/internal/custom files from public headers. These diff --git a/Firestore/Example/LibFuzzer.podspec b/Firestore/Example/LibFuzzer.podspec index 16e9a1e74d7..528d73b1410 100644 --- a/Firestore/Example/LibFuzzer.podspec +++ b/Firestore/Example/LibFuzzer.podspec @@ -31,6 +31,7 @@ Pod::Spec.new do |s| # LibFuzzer uses thread_local which does not appear to be supported before # iOS 9. s.ios.deployment_target = '9.0' + s.osx.deployment_target = '10.10' # Check out only libFuzzer folder. s.source = { diff --git a/Firestore/Example/ProtobufCpp.podspec b/Firestore/Example/ProtobufCpp.podspec index c809c0690d5..921c0b3e55d 100644 --- a/Firestore/Example/ProtobufCpp.podspec +++ b/Firestore/Example/ProtobufCpp.podspec @@ -29,6 +29,9 @@ Pod::Spec.new do |s| :tag => "v#{s.version}" } + s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.10' + s.source_files = 'src/**/*.{h,cc}' s.exclude_files = # skip test files. (Yes, the test files are intermixed with # the source. No there doesn't seem to be a common/simple diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index fde73c3fcbf..a460b560227 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -728,7 +728,7 @@ - (void)testSpecTests { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; NSFileManager *fs = [NSFileManager defaultManager]; BOOL exclusiveMode = NO; - for (NSString *file in [fs enumeratorAtPath:[bundle bundlePath]]) { + for (NSString *file in [fs enumeratorAtPath:[bundle resourcePath]]) { if (![@"json" isEqual:[file pathExtension]]) { continue; } diff --git a/Firestore/core/test/firebase/firestore/FSTGoogleTestTests.mm b/Firestore/core/test/firebase/firestore/FSTGoogleTestTests.mm index bb2f8364909..6895f23923c 100644 --- a/Firestore/core/test/firebase/firestore/FSTGoogleTestTests.mm +++ b/Firestore/core/test/firebase/firestore/FSTGoogleTestTests.mm @@ -50,7 +50,7 @@ @interface GoogleTests : XCTestCase // If the user focuses on GoogleTests itself, this means force all C++ tests to // run. -BOOL forceAllTests = NO; +bool forceAllTests = false; /** * Loads this XCTest runner's configuration file and figures out which tests to @@ -216,7 +216,7 @@ void ReportTestResult(XCTestCase *self, SEL _cmd) { atLine:(part.line_number() > 0 ? part.line_number() : 0) - expected:YES]; + expected:true]; } } @@ -225,8 +225,9 @@ void ReportTestResult(XCTestCase *self, SEL _cmd) { * Each TestInfo (which represents an indivudal test method execution) is * translated into a method on the test case. * - * @param The testing::TestCase of interest to translate. - * @param A map of TestInfoKeys to testing::TestInfos, populated by this method. + * @param testCase The testing::TestCase of interest to translate. + * @param infoMap A map of TestInfoKeys to testing::TestInfos, populated by this + * method. * * @return A new Class that's a subclass of XCTestCase, that's been registered * with the Objective-C runtime. @@ -304,15 +305,15 @@ void RunGoogleTestTests() { // Convert XCTest's testToRun set to the equivalent --gtest_filter flag. // - // Note that we only set forceAllTests to YES if the user specifically focused - // on GoogleTests. This prevents XCTest double-counting test cases (and - // failures) when a user asks for all tests. + // Note that we only set forceAllTests to true if the user specifically + // focused on GoogleTests. This prevents XCTest double-counting test cases + // (and failures) when a user asks for all tests. NSSet *allTests = [NSSet setWithObject:masterTestCaseName]; NSSet *testsToRun = LoadXCTestConfigurationTestsToRun(); if (testsToRun) { if ([allTests isEqual:testsToRun]) { NSLog(@"Forcing all tests to run"); - forceAllTests = YES; + forceAllTests = true; } else { NSString *filters = CreateTestFiltersFromTestsToRun(testsToRun); NSLog(@"Using --gtest_filter=%@", filters); From ba332be794783dcab9f8b1d48bc7d43b3a6b3595 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 26 Mar 2019 11:54:03 -0700 Subject: [PATCH 096/214] Port FSTAsyncQueryListener to C++ (#2620) * Test that shows a leak. * Add EventListener to match Android. * Use AsyncEventListener in FSTQueryListenerTests * Remove FSTAsyncQueryListener * Use EventListener throughout --- .../Tests/Core/FSTEventManagerTests.mm | 24 +- .../Tests/Core/FSTQueryListenerTests.mm | 35 +-- .../API/FIRFirestoreSourceTests.mm | 24 ++ .../SpecTests/FSTSyncEngineTestDriver.mm | 2 +- Firestore/Source/API/FIRDocumentReference.mm | 31 ++- Firestore/Source/API/FIRQuery.mm | 52 ++--- Firestore/Source/Core/FSTEventManager.mm | 1 - Firestore/Source/Core/FSTFirestoreClient.h | 8 +- Firestore/Source/Core/FSTFirestoreClient.mm | 19 +- .../Source/Util/FSTAsyncQueryListener.mm | 62 ----- .../firestore/api/document_reference.h | 7 +- .../firestore/api/document_reference.mm | 214 ++++++++++-------- .../firestore/api/document_snapshot.h | 4 + .../firestore/api/listener_registration.h | 26 +-- .../firestore/api/listener_registration.mm | 20 +- .../firebase/firestore/core/event_listener.h | 144 ++++++++++++ .../firebase/firestore/core/query_listener.h | 38 +++- .../firebase/firestore/core/query_listener.mm | 6 +- .../firebase/firestore/core/view_snapshot.h | 10 +- .../firebase/firestore/testutil/xcgmock.h | 1 - 20 files changed, 441 insertions(+), 287 deletions(-) delete mode 100644 Firestore/Source/Util/FSTAsyncQueryListener.mm create mode 100644 Firestore/core/src/firebase/firestore/core/event_listener.h diff --git a/Firestore/Example/Tests/Core/FSTEventManagerTests.mm b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm index 875cff98880..2413b00b3cd 100644 --- a/Firestore/Example/Tests/Core/FSTEventManagerTests.mm +++ b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm @@ -35,25 +35,26 @@ #include "Firestore/core/src/firebase/firestore/util/statusor.h" #include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h" +using firebase::firestore::core::EventListener; using firebase::firestore::core::ListenOptions; using firebase::firestore::core::ViewSnapshot; -using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::DocumentSet; using firebase::firestore::model::OnlineState; using firebase::firestore::util::StatusOr; +using firebase::firestore::util::StatusOrCallback; using testing::ElementsAre; NS_ASSUME_NONNULL_BEGIN namespace { -ViewSnapshotHandler NoopViewSnapshotHandler() { - return [](const StatusOr &) {}; +ViewSnapshot::Listener NoopViewSnapshotHandler() { + return EventListener::Create([](const StatusOr &) {}); } std::shared_ptr NoopQueryListener(FSTQuery *query) { - return QueryListener::Create(query, NoopViewSnapshotHandler()); + return QueryListener::Create(query, ListenOptions::DefaultOptions(), NoopViewSnapshotHandler()); } } // namespace @@ -112,17 +113,14 @@ - (void)testNotifiesListenersInTheRightOrder { FSTQuery *query2 = FSTTestQuery("bar/baz"); NSMutableArray *eventOrder = [NSMutableArray array]; - auto listener1 = QueryListener::Create(query1, [eventOrder](const StatusOr &) { - [eventOrder addObject:@"listener1"]; - }); + auto listener1 = QueryListener::Create( + query1, [eventOrder](StatusOr) { [eventOrder addObject:@"listener1"]; }); - auto listener2 = QueryListener::Create(query2, [eventOrder](const StatusOr &) { - [eventOrder addObject:@"listener2"]; - }); + auto listener2 = QueryListener::Create( + query2, [eventOrder](StatusOr) { [eventOrder addObject:@"listener2"]; }); - auto listener3 = QueryListener::Create(query1, [eventOrder](const StatusOr &) { - [eventOrder addObject:@"listener3"]; - }); + auto listener3 = QueryListener::Create( + query1, [eventOrder](StatusOr) { [eventOrder addObject:@"listener3"]; }); FSTSyncEngine *syncEngineMock = OCMClassMock([FSTSyncEngine class]); FSTEventManager *eventManager = [FSTEventManager eventManagerWithSyncEngine:syncEngineMock]; diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index eaea44453f9..04ee1bb67a8 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -24,11 +24,11 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTView.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Util/FSTAsyncQueryListener.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/include/firebase/firestore/firestore_errors.h" +#include "Firestore/core/src/firebase/firestore/core/event_listener.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/types.h" @@ -40,10 +40,12 @@ #include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h" using firebase::firestore::FirestoreErrorCode; +using firebase::firestore::core::AsyncEventListener; +using firebase::firestore::core::EventListener; using firebase::firestore::core::DocumentViewChange; +using firebase::firestore::core::EventListener; using firebase::firestore::core::ListenOptions; using firebase::firestore::core::ViewSnapshot; -using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::DocumentSet; using firebase::firestore::model::OnlineState; @@ -72,10 +74,11 @@ ViewSnapshot ExcludingMetadataChanges(const ViewSnapshot &snapshot) { }; } -ViewSnapshotHandler Accumulating(std::vector *values) { - return [values](const StatusOr &maybe_snapshot) { - values->push_back(maybe_snapshot.ValueOrDie()); - }; +ViewSnapshot::Listener Accumulating(std::vector *values) { + return EventListener::Create( + [values](const StatusOr &maybe_snapshot) { + values->push_back(maybe_snapshot.ValueOrDie()); + }); } } // namespace @@ -166,26 +169,26 @@ - (void)testRaisesEventForEmptyCollectionAfterSync { } - (void)testMutingAsyncListenerPreventsAllSubsequentEvents { - __block std::vector accum; + std::vector accum; FSTQuery *query = FSTTestQuery("rooms/Eros"); FSTDocument *doc1 = FSTTestDoc("rooms/Eros", 3, @{@"name" : @"Eros"}, FSTDocumentStateSynced); FSTDocument *doc2 = FSTTestDoc("rooms/Eros", 4, @{@"name" : @"Eros2"}, FSTDocumentStateSynced); - __block FSTAsyncQueryListener *listener = [[FSTAsyncQueryListener alloc] - initWithExecutor:_executor.get() - snapshotHandler:^(const StatusOr &maybe_snapshot) { - accum.push_back(maybe_snapshot.ValueOrDie()); - [listener mute]; - }]; + std::shared_ptr> listener = + AsyncEventListener::Create( + _executor.get(), EventListener::Create( + [&accum, &listener](const StatusOr &maybe_snapshot) { + accum.push_back(maybe_snapshot.ValueOrDie()); + listener->Mute(); + })); FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; ViewSnapshot viewSnapshot1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt).value(); ViewSnapshot viewSnapshot2 = FSTTestApplyChanges(view, @[ doc2 ], absl::nullopt).value(); - ViewSnapshotHandler handler = listener.asyncSnapshotHandler; - handler(viewSnapshot1); - handler(viewSnapshot2); + listener->OnEvent(viewSnapshot1); + listener->OnEvent(viewSnapshot2); // Drain queue XCTestExpectation *expectation = [self expectationWithDescription:@"Queue drained"]; diff --git a/Firestore/Example/Tests/Integration/API/FIRFirestoreSourceTests.mm b/Firestore/Example/Tests/Integration/API/FIRFirestoreSourceTests.mm index 0074752b359..3c93f4d542c 100644 --- a/Firestore/Example/Tests/Integration/API/FIRFirestoreSourceTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRFirestoreSourceTests.mm @@ -69,6 +69,30 @@ - (void)testGetCollectionWhileOnlineWithDefaultSource { ])); } +- (void)testGetDocumentError { + FIRDocumentReference *doc = [self.db documentWithPath:@"foo/__invalid__"]; + + XCTestExpectation *completed = [self expectationWithDescription:@"get completed"]; + [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + [completed fulfill]; + }]; + + [self awaitExpectations]; +} + +- (void)testGetCollectionError { + FIRCollectionReference *col = [self.db collectionWithPath:@"__invalid__"]; + + XCTestExpectation *completed = [self expectationWithDescription:@"get completed"]; + [col getDocumentsWithCompletion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + [completed fulfill]; + }]; + + [self awaitExpectations]; +} + - (void)testGetDocumentWhileOfflineWithDefaultSource { FIRDocumentReference *doc = [self documentRef]; diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index f1e42579480..c6737d8ba97 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -351,7 +351,7 @@ - (TargetId)addUserListenerWithQuery:(FSTQuery *)query { // TODO(dimond): Allow customizing listen options in spec tests // TODO(dimond): Change spec tests to verify isFromCache on snapshots ListenOptions options = ListenOptions::FromIncludeMetadataChanges(true); - auto listener = std::make_shared( + auto listener = QueryListener::Create( query, options, [self, query](const StatusOr &maybe_snapshot) { FSTQueryEvent *event = [[FSTQueryEvent alloc] init]; event.query = query; diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index 7d5d1e3e054..c1e838fa6b6 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -34,6 +34,7 @@ #include "Firestore/core/src/firebase/firestore/api/document_reference.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" +#include "Firestore/core/src/firebase/firestore/core/event_listener.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/precondition.h" @@ -48,6 +49,7 @@ using firebase::firestore::api::DocumentReference; using firebase::firestore::api::DocumentSnapshot; using firebase::firestore::api::Firestore; +using firebase::firestore::core::EventListener; using firebase::firestore::core::ListenOptions; using firebase::firestore::core::ParsedSetData; using firebase::firestore::core::ParsedUpdateData; @@ -213,21 +215,30 @@ - (void)getDocumentWithSource:(FIRFirestoreSource)source listener:(FIRDocumentSnapshotBlock) listener { ListenerRegistration result = _documentReference.AddSnapshotListener( - [self wrapDocumentSnapshotBlock:listener], std::move(internalOptions)); + std::move(internalOptions), [self wrapDocumentSnapshotBlock:listener]); return [[FSTListenerRegistration alloc] initWithRegistration:std::move(result)]; } -- (StatusOrCallback)wrapDocumentSnapshotBlock:(FIRDocumentSnapshotBlock)block { - FIRFirestore *firestore = self.firestore; - return [block, firestore](StatusOr maybe_snapshot) { - if (maybe_snapshot.ok()) { - FIRDocumentSnapshot *result = - [[FIRDocumentSnapshot alloc] initWithSnapshot:std::move(maybe_snapshot).ValueOrDie()]; - block(result, nil); - } else { - block(nil, util::MakeNSError(maybe_snapshot.status())); +- (DocumentSnapshot::Listener)wrapDocumentSnapshotBlock:(FIRDocumentSnapshotBlock)block { + class Converter : public EventListener { + public: + explicit Converter(FIRDocumentSnapshotBlock block) : block_(block) { } + + void OnEvent(StatusOr maybe_snapshot) override { + if (maybe_snapshot.ok()) { + FIRDocumentSnapshot *result = + [[FIRDocumentSnapshot alloc] initWithSnapshot:std::move(maybe_snapshot).ValueOrDie()]; + block_(result, nil); + } else { + block_(nil, util::MakeNSError(maybe_snapshot.status())); + } + } + + private: + FIRDocumentSnapshotBlock block_; }; + return absl::make_unique(block); } @end diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 2b39e45ae06..507e1f347b2 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -38,7 +38,6 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTFieldValue.h" -#import "Firestore/Source/Util/FSTAsyncQueryListener.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" @@ -50,8 +49,9 @@ #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::core::AsyncEventListener; +using firebase::firestore::core::EventListener; using firebase::firestore::core::ViewSnapshot; -using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldPath; using firebase::firestore::model::ResourcePath; @@ -175,34 +175,34 @@ ListenOptions listenOptions( Firestore *firestore = self.firestore.wrapped; FSTQuery *query = self.query; - ViewSnapshotHandler snapshotHandler = [listener, firestore, - query](const StatusOr &maybe_snapshot) { - if (!maybe_snapshot.status().ok()) { - listener(nil, MakeNSError(maybe_snapshot.status())); - return; - } - ViewSnapshot snapshot = maybe_snapshot.ValueOrDie(); - SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache()); - - listener([[FIRQuerySnapshot alloc] initWithFirestore:firestore - originalQuery:query - snapshot:std::move(snapshot) - metadata:std::move(metadata)], - nil); - }; + // Convert from ViewSnapshots to QuerySnapshots. + auto view_listener = EventListener::Create( + [listener, firestore, query](StatusOr maybe_snapshot) { + if (!maybe_snapshot.status().ok()) { + listener(nil, MakeNSError(maybe_snapshot.status())); + return; + } + + ViewSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie(); + SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache()); + + listener([[FIRQuerySnapshot alloc] initWithFirestore:firestore + originalQuery:query + snapshot:std::move(snapshot) + metadata:std::move(metadata)], + nil); + }); - FSTAsyncQueryListener *asyncListener = - [[FSTAsyncQueryListener alloc] initWithExecutor:self.firestore.client.userExecutor - snapshotHandler:std::move(snapshotHandler)]; + // Call the view_listener on the user Executor. + auto async_listener = AsyncEventListener::Create(firestore->client().userExecutor, + std::move(view_listener)); - std::shared_ptr internalListener = - [firestore->client() listenToQuery:query - options:internalOptions - viewSnapshotHandler:[asyncListener asyncSnapshotHandler]]; + std::shared_ptr query_listener = + [firestore->client() listenToQuery:query options:internalOptions listener:async_listener]; return [[FSTListenerRegistration alloc] - initWithRegistration:ListenerRegistration(firestore->client(), asyncListener, - std::move(internalListener))]; + initWithRegistration:ListenerRegistration(firestore->client(), std::move(async_listener), + std::move(query_listener))]; } - (FIRQuery *)queryWhereField:(NSString *)field isEqualTo:(id)value { diff --git a/Firestore/Source/Core/FSTEventManager.mm b/Firestore/Source/Core/FSTEventManager.mm index aab3c0bbec2..93f5999f3cb 100644 --- a/Firestore/Source/Core/FSTEventManager.mm +++ b/Firestore/Source/Core/FSTEventManager.mm @@ -36,7 +36,6 @@ namespace objc = firebase::firestore::util::objc; using firebase::firestore::core::DocumentViewChange; using firebase::firestore::core::ViewSnapshot; -using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::model::OnlineState; using firebase::firestore::model::TargetId; using firebase::firestore::util::MakeStatus; diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index 8c7d97fdfd0..8d4c4cf4310 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -47,8 +47,10 @@ NS_ASSUME_NONNULL_BEGIN +using firebase::firestore::api::DocumentSnapshot; using firebase::firestore::core::ListenOptions; using firebase::firestore::core::QueryListener; +using firebase::firestore::core::ViewSnapshot; /** * FirestoreClient is a top-level class that constructs and owns all of the pieces of the client @@ -84,8 +86,7 @@ using firebase::firestore::core::QueryListener; /** Starts listening to a query. */ - (std::shared_ptr)listenToQuery:(FSTQuery *)query options:(ListenOptions)options - viewSnapshotHandler:(firebase::firestore::core::ViewSnapshotHandler &&) - viewSnapshotHandler; + listener:(ViewSnapshot::SharedListener &&)listener; /** Stops listening to a query previously listened to. */ - (void)removeListener:(const std::shared_ptr &)listener; @@ -95,8 +96,7 @@ using firebase::firestore::core::QueryListener; * doesn't exist, an error will be sent to the completion. */ - (void)getDocumentFromLocalCache:(const firebase::firestore::api::DocumentReference &)doc - completion:(firebase::firestore::util::StatusOrCallback< - firebase::firestore::api::DocumentSnapshot> &&)completion; + completion:(DocumentSnapshot::Listener &&)completion; /** * Retrieves a (possibly empty) set of documents from the cache via the diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index 83c8993cd01..de03ebe8b7c 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -64,7 +64,6 @@ using firebase::firestore::auth::User; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::core::ViewSnapshot; -using firebase::firestore::core::ViewSnapshotHandler; using firebase::firestore::local::LruParams; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKeySet; @@ -314,13 +313,12 @@ - (void)shutdownWithCompletion:(nullable FSTVoidErrorBlock)completion { - (std::shared_ptr)listenToQuery:(FSTQuery *)query options:(ListenOptions)options - viewSnapshotHandler:(ViewSnapshotHandler &&)viewSnapshotHandler { - auto listener = - std::make_shared(query, std::move(options), std::move(viewSnapshotHandler)); + listener:(ViewSnapshot::SharedListener &&)listener { + auto query_listener = QueryListener::Create(query, std::move(options), std::move(listener)); - _workerQueue->Enqueue([self, listener] { [self.eventManager addListener:listener]; }); + _workerQueue->Enqueue([self, query_listener] { [self.eventManager addListener:query_listener]; }); - return listener; + return query_listener; } - (void)removeListener:(const std::shared_ptr &)listener { @@ -328,8 +326,9 @@ - (void)removeListener:(const std::shared_ptr &)listener { } - (void)getDocumentFromLocalCache:(const DocumentReference &)doc - completion:(StatusOrCallback &&)completion { - _workerQueue->Enqueue([self, doc, completion] { + completion:(DocumentSnapshot::Listener &&)completion { + auto shared_completion = absl::ShareUniquePtr(std::move(completion)); + _workerQueue->Enqueue([self, doc, shared_completion] { FSTMaybeDocument *maybeDoc = [self.localStore readDocument:doc.key()]; StatusOr maybe_snapshot; @@ -349,8 +348,8 @@ - (void)getDocumentFromLocalCache:(const DocumentReference &)doc "FIRFirestoreSourceCache to attempt to retrieve the document "}; } - if (completion) { - self->_userExecutor->Execute([=] { completion(std::move(maybe_snapshot)); }); + if (shared_completion) { + self->_userExecutor->Execute([=] { shared_completion->OnEvent(std::move(maybe_snapshot)); }); } }); } diff --git a/Firestore/Source/Util/FSTAsyncQueryListener.mm b/Firestore/Source/Util/FSTAsyncQueryListener.mm deleted file mode 100644 index 8133feeac46..00000000000 --- a/Firestore/Source/Util/FSTAsyncQueryListener.mm +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 "Firestore/Source/Util/FSTAsyncQueryListener.h" - -#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" -#include "Firestore/core/src/firebase/firestore/util/statusor.h" - -using firebase::firestore::core::ViewSnapshot; -using firebase::firestore::core::ViewSnapshotHandler; -using firebase::firestore::util::Executor; -using firebase::firestore::util::StatusOr; - -@implementation FSTAsyncQueryListener { - volatile BOOL _muted; - ViewSnapshotHandler _snapshotHandler; - Executor *_executor; -} - -- (instancetype)initWithExecutor:(Executor *)executor - snapshotHandler:(ViewSnapshotHandler &&)snapshotHandler { - if (self = [super init]) { - _executor = executor; - _snapshotHandler = snapshotHandler; - } - return self; -} - -- (ViewSnapshotHandler)asyncSnapshotHandler { - // Retain `self` strongly in resulting snapshot handler so that even if the - // user releases the `FSTAsyncQueryListener` we'll continue to deliver - // events. This is done specifically to facilitate the common case where - // users just want to turn on notifications "forever" and don't want to have - // to keep track of our handle to keep them going. - return [self](const StatusOr &maybe_snapshot) { - // TODO(c++14): move into lambda. - self->_executor->Execute([self, maybe_snapshot] { - if (!self->_muted) { - self->_snapshotHandler(maybe_snapshot); - } - }); - }; -} - -- (void)mute { - _muted = true; -} - -@end diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.h b/Firestore/core/src/firebase/firestore/api/document_reference.h index 491c31fbb80..d107d452315 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.h +++ b/Firestore/core/src/firebase/firestore/api/document_reference.h @@ -51,8 +51,6 @@ class Firestore; class DocumentReference { public: using Completion = void (^)(NSError* _Nullable error) _Nullable; - using DocumentCompletion = void (^)(FIRDocumentSnapshot* _Nullable document, - NSError* _Nullable error) _Nullable; DocumentReference() = default; DocumentReference(model::ResourcePath path, Firestore* firestore); @@ -87,11 +85,10 @@ class DocumentReference { void DeleteDocument(Completion completion); void GetDocument(FIRFirestoreSource source, - util::StatusOrCallback&& completion); + DocumentSnapshot::Listener&& completion); ListenerRegistration AddSnapshotListener( - util::StatusOrCallback&& listener, - core::ListenOptions options); + core::ListenOptions options, DocumentSnapshot::Listener&& listener); private: Firestore* firestore_ = nullptr; diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index 1925ebfcd7e..3418bc1647e 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -16,6 +16,7 @@ #include "Firestore/core/src/firebase/firestore/api/document_reference.h" +#include // NOLINT(build/c++11) #include #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" @@ -25,7 +26,6 @@ #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTMutation.h" -#import "Firestore/Source/Util/FSTAsyncQueryListener.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" @@ -47,8 +47,9 @@ namespace api { namespace objc = util::objc; +using core::AsyncEventListener; +using core::EventListener; using core::ViewSnapshot; -using core::ViewSnapshotHandler; using model::DocumentKey; using model::Precondition; using model::ResourcePath; @@ -113,9 +114,8 @@ [firestore_->client() writeMutations:{mutation} completion:completion]; } -void DocumentReference::GetDocument( - FIRFirestoreSource source, - StatusOrCallback&& completion) { +void DocumentReference::GetDocument(FIRFirestoreSource source, + DocumentSnapshot::Listener&& completion) { if (source == FIRFirestoreSourceCache) { [firestore_->client() getDocumentFromLocalCache:*this completion:std::move(completion)]; @@ -127,100 +127,124 @@ ListenOptions options( /*include_document_metadata_changes=*/true, /*wait_for_sync_when_online=*/true); - // Create an empty ListenerRegistration captured in the closure below and - // then, once the listen has started, assign to it and signal the listener - // that we succeeded. - // - // TODO(varconst): replace with a synchronization primitive that doesn't - // require libdispatch. See - // https://github.com/firebase/firebase-ios-sdk/blob/3ccbdcdc65c93c4621c045c3c6d15de9dcefa23f/Firestore/Source/Core/FSTFirestoreClient.mm#L161 - // for an example. - dispatch_semaphore_t registered = dispatch_semaphore_create(0); - auto listener_registration = std::make_shared(); - StatusOrCallback listener = - [listener_registration, registered, completion, - source](StatusOr maybe_snapshot) { - if (!maybe_snapshot.ok()) { - completion(std::move(maybe_snapshot)); - return; - } - - DocumentSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie(); - - // Remove query first before passing event to user to avoid user actions - // affecting the now stale query. - dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER); - listener_registration->Remove(); - - if (!snapshot.exists() && snapshot.metadata().from_cache()) { - // TODO(dimond): Reconsider how to raise missing documents when - // offline. If we're online and the document doesn't exist then we - // call the completion with a document with document.exists set to - // false. If we're offline however, we call the completion handler - // with an error. Two options: 1) Cache the negative response from the - // server so we can deliver that even when you're offline. - // 2) Actually call the completion handler with an error if the - // document doesn't exist when you are offline. - completion( - Status{FirestoreErrorCode::Unavailable, - "Failed to get document because the client is offline."}); - } else if (snapshot.exists() && snapshot.metadata().from_cache() && - source == FIRFirestoreSourceServer) { - completion(Status{FirestoreErrorCode::Unavailable, - "Failed to get document from server. (However, " - "this document does exist in the local cache. Run " - "again without setting source to " - "FIRFirestoreSourceServer to retrieve the cached " - "document.)"}); - } else { - completion(std::move(snapshot)); - } - }; - - *listener_registration = - AddSnapshotListener(std::move(listener), std::move(options)); - dispatch_semaphore_signal(registered); + class ListenOnce : public EventListener { + public: + ListenOnce(FIRFirestoreSource source, + DocumentSnapshot::Listener&& completion) + : source_(source), completion_(std::move(completion)) { + } + + void OnEvent(StatusOr maybe_snapshot) override { + if (!maybe_snapshot.ok()) { + completion_->OnEvent(std::move(maybe_snapshot)); + return; + } + + DocumentSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie(); + + // Remove query first before passing event to user to avoid user actions + // affecting the now stale query. + ListenerRegistration registration = + registration_promise_.get_future().get(); + registration.Remove(); + + if (!snapshot.exists() && snapshot.metadata().from_cache()) { + // TODO(dimond): Reconsider how to raise missing documents when + // offline. If we're online and the document doesn't exist then we + // call the completion with a document with document.exists set to + // false. If we're offline however, we call the completion handler + // with an error. Two options: 1) Cache the negative response from the + // server so we can deliver that even when you're offline. + // 2) Actually call the completion handler with an error if the + // document doesn't exist when you are offline. + completion_->OnEvent( + Status{FirestoreErrorCode::Unavailable, + "Failed to get document because the client is offline."}); + } else if (snapshot.exists() && snapshot.metadata().from_cache() && + source_ == FIRFirestoreSourceServer) { + completion_->OnEvent( + Status{FirestoreErrorCode::Unavailable, + "Failed to get document from server. (However, " + "this document does exist in the local cache. Run " + "again without setting source to " + "FIRFirestoreSourceServer to retrieve the cached " + "document.)"}); + } else { + completion_->OnEvent(std::move(snapshot)); + } + } + + void Resolve(ListenerRegistration&& registration) { + registration_promise_.set_value(std::move(registration)); + } + + private: + FIRFirestoreSource source_; + DocumentSnapshot::Listener completion_; + + std::promise registration_promise_; + }; + auto listener = absl::make_unique(source, std::move(completion)); + auto listener_unowned = listener.get(); + + ListenerRegistration registration = + AddSnapshotListener(std::move(options), std::move(listener)); + + listener_unowned->Resolve(std::move(registration)); } ListenerRegistration DocumentReference::AddSnapshotListener( - StatusOrCallback&& listener, ListenOptions options) { - Firestore* firestore = firestore_; + ListenOptions options, DocumentSnapshot::Listener&& user_listener) { FSTQuery* query = [FSTQuery queryWithPath:key_.path()]; - DocumentKey key = key_; - - ViewSnapshotHandler handler = - [key, listener, firestore](const StatusOr& maybe_snapshot) { - if (!maybe_snapshot.ok()) { - listener(maybe_snapshot.status()); - return; - } - - const ViewSnapshot& snapshot = maybe_snapshot.ValueOrDie(); - HARD_ASSERT(snapshot.documents().size() <= 1, - "Too many documents returned on a document query"); - FSTDocument* document = snapshot.documents().GetDocument(key); - - bool has_pending_writes = - document - ? snapshot.mutated_keys().contains(key) - // We don't raise `has_pending_writes` for deleted documents. - : false; - - DocumentSnapshot result{firestore, std::move(key), document, - snapshot.from_cache(), has_pending_writes}; - listener(std::move(result)); - }; - - FSTAsyncQueryListener* async_listener = [[FSTAsyncQueryListener alloc] - initWithExecutor:firestore_->client().userExecutor - snapshotHandler:std::move(handler)]; - - std::shared_ptr internal_listener = [firestore_->client() - listenToQuery:query - options:options - viewSnapshotHandler:[async_listener asyncSnapshotHandler]]; - return ListenerRegistration(firestore_->client(), async_listener, - internal_listener); + + // Convert from ViewSnapshots to DocumentSnapshots. + class Converter : public EventListener { + public: + Converter(DocumentReference* parent, + DocumentSnapshot::Listener&& user_listener) + : parent_(parent), user_listener_(std::move(user_listener)) { + } + + void OnEvent(StatusOr maybe_snapshot) override { + if (!maybe_snapshot.ok()) { + user_listener_->OnEvent(maybe_snapshot.status()); + return; + } + Firestore* firestore = parent_->firestore_; + DocumentKey key = parent_->key_; + + ViewSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie(); + HARD_ASSERT(snapshot.documents().size() <= 1, + "Too many documents returned on a document query"); + FSTDocument* document = snapshot.documents().GetDocument(key); + + bool has_pending_writes = + document ? snapshot.mutated_keys().contains(key) + // We don't raise `has_pending_writes` for deleted documents. + : false; + + DocumentSnapshot result{firestore, std::move(key), document, + snapshot.from_cache(), has_pending_writes}; + user_listener_->OnEvent(std::move(result)); + } + + private: + DocumentReference* parent_; + DocumentSnapshot::Listener user_listener_; + }; + auto view_listener = + absl::make_unique(this, std::move(user_listener)); + + // Call the view_listener on the user Executor. + auto async_listener = AsyncEventListener::Create( + firestore_->client().userExecutor, std::move(view_listener)); + + std::shared_ptr query_listener = + [firestore_->client() listenToQuery:query + options:options + listener:async_listener]; + return ListenerRegistration(firestore_->client(), std::move(async_listener), + std::move(query_listener)); } bool operator==(const DocumentReference& lhs, const DocumentReference& rhs) { diff --git a/Firestore/core/src/firebase/firestore/api/document_snapshot.h b/Firestore/core/src/firebase/firestore/api/document_snapshot.h index 4fcafa2b7d5..e0662e11535 100644 --- a/Firestore/core/src/firebase/firestore/api/document_snapshot.h +++ b/Firestore/core/src/firebase/firestore/api/document_snapshot.h @@ -23,12 +23,14 @@ #import +#include #include #include #import "Firestore/Source/Model/FSTFieldValue.h" #include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" +#include "Firestore/core/src/firebase/firestore/core/event_listener.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" @@ -45,6 +47,8 @@ class Firestore; class DocumentSnapshot { public: + using Listener = std::unique_ptr>; + DocumentSnapshot() = default; DocumentSnapshot(Firestore* firestore, diff --git a/Firestore/core/src/firebase/firestore/api/listener_registration.h b/Firestore/core/src/firebase/firestore/api/listener_registration.h index bb66a9266e4..10fe4d0c316 100644 --- a/Firestore/core/src/firebase/firestore/api/listener_registration.h +++ b/Firestore/core/src/firebase/firestore/api/listener_registration.h @@ -26,9 +26,9 @@ #include #include +#include "Firestore/core/src/firebase/firestore/core/event_listener.h" #include "Firestore/core/src/firebase/firestore/core/query_listener.h" -@class FSTAsyncQueryListener; @class FSTFirestoreClient; NS_ASSUME_NONNULL_BEGIN @@ -54,23 +54,17 @@ namespace api { * * calls to Remove() after we send an error, * * calls to Remove() even after deleting the App in which the listener was * started. - * - * ListenerRegistration is default constructible to facilitate the pattern in - * DocumentReference::GetDocument, where the closure that implements a listener - * needs to be able to use the ListenerRegistration thats returned from - * starting the listener. The default ListenerRegistration acts as a shared - * placeholder that's filled in later once the listener is started. */ class ListenerRegistration { public: - ListenerRegistration() = default; - - ListenerRegistration(FSTFirestoreClient* client, - FSTAsyncQueryListener* async_listener, - std::shared_ptr internal_listener) + ListenerRegistration( + FSTFirestoreClient* client, + std::shared_ptr> + async_listener, + std::shared_ptr query_listener) : client_(client), - async_listener_(async_listener), - internal_listener_(std::move(internal_listener)) { + async_listener_(std::move(async_listener)), + query_listener_(std::move(query_listener)) { } /** @@ -84,10 +78,10 @@ class ListenerRegistration { FSTFirestoreClient* client_ = nil; /** The async listener that is used to mute events synchronously. */ - FSTAsyncQueryListener* async_listener_ = nil; + std::weak_ptr> async_listener_; /** The internal QueryListener that can be used to unlisten the query. */ - std::weak_ptr internal_listener_; + std::weak_ptr query_listener_; }; } // namespace api diff --git a/Firestore/core/src/firebase/firestore/api/listener_registration.mm b/Firestore/core/src/firebase/firestore/api/listener_registration.mm index 4c9ac859ead..b885953df71 100644 --- a/Firestore/core/src/firebase/firestore/api/listener_registration.mm +++ b/Firestore/core/src/firebase/firestore/api/listener_registration.mm @@ -17,23 +17,25 @@ #include "Firestore/core/src/firebase/firestore/api/listener_registration.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" -#import "Firestore/Source/Util/FSTAsyncQueryListener.h" namespace firebase { namespace firestore { namespace api { void ListenerRegistration::Remove() { - [async_listener_ mute]; - async_listener_ = nil; + auto async_listener = async_listener_.lock(); + if (async_listener) { + async_listener->Mute(); + async_listener_.reset(); + } - std::shared_ptr listener = internal_listener_.lock(); - if (listener) { - [client_ removeListener:listener]; - listener.reset(); - internal_listener_.reset(); - client_ = nil; + auto query_listener = query_listener_.lock(); + if (query_listener) { + [client_ removeListener:query_listener]; + query_listener_.reset(); } + + client_ = nil; } } // namespace api diff --git a/Firestore/core/src/firebase/firestore/core/event_listener.h b/Firestore/core/src/firebase/firestore/core/event_listener.h new file mode 100644 index 00000000000..dcb9b8013dc --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/event_listener.h @@ -0,0 +1,144 @@ +/* + * Copyright 2019 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_CORE_EVENT_LISTENER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_EVENT_LISTENER_H_ + +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/executor.h" +#include "Firestore/core/src/firebase/firestore/util/statusor.h" +#include "Firestore/core/src/firebase/firestore/util/statusor_callback.h" +#include "absl/memory/memory.h" + +namespace firebase { +namespace firestore { +namespace core { + +/** + * A general interface for listening to events internally. + */ +template +class EventListener { + public: + static std::unique_ptr> Create( + util::StatusOrCallback callback); + + virtual ~EventListener() { + } + + /** + * OnEvent will be called with the new value or the error if an error + * occurred. + * + * @param maybe_value The value of the event or the error. + */ + virtual void OnEvent(util::StatusOr maybe_value) = 0; +}; + +/** + * A wrapper around another EventListener that dispatches events asynchronously. + */ +template +class AsyncEventListener + : public EventListener, + public std::enable_shared_from_this> { + public: + using DelegateListener = std::unique_ptr>; + + AsyncEventListener(util::Executor* executor, DelegateListener&& delegate) + : executor_(executor), delegate_(std::move(delegate)) { + // std::atomic's constructor is not atomic, so assign after contruction + // (since assignment is atomic). + muted_ = false; + } + + static std::shared_ptr> Create( + util::Executor* executor, DelegateListener&& delegate); + + static std::shared_ptr> Create( + util::Executor* executor, EventListener&& delegate) { + return Create(executor, + absl::make_unique(std::move(delegate))); + } + + void OnEvent(util::StatusOr maybe_value) override; + + /** + * Synchronously mutes the listener and raises no further events. This method + * is thread safe and can be called from any queue. + */ + void Mute(); + + private: + std::atomic muted_; + util::Executor* executor_; + DelegateListener delegate_; +}; + +template +std::unique_ptr> EventListener::Create( + util::StatusOrCallback callback) { + class CallbackEventListener : public EventListener { + public: + explicit CallbackEventListener(util::StatusOrCallback&& callback) + : callback_(std::move(callback)) { + } + + void OnEvent(util::StatusOr maybe_value) override { + callback_(std::move(maybe_value)); + } + + private: + util::StatusOrCallback callback_; + }; + + return absl::make_unique(std::move(callback)); +} + +template +std::shared_ptr> AsyncEventListener::Create( + util::Executor* executor, DelegateListener&& delegate) { + return std::make_shared>(executor, std::move(delegate)); +} + +template +void AsyncEventListener::Mute() { + muted_ = true; +} + +template +void AsyncEventListener::OnEvent(util::StatusOr maybe_value) { + // Retain a strong reference to this. If the EventManager is sending an error + // it will immediately clear its strong reference to this after posting the + // event. The strong reference here allows the AsyncEventListener to survive + // until the executor gets around to calling. + std::shared_ptr> shared_this = this->shared_from_this(); + + executor_->Execute([shared_this, maybe_value]() { + if (!shared_this->muted_) { + shared_this->delegate_->OnEvent(std::move(maybe_value)); + } + }); +} + +} // namespace core +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_EVENT_LISTENER_H_ diff --git a/Firestore/core/src/firebase/firestore/core/query_listener.h b/Firestore/core/src/firebase/firestore/core/query_listener.h index a5a925ac092..60cf13ccd4b 100644 --- a/Firestore/core/src/firebase/firestore/core/query_listener.h +++ b/Firestore/core/src/firebase/firestore/core/query_listener.h @@ -30,6 +30,7 @@ #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/types.h" #include "Firestore/core/src/firebase/firestore/util/status.h" +#include "Firestore/core/src/firebase/firestore/util/statusor_callback.h" #include "absl/types/optional.h" @class FSTQuery; @@ -46,22 +47,36 @@ namespace core { */ class QueryListener { public: - static std::shared_ptr Create(FSTQuery* query, - ListenOptions options, - ViewSnapshotHandler&& listener) { + static std::shared_ptr Create( + FSTQuery* query, + ListenOptions options, + ViewSnapshot::SharedListener&& listener) { return std::make_shared(query, std::move(options), std::move(listener)); } - static std::shared_ptr Create(FSTQuery* query, - ViewSnapshotHandler&& listener) { - return std::make_shared( - query, ListenOptions::DefaultOptions(), std::move(listener)); + static std::shared_ptr Create( + FSTQuery* query, ViewSnapshot::SharedListener&& listener) { + return Create(query, ListenOptions::DefaultOptions(), std::move(listener)); + } + + static std::shared_ptr Create( + FSTQuery* query, + ListenOptions options, + util::StatusOrCallback&& listener) { + auto event_listener = + EventListener::Create(std::move(listener)); + return Create(query, std::move(options), std::move(event_listener)); + } + + static std::shared_ptr Create( + FSTQuery* query, util::StatusOrCallback&& listener) { + return Create(query, ListenOptions::DefaultOptions(), std::move(listener)); } QueryListener(FSTQuery* query, ListenOptions options, - ViewSnapshotHandler&& listener) + ViewSnapshot::SharedListener&& listener) : query_(query), options_(std::move(options)), listener_(std::move(listener)) { @@ -91,8 +106,11 @@ class QueryListener { FSTQuery* query_ = nil; ListenOptions options_; - /** The ViewSnapshotHandler associated with this query listener. */ - ViewSnapshotHandler listener_; + /** + * The EventListener that will process ViewSnapshots associated with this + * query listener. + */ + ViewSnapshot::SharedListener listener_; /** * Initial snapshots (e.g. from cache) may not be propagated to the diff --git a/Firestore/core/src/firebase/firestore/core/query_listener.mm b/Firestore/core/src/firebase/firestore/core/query_listener.mm index 1ee080f0782..df901f83eb6 100644 --- a/Firestore/core/src/firebase/firestore/core/query_listener.mm +++ b/Firestore/core/src/firebase/firestore/core/query_listener.mm @@ -65,14 +65,14 @@ RaiseInitialEvent(snapshot); } } else if (ShouldRaiseEvent(snapshot)) { - listener_(snapshot); + listener_->OnEvent(snapshot); } snapshot_ = std::move(snapshot); } void QueryListener::OnError(Status error) { - listener_(std::move(error)); + listener_->OnEvent(std::move(error)); } void QueryListener::OnOnlineStateChanged(OnlineState online_state) { @@ -137,7 +137,7 @@ snapshot.query(), snapshot.documents(), snapshot.mutated_keys(), snapshot.from_cache(), snapshot.excludes_metadata_changes()); raised_initial_event_ = true; - listener_(modified_snapshot); + listener_->OnEvent(std::move(modified_snapshot)); } } // namespace core diff --git a/Firestore/core/src/firebase/firestore/core/view_snapshot.h b/Firestore/core/src/firebase/firestore/core/view_snapshot.h index 279ea706952..5e4c7defa7a 100644 --- a/Firestore/core/src/firebase/firestore/core/view_snapshot.h +++ b/Firestore/core/src/firebase/firestore/core/view_snapshot.h @@ -23,10 +23,12 @@ #include #include +#include #include #include #include +#include "Firestore/core/src/firebase/firestore/core/event_listener.h" #include "Firestore/core/src/firebase/firestore/immutable/sorted_map.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_key_set.h" @@ -100,17 +102,15 @@ class DocumentViewChangeSet { immutable::SortedMap change_map_; }; -class ViewSnapshot; - -using ViewSnapshotHandler = - std::function&)>; - /** * A view snapshot is an immutable capture of the results of a query and the * changes to them. */ class ViewSnapshot { public: + using Listener = std::unique_ptr>; + using SharedListener = std::shared_ptr>; + ViewSnapshot(FSTQuery* query, model::DocumentSet documents, model::DocumentSet old_documents, diff --git a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h index 381707f3753..37c75806231 100644 --- a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h +++ b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h @@ -144,7 +144,6 @@ OBJC_PRINT_TO(FIRWriteBatch); OBJC_PRINT_TO(FSTArrayRemoveFieldValue); OBJC_PRINT_TO(FSTArrayUnionFieldValue); OBJC_PRINT_TO(FSTArrayValue); -OBJC_PRINT_TO(FSTAsyncQueryListener); OBJC_PRINT_TO(FSTBlobValue); OBJC_PRINT_TO(FSTBooleanValue); OBJC_PRINT_TO(FSTBound); From 961e9b0bfbbc84e3e1d942fa84e1a9b6ad0ec00c Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Tue, 26 Mar 2019 14:54:43 -0400 Subject: [PATCH 097/214] GULLogger: logged issue counting concurrency issue fix (#2627) --- GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m | 4 ++-- GoogleUtilities/Logger/GULLogger.m | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m index 35109e71518..62abd5aa9e1 100644 --- a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m +++ b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m @@ -32,7 +32,7 @@ extern BOOL getGULLoggerDebugMode(void); -extern CFStringRef getGULLoggerUsetDefaultsSuiteName(void); +extern CFStringRef getGULLoggerUserDefaultsSuiteName(void); extern dispatch_queue_t getGULLoggerCounterQueue(void); static NSString *const kMessageCode = @"I-COR000001"; @@ -51,7 +51,7 @@ - (void)setUp { GULResetLogger(); self.loggerDefaults = [[NSUserDefaults alloc] - initWithSuiteName:CFBridgingRelease(getGULLoggerUsetDefaultsSuiteName())]; + initWithSuiteName:CFBridgingRelease(getGULLoggerUserDefaultsSuiteName())]; } - (void)tearDown { diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index 7a88af8cf5e..d92ec673372 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -207,15 +207,15 @@ void GULLogBasic(GULLoggerLevel level, // NSUserDefaults cannot be used due to a bug described in GULUserDefaults // GULUserDefaults cannot be used because GULLogger is a dependency for GULUserDefaults -// We have to use C API deireclty here +// We have to use C API direclty here -CFStringRef getGULLoggerUsetDefaultsSuiteName(void) { +CFStringRef getGULLoggerUserDefaultsSuiteName(void) { return (__bridge CFStringRef) @"GoogleUtilities.Logger.GULLogger"; } NSInteger GULGetUserDefaultsIntegerForKey(NSString *key) { id value = (__bridge_transfer id)CFPreferencesCopyAppValue((__bridge CFStringRef)key, - getGULLoggerUsetDefaultsSuiteName()); + getGULLoggerUserDefaultsSuiteName()); if (![value isKindOfClass:[NSNumber class]]) { return 0; } @@ -226,8 +226,8 @@ NSInteger GULGetUserDefaultsIntegerForKey(NSString *key) { void GULLoggerUserDefaultsSetIntegerForKey(NSInteger count, NSString *key) { NSNumber *countNumber = @(count); CFPreferencesSetAppValue((__bridge CFStringRef)key, (__bridge CFNumberRef)countNumber, - getGULLoggerUsetDefaultsSuiteName()); - CFPreferencesAppSynchronize(getGULLoggerUsetDefaultsSuiteName()); + getGULLoggerUserDefaultsSuiteName()); + CFPreferencesAppSynchronize(getGULLoggerUserDefaultsSuiteName()); } #pragma mark - Number of errors and warnings @@ -238,8 +238,6 @@ dispatch_queue_t getGULLoggerCounterQueue(void) { dispatch_once(&onceToken, ^{ queue = dispatch_queue_create("GoogleUtilities.GULLogger.counterQueue", DISPATCH_QUEUE_CONCURRENT); - dispatch_set_target_queue(queue, - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); }); return queue; From f811fb25852d90a4554f8bf37913c300dfb2971c Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 26 Mar 2019 12:45:11 -0700 Subject: [PATCH 098/214] Update changelog for instanceID release (#2634) --- Firebase/InstanceID/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Firebase/InstanceID/CHANGELOG.md b/Firebase/InstanceID/CHANGELOG.md index 74ff31004cd..ce69a2cb9f8 100644 --- a/Firebase/InstanceID/CHANGELOG.md +++ b/Firebase/InstanceID/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2019-04-02 -- v3.8.1 +- Fixed handling the multiple calls of instanceIDWithHandler. (#2445) +- Fixed a race condition where token kept getting refreshed at app start. (#2438) + # 2019-03-19 -- v3.8.0 - Adding community support for tvOS. (#2428) - Adding Firebase info to checkin. (#2509) From 7c26983cbc86e30ba0ec8e4e80ccb8bc6ca7bbfc Mon Sep 17 00:00:00 2001 From: dmandar Date: Tue, 26 Mar 2019 14:17:18 -0700 Subject: [PATCH 099/214] Update FDL CHANGELOG for M46. (#2646) * Update FDL CHANGELOG for M46. * Add space. --- Firebase/DynamicLinks/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firebase/DynamicLinks/CHANGELOG.md b/Firebase/DynamicLinks/CHANGELOG.md index 1a24eb88195..8d4066baecd 100644 --- a/Firebase/DynamicLinks/CHANGELOG.md +++ b/Firebase/DynamicLinks/CHANGELOG.md @@ -1,3 +1,6 @@ +# v3.4.3 +- Fixed an issue where matchesshortlinkformat was returning true for certain FDL long links. + # v3.4.2 - Fixes an issue with certain analytics attribution parameters not being recorded on an app install. (#2462) From 97a7c47d0523c3551415c9cb1023ade7d103ad11 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Tue, 26 Mar 2019 14:49:11 -0700 Subject: [PATCH 100/214] Update CHANGELOG.md (#2649) --- Firebase/Auth/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md index ea901cb5a56..a4e3c9612ce 100644 --- a/Firebase/Auth/CHANGELOG.md +++ b/Firebase/Auth/CHANGELOG.md @@ -1,3 +1,6 @@ +# v5.4.2 +- Support new error code ERROR_INVALID_PROVIDER_ID. (#2629) + # v5.4.1 - Deprecate Microsoft and Yahoo OAuth Provider ID (#2517) - Fix an issue where an exception was thrown when linking OAuth credentials. (#2521) From c2a10a9f865c244d9e187807c8618a21fc0a5843 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 26 Mar 2019 14:51:57 -0700 Subject: [PATCH 101/214] Add support for Firestore unit tests on macOS (#2641) * Rename Firestore/Example/App/macOS_example to macOS * Update paths to macOS and names to Firestore_Example_macOS * Add Firestore_Tests_macOS target This uses a shared Tests Info.plist. This is important because it configures NSPrincipalClass * Teach sync_project about Firestore_Tests_macOS * Add files to Firestore_Tests_macOS * Add Firestore_Tests_macOS target to the Podfile * Add HEADER_SEARCH_PATHS to match iOS * Force C99/C++11 conformance * pod install after adding Firestore_Tests_macOS * Get macOS tests to link * Add spec test json files to the macOS tests * Remove development code signing * Disable app sandboxing to allow GoogleTest death tests to succeed. --- .../{macOS_example => macOS}/AppDelegate.h | 0 .../{macOS_example => macOS}/AppDelegate.m | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../Base.lproj/MainMenu.xib | 14 +- .../App/{macOS_example => macOS}/Info.plist | 0 .../macOS.entitlements} | 2 +- .../App/{macOS_example => macOS}/main.m | 0 .../Firestore.xcodeproj/project.pbxproj | 691 ++++++++++++++++-- .../Firestore_Example_macOS.xcscheme | 101 +++ .../xcschemes/Firestore_Tests_macOS.xcscheme | 88 +++ Firestore/Example/Podfile | 12 +- scripts/sync_project.rb | 30 +- 13 files changed, 868 insertions(+), 70 deletions(-) rename Firestore/Example/App/{macOS_example => macOS}/AppDelegate.h (100%) rename Firestore/Example/App/{macOS_example => macOS}/AppDelegate.m (100%) rename Firestore/Example/App/{macOS_example => macOS}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename Firestore/Example/App/{macOS_example => macOS}/Assets.xcassets/Contents.json (100%) rename Firestore/Example/App/{macOS_example => macOS}/Base.lproj/MainMenu.xib (98%) rename Firestore/Example/App/{macOS_example => macOS}/Info.plist (100%) rename Firestore/Example/App/{macOS_example/macOS_example.entitlements => macOS/macOS.entitlements} (97%) rename Firestore/Example/App/{macOS_example => macOS}/main.m (100%) create mode 100644 Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_macOS.xcscheme create mode 100644 Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_macOS.xcscheme diff --git a/Firestore/Example/App/macOS_example/AppDelegate.h b/Firestore/Example/App/macOS/AppDelegate.h similarity index 100% rename from Firestore/Example/App/macOS_example/AppDelegate.h rename to Firestore/Example/App/macOS/AppDelegate.h diff --git a/Firestore/Example/App/macOS_example/AppDelegate.m b/Firestore/Example/App/macOS/AppDelegate.m similarity index 100% rename from Firestore/Example/App/macOS_example/AppDelegate.m rename to Firestore/Example/App/macOS/AppDelegate.m diff --git a/Firestore/Example/App/macOS_example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Firestore/Example/App/macOS/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Firestore/Example/App/macOS_example/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Firestore/Example/App/macOS/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Firestore/Example/App/macOS_example/Assets.xcassets/Contents.json b/Firestore/Example/App/macOS/Assets.xcassets/Contents.json similarity index 100% rename from Firestore/Example/App/macOS_example/Assets.xcassets/Contents.json rename to Firestore/Example/App/macOS/Assets.xcassets/Contents.json diff --git a/Firestore/Example/App/macOS_example/Base.lproj/MainMenu.xib b/Firestore/Example/App/macOS/Base.lproj/MainMenu.xib similarity index 98% rename from Firestore/Example/App/macOS_example/Base.lproj/MainMenu.xib rename to Firestore/Example/App/macOS/Base.lproj/MainMenu.xib index 90a42771c8e..ca6b7de54bc 100644 --- a/Firestore/Example/App/macOS_example/Base.lproj/MainMenu.xib +++ b/Firestore/Example/App/macOS/Base.lproj/MainMenu.xib @@ -19,11 +19,11 @@ - + - + - + @@ -37,7 +37,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -668,7 +668,7 @@ - + @@ -678,7 +678,7 @@ - + diff --git a/Firestore/Example/App/macOS_example/Info.plist b/Firestore/Example/App/macOS/Info.plist similarity index 100% rename from Firestore/Example/App/macOS_example/Info.plist rename to Firestore/Example/App/macOS/Info.plist diff --git a/Firestore/Example/App/macOS_example/macOS_example.entitlements b/Firestore/Example/App/macOS/macOS.entitlements similarity index 97% rename from Firestore/Example/App/macOS_example/macOS_example.entitlements rename to Firestore/Example/App/macOS/macOS.entitlements index 40b639e46f5..997a18c9c0a 100644 --- a/Firestore/Example/App/macOS_example/macOS_example.entitlements +++ b/Firestore/Example/App/macOS/macOS.entitlements @@ -3,7 +3,7 @@ com.apple.security.app-sandbox - + com.apple.security.files.user-selected.read-only com.apple.security.network.client diff --git a/Firestore/Example/App/macOS_example/main.m b/Firestore/Example/App/macOS/main.m similarity index 100% rename from Firestore/Example/App/macOS_example/main.m rename to Firestore/Example/App/macOS/main.m diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 91147e35e1f..56065961f82 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -24,17 +24,75 @@ /* Begin PBXBuildFile section */ 020AFD89BB40E5175838BB76 /* local_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F8043813A5D16963EC02B182 /* local_serializer_test.cc */; }; + 036F975093414351FE952F08 /* index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */; }; + 0455FC6E2A281BD755FD933A /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; + 051D3E20184AF195266EF678 /* no_document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908720322E8800CC290A /* no_document_test.cc */; }; 0535C1B65DADAE1CE47FA3CA /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; + 056542AD1D0F78E29E22EFA9 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; }; + 072D805A94E767DE4D371881 /* FSTSyncEngineTestDriver.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */; }; + 07B1E8C62772758BC82FEBEE /* field_mask_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5320A36E1F00BCEB75 /* field_mask_test.cc */; }; + 08D853C9D3A4DC919C55671A /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; }; + 0A6FBE65A7FE048BAD562A15 /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; + 0B7B24194E2131F5C325FE0E /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; }; + 0D67722B43147F775891EA43 /* FSTSerializerBetaTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */; }; + 0DAA255C2FEB387895ADEE12 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; + 10B7426844685A48234C093B /* FSTArraySortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF07E1F3D0B6E003D0CDC /* FSTArraySortedDictionaryTests.m */; }; + 10CA552415BE0954221A1626 /* FSTLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650220A0E93200A2D6A1 /* FSTLRUGarbageCollectorTests.mm */; }; + 113190791F42202FDE1ABC14 /* FIRQuerySnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */; }; + 11F8EE69182C9699E90A9E3D /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; }; + 12158DFCEE09D24B7988A340 /* maybe_document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */; }; 132E3483789344640A52F223 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; 132E3E53179DE287D875F3F2 /* FSTLevelDBTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */; }; 132E3EE56C143B2C9ACB6187 /* FSTLevelDBBenchmarkTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E3BB3D5C42282B4ACFB20 /* FSTLevelDBBenchmarkTests.mm */; }; + 135429EEF1D7FA9D1E329392 /* fake_credentials_provider.cc in Sources */ = {isa = PBXBuildFile; fileRef = B60894F62170207100EBC644 /* fake_credentials_provider.cc */; }; + 156B042479C42DB2C3190C63 /* FSTTreeSortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0841F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m */; }; + 16F52ECC6FA8A0587CD779EB /* user_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93220239654000A432D /* user_test.cc */; }; + 16FE432587C1B40AF08613D2 /* type_traits_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */; }; + 1733601ECCEA33E730DEAF45 /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; }; + 18CF41A17EA3292329E1119D /* FIRGeoPointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */; }; 1CAA9012B25F975D445D5978 /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; + 1CC9BABDD52B2A1E37E2698D /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; + 1E2AE064CF32A604DC7BFD4D /* to_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B696858D2214B53900271095 /* to_string_test.cc */; }; + 1E52635E55FD6FAB78FD29D8 /* FSTLevelDBMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0872021552A00B64F25 /* FSTLevelDBMutationQueueTests.mm */; }; + 1F998DDECB54A66222CC66AA /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; + 21F821BF241244BA7BF070D9 /* FSTEventManagerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */; }; + 229D1A9381F698D71F229471 /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; + 29FF9029315C3A9FB0E0D79E /* FSTQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E061202154B900B64F25 /* FSTQueryTests.mm */; }; + 2AAEABFD550255271E3BAC91 /* to_string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68B1E002213A764008977EF /* to_string_apple_test.mm */; }; + 2E0BBA7E627EB240BA11B0D0 /* exponential_backoff_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D1B68420E2AB1A00B35856 /* exponential_backoff_test.cc */; }; + 2E6E6164F44B9E3C6BB88313 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; + 2EAD77559EC654E6CA4D3E21 /* FIRSnapshotMetadataTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */; }; + 3021937CBABFD9270A051900 /* FSTViewSnapshotTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05C202154B800B64F25 /* FSTViewSnapshotTest.mm */; }; + 32A95242C56A1A230231DB6A /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; 32F022CB75AEE48CDDAF2982 /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; 333FCB7BB0C9986B5DF28FC8 /* grpc_stream_tester.cc in Sources */ = {isa = PBXBuildFile; fileRef = B1A7E1959AF8141FA7E6B888 /* grpc_stream_tester.cc */; }; + 34387C13A92D31B212BC0CA9 /* FSTMemoryLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650420A0E9BD00A2D6A1 /* FSTMemoryLRUGarbageCollectorTests.mm */; }; + 355A9171EF3F7AD44A9C60CB /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; }; 36FD4CE79613D18BC783C55B /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; + 37EC6C6EA9169BB99078CA96 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; + 38430E0E07C54FCD399AE919 /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E046202154AA00B64F25 /* FIRQueryTests.mm */; }; + 38F973FA8ADEAFE9541C25EA /* FSTMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0962021552C00B64F25 /* FSTMutationQueueTests.mm */; }; + 3958F87E768E5CF40B87EF90 /* FSTMemoryLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0882021552A00B64F25 /* FSTMemoryLocalStoreTests.mm */; }; 3B843E4C1F3A182900548890 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; }; + 3BCEBA50E9678123245C0272 /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93620239689000A432D /* empty_credentials_provider_test.cc */; }; + 3D11B104A8F01F85180B38F6 /* field_transform_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352320A3AEC3003E0143 /* field_transform_test.mm */; }; + 3DFBA7413965F3E6F366E923 /* grpc_unary_call_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964942163E63900EB9CFB /* grpc_unary_call_test.cc */; }; + 3F2DF1DDDF7F5830F0669992 /* datastore_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 546854A820A36867004BDBD5 /* datastore_test.mm */; }; + 45939AFF906155EA27D281AB /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; + 46999832F7D1709B4C29FAA8 /* FIRDocumentReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */; }; + 470A37727BBF516B05ED276A /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; + 49C04B97AB282FFA82FD98CD /* latlng.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */; }; + 4A3FF3B16A39A5DC6B7EBA51 /* target.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */; }; + 4A62B708A6532DD45414DA3A /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; 4AA4ABE36065DB79CD76DD8D /* Pods_Firestore_Benchmarks_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F694C3CE4B77B3C0FA4BBA53 /* Pods_Firestore_Benchmarks_iOS.framework */; }; 4D1F46B2DD91198C8867C04C /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; + 4D98894EB5B3D778F5628456 /* grpc_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6BBE42F21262CF400C6A53E /* grpc_stream_test.cc */; }; + 4DAF501EE4B4DB79ED4239B0 /* secure_random_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A531FC913E500713A1A /* secure_random_test.cc */; }; + 4DC660A62BC2B6369DA5C563 /* status_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352C20A3B3D7003E0143 /* status_test.cc */; }; + 4F67086B5CC1787F612AE503 /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */; }; + 4F857404731D45F02C5EE4C3 /* async_queue_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4680208EA0BE00554BA2 /* async_queue_libdispatch_test.mm */; }; + 53AB47E44D897C81A94031F6 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; + 54080260D85A6F583E61DA1D /* FSTLocalSerializerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08A2021552A00B64F25 /* FSTLocalSerializerTests.mm */; }; 54131E9720ADE679001DF3FF /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; 544129DA21C2DDC800EFB9CC /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; 544129DB21C2DDC800EFB9CC /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; @@ -46,6 +104,19 @@ 5467FB01203E5717009C9584 /* FIRFirestoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */; }; 5467FB08203E6A44009C9584 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; 546854AA20A36867004BDBD5 /* datastore_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 546854A820A36867004BDBD5 /* datastore_test.mm */; }; + 546877D52248206A005E3DE0 /* collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129C1F315EE100DD57A1 /* collection_spec_test.json */; }; + 546877D62248206A005E3DE0 /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; + 546877D72248206A005E3DE0 /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; }; + 546877D82248206A005E3DE0 /* limit_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129F1F315EE100DD57A1 /* limit_spec_test.json */; }; + 546877D92248206A005E3DE0 /* listen_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A01F315EE100DD57A1 /* listen_spec_test.json */; }; + 546877DA2248206A005E3DE0 /* offline_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A11F315EE100DD57A1 /* offline_spec_test.json */; }; + 546877DB2248206A005E3DE0 /* orderby_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A21F315EE100DD57A1 /* orderby_spec_test.json */; }; + 546877DC2248206A005E3DE0 /* perf_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */; }; + 546877DD2248206A005E3DE0 /* persistence_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A31F315EE100DD57A1 /* persistence_spec_test.json */; }; + 546877DE2248206A005E3DE0 /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; + 546877DF2248206A005E3DE0 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; }; + 546877E02248206A005E3DE0 /* resume_token_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A41F315EE100DD57A1 /* resume_token_spec_test.json */; }; + 546877E12248206A005E3DE0 /* write_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A51F315EE100DD57A1 /* write_spec_test.json */; }; 54740A571FC914BA00713A1A /* secure_random_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A531FC913E500713A1A /* secure_random_test.cc */; }; 54740A581FC914F000713A1A /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; }; 54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; @@ -138,11 +209,19 @@ 54DA12AE1F315EE100DD57A1 /* resume_token_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A41F315EE100DD57A1 /* resume_token_spec_test.json */; }; 54DA12AF1F315EE100DD57A1 /* write_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A51F315EE100DD57A1 /* write_spec_test.json */; }; 54EB764D202277B30088B8F3 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; + 550FB7562D0CF9C3E1984000 /* FSTQueryListenerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05D202154B900B64F25 /* FSTQueryListenerTests.mm */; }; + 5556B648B9B1C2F79A706B4F /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; + 568EC1C0F68A7B95E57C8C6C /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; + 56D85436D3C864B804851B15 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; 5A080105CCBFDB6BF3F3772D /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; + 5B62003FEA9A3818FDF4E2DD /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; 5CC9650320A0E93200A2D6A1 /* FSTLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650220A0E93200A2D6A1 /* FSTLRUGarbageCollectorTests.mm */; }; 5CC9650520A0E9BD00A2D6A1 /* FSTMemoryLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650420A0E9BD00A2D6A1 /* FSTMemoryLRUGarbageCollectorTests.mm */; }; 5CC9650720A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650620A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm */; }; 5D405BE298CE4692CB00790A /* Pods_Firestore_Tests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */; }; + 5D45CC300ED037358EF33A8F /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; + 5EFBAD082CB0F86CD0711979 /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; + 5F19F66D8B01BA2B97579017 /* tree_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */; }; 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; @@ -155,6 +234,7 @@ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; + 60C72F86D2231B1B6592A5E6 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; 6161B5032047140C00A99DBB /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; 618BBEA620B89AAC00B5BCE7 /* target.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */; }; 618BBEA720B89AAC00B5BCE7 /* maybe_document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */; }; @@ -164,6 +244,12 @@ 618BBEB020B89AAC00B5BCE7 /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; 618BBEB120B89AAC00B5BCE7 /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; }; 61F72C5620BC48FD001A68CB /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; + 62DA31B79FE97A90EEF28B0B /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; + 63BB61B6366E7F80C348419D /* FSTLevelDBTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */; }; + 6672B445E006A7708B8531ED /* FSTImmutableSortedDictionary+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0801F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m */; }; + 66FAB8EAC012A3822BD4D0C9 /* leveldb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */; }; + 6B8806528FD3757D33D8B8AE /* FSTMemoryQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08B2021552B00B64F25 /* FSTMemoryQueryCacheTests.mm */; }; + 6DCA8E54E652B78EFF3EEDAC /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; 6E59498D20F55BA800ECD9A5 /* FuzzingResources in Resources */ = {isa = PBXBuildFile; fileRef = 6ED6DEA120F5502700FC6076 /* FuzzingResources */; }; 6E8302E021022309003E1EA3 /* FSTFuzzTestFieldPath.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6E8302DF21022309003E1EA3 /* FSTFuzzTestFieldPath.mm */; }; 6EA39FDE20FE820E008D461F /* FSTFuzzTestSerializer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EA39FDD20FE820E008D461F /* FSTFuzzTestSerializer.mm */; }; @@ -173,17 +259,48 @@ 6EDD3B4920BF247500C33877 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 6EDD3B6020BF25AE00C33877 /* FSTFuzzTestsPrincipal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EDD3B5E20BF24D000C33877 /* FSTFuzzTestsPrincipal.mm */; }; 6F3CAC76D918D6B0917EDF92 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; + 6FD2369F24E884A9D767DD80 /* FIRDocumentSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */; }; + 6FF2B680CC8631B06C7BD7AB /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; }; + 70D96C9129976DB01AC58BAC /* FSTMemoryMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0972021552C00B64F25 /* FSTMemoryMutationQueueTests.mm */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; + 71DF9A27169F25383C762F85 /* grpc_stream_tester.cc in Sources */ = {isa = PBXBuildFile; fileRef = B1A7E1959AF8141FA7E6B888 /* grpc_stream_tester.cc */; }; + 72AD91671629697074F2545B /* ordered_code_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D03201BC6E400D97691 /* ordered_code_test.cc */; }; 731541612214AFFA0037F4DC /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; 73866AA12082B0A5009BB4FF /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; 73F1F73C2210F3D800E1F692 /* memory_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */; }; 73F1F73D2210F3D800E1F692 /* index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */; }; 73F1F7412211FEF300E1F692 /* leveldb_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */; }; 73FE5066020EF9B2892C86BF /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; + 74985DE2C7EF4150D7A455FD /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; + 7EF540911720DAAF516BEDF0 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; + 83A9CD3B6E791A860CE81FA1 /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; 84DBE646DCB49305879D3500 /* nanopb_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */; }; + 862B1AC9EDAB309BBF4FB18C /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; + 8943A7C0750CEB0B98D21209 /* FSTPersistenceTestHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08D2021552B00B64F25 /* FSTPersistenceTestHelpers.mm */; }; + 897F3C1936612ACB018CA1DD /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; + 8C39F6D4B3AA9074DF00CFB8 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; 8C82D4D3F9AB63E79CC52DC8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */; }; + 8DA258092DD856D829D973B5 /* transform_operations_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352220A3AEC3003E0143 /* transform_operations_test.mm */; }; + 904DA0AE915C02154AE547FC /* FSTLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */; }; + 927D22C6D294B82D1580C48D /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0922021552B00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm */; }; + 938F2AF6EC5CD0B839300DB0 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; + 939A15D3AD941CF7242DA9FA /* FSTLevelDBLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650620A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm */; }; + 939C898FE9D129F6A2EA259C /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; + 9720B8BD354CCB64C0C627E6 /* nanopb_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */; }; + 974FF09E6AFD24D5A39B898B /* local_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F8043813A5D16963EC02B182 /* local_serializer_test.cc */; }; + 9774A6C2AA02A12D80B34C3C /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; + 9783FAEA4CF758E8C4C2D76E /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; }; 9794E074439ABE5457E60F35 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; + 9D0E720F5A6DBD48FF325016 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; + 9D71628E38D9F64C965DF29E /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; + A1F57CC739211F64F2E9232D /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; + A38F4AE525A87FDEA41DED47 /* FSTLevelDBQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */; }; + A61AE3D94C975A87EFA82ADA /* firebase_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */; }; + A6543DD0A56F8523C6D518E1 /* FSTMutationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B72021555100B64F25 /* FSTMutationTests.mm */; }; + A7470B7B2433264FFDCC7AC3 /* FSTLevelDBLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08F2021552B00B64F25 /* FSTLevelDBLocalStoreTests.mm */; }; + A907244EE37BC32C8D82948E /* FSTSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */; }; + AAA50E56B9A7EF3EFDA62172 /* create_noop_connectivity_monitor.cc in Sources */ = {isa = PBXBuildFile; fileRef = B67BF448216EB43000CA9097 /* create_noop_connectivity_monitor.cc */; }; AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; AB380CFE201A2F4500D97691 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; @@ -201,10 +318,16 @@ ABC1D7E42024AFDE00BA84F0 /* firebase_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */; }; ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; ABF6506C201131F8005F2C74 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; + AC6C1E57B18730428CB15E03 /* executor_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */; }; + ACC9369843F5ED3BD2284078 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; + AD86162AC78673BA969F3467 /* FSTQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0892021552A00B64F25 /* FSTQueryCacheTests.mm */; }; + B192F30DECA8C28007F9B1D0 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; + B49311BDE5EB6DF811E03C1B /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D9342023966E000A432D /* credentials_provider_test.cc */; }; B60894F72170207200EBC644 /* fake_credentials_provider.cc in Sources */ = {isa = PBXBuildFile; fileRef = B60894F62170207100EBC644 /* fake_credentials_provider.cc */; }; B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; B65D34A9203C995B0076A5E1 /* FIRTimestampTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */; }; B66D8996213609EE0086DA0C /* stream_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B66D8995213609EE0086DA0C /* stream_test.mm */; }; + B67BB1DA1E247A87B4755C26 /* FSTViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05E202154B900B64F25 /* FSTViewTests.mm */; }; B67BF449216EB43000CA9097 /* create_noop_connectivity_monitor.cc in Sources */ = {isa = PBXBuildFile; fileRef = B67BF448216EB43000CA9097 /* create_noop_connectivity_monitor.cc */; }; B686F2AF2023DDEE0028D6BE /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; }; B686F2B22025000D0028D6BE /* resource_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2B02024FFD70028D6BE /* resource_path_test.cc */; }; @@ -224,21 +347,42 @@ B6FB468E208F9BAB00554BA2 /* executor_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */; }; B6FB468F208F9BAE00554BA2 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; B6FB4690208F9BB300554BA2 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; + B8062EBDB8E5B680E46A6DD1 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; + B89EF6551734723BDC6AB79C /* FSTDocumentKeyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */; }; + BCD9AEA4A890E804922BF72F /* FSTRemoteEventTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */; }; BEE0294A23AB993E5DE0E946 /* leveldb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */; }; C1AA536F90A0A576CA2816EB /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */; }; + C21B3A1CCB3AD42E57EA14FC /* Pods_Firestore_Tests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 759E964B6A03E6775C992710 /* Pods_Firestore_Tests_macOS.framework */; }; + C39CBADA58F442C8D66C3DA2 /* FIRFieldPathTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */; }; + C4055D868A38221B332CD03D /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; C482E724F4B10968417C3F78 /* Pods_Firestore_FuzzTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B79CA87A1A01FC5329031C9B /* Pods_Firestore_FuzzTests_iOS.framework */; }; + C5655568EC2A9F6B5E6F9141 /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; + C5C01A1FB216DA4BA8BF1A02 /* stream_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B66D8995213609EE0086DA0C /* stream_test.mm */; }; + C5DEDF6148FD41B3000DDD5C /* FSTMemoryRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08C2021552B00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm */; }; + C5F1E2220E30ED5EAC9ABD9E /* mutation.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8220B89AAC00B5BCE7 /* mutation.pb.cc */; }; + C7F174164D7C55E35A526009 /* resource_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2B02024FFD70028D6BE /* resource_path_test.cc */; }; C80B10E79CDD7EF7843C321E /* type_traits_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */; }; C8D3CE2343E53223E6487F2C /* Pods_Firestore_Example_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */; }; + C9F96C511F45851D38EC449C /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; }; CA989C0E6020C372A62B7062 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; + CD0AA9E5D83C00CAAE7C2F67 /* FIRTimestampTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */; }; + D063F56AC89E074F9AB05DD3 /* FSTRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */; }; + D44DA2F61B854E8771E4E446 /* memory_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */; }; + D572B4D4DBDD6B9235781646 /* objc_compatibility_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B696858F221770F000271095 /* objc_compatibility_apple_test.mm */; }; + D57F4CB3C92CE3D4DF329B78 /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; + D59FAEE934987D4C4B2A67B2 /* FIRFirestoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */; }; D5B252EE3F4037405DB1ECE3 /* FIRNumericTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */; }; D5B25CBF07F65E885C9D68AB /* perf_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */; }; + D9366A834BFF13246DC3AF9E /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; }; D94A1862B8FB778225DB54A1 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; DAFF0CF921E64AC30062958F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFF0CF821E64AC30062958F /* AppDelegate.m */; }; DAFF0CFB21E64AC40062958F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAFF0CFA21E64AC40062958F /* Assets.xcassets */; }; DAFF0CFE21E64AC40062958F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DAFF0CFC21E64AC40062958F /* MainMenu.xib */; }; DAFF0D0121E64AC40062958F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFF0D0021E64AC40062958F /* main.m */; }; DAFF0D0921E653A00062958F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 54D400D32148BACE001D2BCC /* GoogleService-Info.plist */; }; - DD213F68A6F79E1D4924BD95 /* Pods_macOS_example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E42355285B9EF55ABD785792 /* Pods_macOS_example.framework */; }; + DB7E9C5A59CCCDDB7F0C238A /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; + DBDC8E997E909804F1B43E92 /* log_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54C2294E1FECABAE007D065B /* log_test.cc */; }; + DD213F68A6F79E1D4924BD95 /* Pods_Firestore_Example_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E42355285B9EF55ABD785792 /* Pods_Firestore_Example_macOS.framework */; }; DD5976A45071455FF3FE74B8 /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; DE03B2D41F2149D600A30B9C /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; DE03B2D51F2149D600A30B9C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; @@ -249,10 +393,37 @@ DE2EF0861F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0801F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m */; }; DE2EF0871F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0821F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m */; }; DE2EF0881F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0841F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m */; }; + DEF036EA1ECEECD8E3ECC362 /* FSTImmutableSortedSet+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0821F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m */; }; + E084921EFB7CF8CB1E950D6C /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; + E11DDA3DD75705F26245E295 /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; }; + E3C0E5F834A82EEE9F8C4519 /* watch_change_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68FC0E421F6848700A7055C /* watch_change_test.mm */; }; + E4C0CC7FB88D8F6CB1B972C6 /* leveldb_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */; }; + E6821243C510797EFFC7BCE2 /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; + E764F0F389E7119220EB212C /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; + E7D415B8717701B952C344E5 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; + E82F8EBBC8CC37299A459E73 /* hashing_test_apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = B69CF3F02227386500B281C8 /* hashing_test_apple.mm */; }; + E9558682F8A4DD3E7C85C067 /* FSTLevelDBMigrationsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0862021552A00B64F25 /* FSTLevelDBMigrationsTests.mm */; }; + E980E1DCF759D5EF9F6B98F2 /* FSTDocumentTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */; }; EBFC611B1BF195D0EC710AF4 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; + EC80A217F3D66EB0272B36B0 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; + F007A46BE03A01C077EFCBD8 /* FSTFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */; }; + F3261CBFC169DB375A0D9492 /* FSTMockDatastore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */; }; + F3F09BC931A717CEFF4E14B9 /* FIRFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */; }; + F4F00BF4E87D7F0F0F8831DB /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; + F8D3EF0C9044BB3F3E81C6CA /* FSTDocumentSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */; }; + F9DC01FCBE76CD4F0453A67C /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; + FF3405218188DFCE586FB26B /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; + FF4FA5757D13A2B7CEE40F04 /* document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D821C2DDC800EFB9CC /* document.pb.cc */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 544AB1972248072200F851E6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = DAFF0CF421E64AC30062958F; + remoteInfo = Firestore_Example_macOS; + }; 54C9EDF62040E16300A969CD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6003F582195388D10070C39A /* Project object */; @@ -324,6 +495,7 @@ 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_util_test.cc; sourceTree = ""; }; 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = nanopb_string_test.cc; path = nanopb/nanopb_string_test.cc; sourceTree = ""; }; 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = strerror_test.cc; sourceTree = ""; }; + 397FB002E298B780F1E223E2 /* Pods-Firestore_Tests_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS.release.xcconfig"; sourceTree = ""; }; 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = ""; }; 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = ""; }; 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.release.xcconfig"; sourceTree = ""; }; @@ -341,6 +513,7 @@ 544129D721C2DDC800EFB9CC /* document.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = document.pb.h; sourceTree = ""; }; 544129D821C2DDC800EFB9CC /* document.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = document.pb.cc; sourceTree = ""; }; 544129D921C2DDC800EFB9CC /* write.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = write.pb.cc; sourceTree = ""; }; + 544AB1922248072200F851E6 /* Firestore_Tests_macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_Tests_macOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54511E8D209805F8005BD28F /* hashing_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hashing_test.cc; sourceTree = ""; }; 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFirestoreTests.mm; sourceTree = ""; }; 5467FB06203E6A44009C9584 /* app_testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = app_testing.h; sourceTree = ""; }; @@ -504,12 +677,13 @@ 73F1F73A2210F3D800E1F692 /* index_manager_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = index_manager_test.h; sourceTree = ""; }; 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = index_manager_test.mm; sourceTree = ""; }; 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = leveldb_index_manager_test.mm; sourceTree = ""; }; + 759E964B6A03E6775C992710 /* Pods_Firestore_Tests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 79507DF8378D3C42F5B36268 /* string_win_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = string_win_test.cc; sourceTree = ""; }; 84434E57CA72951015FC71BC /* Pods-Firestore_FuzzTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_FuzzTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS.debug.xcconfig"; sourceTree = ""; }; 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 8E002F4AD5D9B6197C940847 /* Firestore.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Firestore.podspec; path = ../Firestore.podspec; sourceTree = ""; }; 97C492D2524E92927C11F425 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_FuzzTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS.release.xcconfig"; sourceTree = ""; }; - 98366480BD1FD44A1FEDD982 /* Pods-macOS_example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-macOS_example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-macOS_example/Pods-macOS_example.debug.xcconfig"; sourceTree = ""; }; + 98366480BD1FD44A1FEDD982 /* Pods-Firestore_Example_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS.debug.xcconfig"; sourceTree = ""; }; 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_format_apple_test.mm; sourceTree = ""; }; A5FA86650A18F3B7A8162287 /* Pods-Firestore_Benchmarks_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Benchmarks_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS.release.xcconfig"; sourceTree = ""; }; AB356EF6200EA5EB0089B766 /* field_value_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = field_value_test.cc; sourceTree = ""; }; @@ -564,19 +738,20 @@ B9C261C26C5D311E1E3C0CB9 /* query_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = query_test.cc; sourceTree = ""; }; BA6E5B9D53CCF301F58A62D7 /* xcgmock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = xcgmock.h; sourceTree = ""; }; BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BD01F0E43E4E2A07B8B05099 /* Pods-Firestore_Tests_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS.debug.xcconfig"; sourceTree = ""; }; C8522DE226C467C54E6788D8 /* mutation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = mutation_test.cc; sourceTree = ""; }; D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = delayed_constructor_test.cc; sourceTree = ""; }; D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = perf_spec_test.json; sourceTree = ""; }; D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRNumericTransformTests.mm; sourceTree = ""; }; - DAFF0CF521E64AC30062958F /* macOS_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = macOS_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DAFF0CF521E64AC30062958F /* Firestore_Example_macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example_macOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; DAFF0CF721E64AC30062958F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DAFF0CF821E64AC30062958F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; DAFF0CFA21E64AC40062958F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; DAFF0CFD21E64AC40062958F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; DAFF0CFF21E64AC40062958F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DAFF0D0021E64AC40062958F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - DAFF0D0221E64AC40062958F /* macOS_example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS_example.entitlements; sourceTree = ""; }; + DAFF0D0221E64AC40062958F /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; }; DAFF0D0721E653460062958F /* roots.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = roots.pem; path = ../../../../etc/roots.pem; sourceTree = ""; }; DE03B2E91F2149D600A30B9C /* Firestore_IntegrationTests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_IntegrationTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DE03B3621F215E1600A30B9C /* CAcert.pem */ = {isa = PBXFileReference; lastKnownFileType = text; path = CAcert.pem; sourceTree = ""; }; @@ -593,8 +768,8 @@ DE51B1981F0D48AC0013853F /* FSTSpecTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTSpecTests.h; sourceTree = ""; }; DE51B19A1F0D48AC0013853F /* FSTSyncEngineTestDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTSyncEngineTestDriver.h; sourceTree = ""; }; DE51B1A71F0D48AC0013853F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - DF148C0D5EEC4A2CD9FA484C /* Pods-macOS_example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-macOS_example.release.xcconfig"; path = "Pods/Target Support Files/Pods-macOS_example/Pods-macOS_example.release.xcconfig"; sourceTree = ""; }; - E42355285B9EF55ABD785792 /* Pods_macOS_example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_macOS_example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DF148C0D5EEC4A2CD9FA484C /* Pods-Firestore_Example_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS.release.xcconfig"; sourceTree = ""; }; + E42355285B9EF55ABD785792 /* Pods_Firestore_Example_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E592181BFD7C53C305123739 /* Pods-Firestore_Tests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS.debug.xcconfig"; sourceTree = ""; }; ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; ED4B3E3EA0EBF3ED19A07060 /* grpc_stream_tester.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = grpc_stream_tester.h; sourceTree = ""; }; @@ -606,6 +781,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 544AB18F2248072200F851E6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C21B3A1CCB3AD42E57EA14FC /* Pods_Firestore_Tests_macOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54C9EDEE2040E16300A969CD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -659,7 +842,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DD213F68A6F79E1D4924BD95 /* Pods_macOS_example.framework in Frameworks */, + DD213F68A6F79E1D4924BD95 /* Pods_Firestore_Example_macOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -681,7 +864,7 @@ isa = PBXGroup; children = ( 6003F593195388D20070C39A /* iOS */, - DAFF0CF621E64AC30062958F /* macOS_example */, + DAFF0CF621E64AC30062958F /* macOS */, 54D400D32148BACE001D2BCC /* GoogleService-Info.plist */, ); path = App; @@ -894,11 +1077,12 @@ children = ( 5CAE131920FFFED600BE9A4A /* Firestore_Benchmarks_iOS.xctest */, 6003F58A195388D20070C39A /* Firestore_Example_iOS.app */, + DAFF0CF521E64AC30062958F /* Firestore_Example_macOS.app */, 6EDD3B5B20BF247500C33877 /* Firestore_FuzzTests_iOS.xctest */, DE03B2E91F2149D600A30B9C /* Firestore_IntegrationTests_iOS.xctest */, 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */, 6003F5AE195388D20070C39A /* Firestore_Tests_iOS.xctest */, - DAFF0CF521E64AC30062958F /* macOS_example.app */, + 544AB1922248072200F851E6 /* Firestore_Tests_macOS.xctest */, ); name = Products; sourceTree = ""; @@ -911,12 +1095,13 @@ F694C3CE4B77B3C0FA4BBA53 /* Pods_Firestore_Benchmarks_iOS.framework */, 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */, BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */, + E42355285B9EF55ABD785792 /* Pods_Firestore_Example_macOS.framework */, B79CA87A1A01FC5329031C9B /* Pods_Firestore_FuzzTests_iOS.framework */, ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */, 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */, - E42355285B9EF55ABD785792 /* Pods_macOS_example.framework */, 6003F591195388D20070C39A /* UIKit.framework */, 6003F5AF195388D20070C39A /* XCTest.framework */, + 759E964B6A03E6775C992710 /* Pods_Firestore_Tests_macOS.framework */, ); name = Frameworks; sourceTree = ""; @@ -1084,14 +1269,16 @@ 11984BA0A99D7A7ABA5B0D90 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig */, 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */, 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */, + 98366480BD1FD44A1FEDD982 /* Pods-Firestore_Example_macOS.debug.xcconfig */, + DF148C0D5EEC4A2CD9FA484C /* Pods-Firestore_Example_macOS.release.xcconfig */, 84434E57CA72951015FC71BC /* Pods-Firestore_FuzzTests_iOS.debug.xcconfig */, 97C492D2524E92927C11F425 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */, 1277F98C20D2DF0867496976 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */, F354C0FE92645B56A6C6FD44 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */, E592181BFD7C53C305123739 /* Pods-Firestore_Tests_iOS.debug.xcconfig */, B3F5B3AAE791A5911B9EAA82 /* Pods-Firestore_Tests_iOS.release.xcconfig */, - 98366480BD1FD44A1FEDD982 /* Pods-macOS_example.debug.xcconfig */, - DF148C0D5EEC4A2CD9FA484C /* Pods-macOS_example.release.xcconfig */, + BD01F0E43E4E2A07B8B05099 /* Pods-Firestore_Tests_macOS.debug.xcconfig */, + 397FB002E298B780F1E223E2 /* Pods-Firestore_Tests_macOS.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -1136,7 +1323,7 @@ path = auth; sourceTree = ""; }; - DAFF0CF621E64AC30062958F /* macOS_example */ = { + DAFF0CF621E64AC30062958F /* macOS */ = { isa = PBXGroup; children = ( DAFF0D0621E652FD0062958F /* resources */, @@ -1145,10 +1332,10 @@ DAFF0CFA21E64AC40062958F /* Assets.xcassets */, DAFF0CFF21E64AC40062958F /* Info.plist */, DAFF0CFC21E64AC40062958F /* MainMenu.xib */, - DAFF0D0221E64AC40062958F /* macOS_example.entitlements */, + DAFF0D0221E64AC40062958F /* macOS.entitlements */, DAFF0D0021E64AC40062958F /* main.m */, ); - path = macOS_example; + path = macOS; sourceTree = ""; }; DAFF0D0621E652FD0062958F /* resources */ = { @@ -1348,6 +1535,26 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 544AB1912248072200F851E6 /* Firestore_Tests_macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 544AB19B2248072200F851E6 /* Build configuration list for PBXNativeTarget "Firestore_Tests_macOS" */; + buildPhases = ( + 30108B32BF2B385AECDB7FB2 /* [CP] Check Pods Manifest.lock */, + 544AB18E2248072200F851E6 /* Sources */, + 544AB18F2248072200F851E6 /* Frameworks */, + 544AB1902248072200F851E6 /* Resources */, + 7E4A6E169B172874E17A3ECA /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 544AB1982248072200F851E6 /* PBXTargetDependency */, + ); + name = Firestore_Tests_macOS; + productName = Firestore_Tests_macOS; + productReference = 544AB1922248072200F851E6 /* Firestore_Tests_macOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 54C9EDF02040E16300A969CD /* Firestore_SwiftTests_iOS */ = { isa = PBXNativeTarget; buildConfigurationList = 54C9EDFA2040E16300A969CD /* Build configuration list for PBXNativeTarget "Firestore_SwiftTests_iOS" */; @@ -1448,9 +1655,9 @@ productReference = 6EDD3B5B20BF247500C33877 /* Firestore_FuzzTests_iOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - DAFF0CF421E64AC30062958F /* macOS_example */ = { + DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = DAFF0D0521E64AC40062958F /* Build configuration list for PBXNativeTarget "macOS_example" */; + buildConfigurationList = DAFF0D0521E64AC40062958F /* Build configuration list for PBXNativeTarget "Firestore_Example_macOS" */; buildPhases = ( 7C2467DCD3E3E16FB0A737DE /* [CP] Check Pods Manifest.lock */, DAFF0CF121E64AC30062958F /* Sources */, @@ -1462,9 +1669,9 @@ ); dependencies = ( ); - name = macOS_example; - productName = macOS_example; - productReference = DAFF0CF521E64AC30062958F /* macOS_example.app */; + name = Firestore_Example_macOS; + productName = Firestore_Example_macOS; + productReference = DAFF0CF521E64AC30062958F /* Firestore_Example_macOS.app */; productType = "com.apple.product-type.application"; }; DE03B2941F2149D600A30B9C /* Firestore_IntegrationTests_iOS */ = { @@ -1498,6 +1705,11 @@ LastUpgradeCheck = 1010; ORGANIZATIONNAME = Google; TargetAttributes = { + 544AB1912248072200F851E6 = { + CreatedOnToolsVersion = 10.1; + ProvisioningStyle = Automatic; + TestTargetID = DAFF0CF421E64AC30062958F; + }; 54C9EDF02040E16300A969CD = { CreatedOnToolsVersion = 9.2; ProvisioningStyle = Automatic; @@ -1505,33 +1717,30 @@ }; 5CAE131820FFFED600BE9A4A = { CreatedOnToolsVersion = 9.3.1; - DevelopmentTeam = EQHXZ8M8AV; ProvisioningStyle = Automatic; TestTargetID = 6003F589195388D20070C39A; }; 6003F5AD195388D20070C39A = { - DevelopmentTeam = EQHXZ8M8AV; + CreatedOnToolsVersion = 9.0; TestTargetID = 6003F589195388D20070C39A; }; 6EDD3AD120BF247500C33877 = { - DevelopmentTeam = EQHXZ8M8AV; + CreatedOnToolsVersion = 9.0; }; DAFF0CF421E64AC30062958F = { CreatedOnToolsVersion = 10.0; - DevelopmentTeam = 63PT2H4G8K; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.Sandbox = { - enabled = 1; + enabled = 0; }; }; }; DE03B2941F2149D600A30B9C = { - DevelopmentTeam = EQHXZ8M8AV; + CreatedOnToolsVersion = 9.0; }; DE29E7F51F2174B000909613 = { CreatedOnToolsVersion = 9.0; - DevelopmentTeam = EQHXZ8M8AV; }; }; }; @@ -1555,12 +1764,33 @@ DE29E7F51F2174B000909613 /* AllTests_iOS */, 6EDD3AD120BF247500C33877 /* Firestore_FuzzTests_iOS */, 5CAE131820FFFED600BE9A4A /* Firestore_Benchmarks_iOS */, - DAFF0CF421E64AC30062958F /* macOS_example */, + DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */, + 544AB1912248072200F851E6 /* Firestore_Tests_macOS */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 544AB1902248072200F851E6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 546877D82248206A005E3DE0 /* limit_spec_test.json in Resources */, + 546877DC2248206A005E3DE0 /* perf_spec_test.json in Resources */, + 546877D52248206A005E3DE0 /* collection_spec_test.json in Resources */, + 546877DF2248206A005E3DE0 /* remote_store_spec_test.json in Resources */, + 546877DA2248206A005E3DE0 /* offline_spec_test.json in Resources */, + 546877DD2248206A005E3DE0 /* persistence_spec_test.json in Resources */, + 546877DB2248206A005E3DE0 /* orderby_spec_test.json in Resources */, + 546877E12248206A005E3DE0 /* write_spec_test.json in Resources */, + 546877D62248206A005E3DE0 /* existence_filter_spec_test.json in Resources */, + 546877DE2248206A005E3DE0 /* query_spec_test.json in Resources */, + 546877E02248206A005E3DE0 /* resume_token_spec_test.json in Resources */, + 546877D92248206A005E3DE0 /* listen_spec_test.json in Resources */, + 546877D72248206A005E3DE0 /* limbo_spec_test.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54C9EDEF2040E16300A969CD /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1670,6 +1900,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 30108B32BF2B385AECDB7FB2 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Firestore_Tests_macOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 329C25E418360CEF62F6CB2B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1678,9 +1930,9 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/leveldb-library-iOS/leveldb.framework", - "${BUILT_PRODUCTS_DIR}/GoogleTest/GoogleTest.framework", - "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", - "${BUILT_PRODUCTS_DIR}/ProtobufCpp/ProtobufCpp.framework", + "${BUILT_PRODUCTS_DIR}/GoogleTest-iOS/GoogleTest.framework", + "${BUILT_PRODUCTS_DIR}/OCMock-iOS/OCMock.framework", + "${BUILT_PRODUCTS_DIR}/ProtobufCpp-iOS/ProtobufCpp.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -1718,7 +1970,7 @@ files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-macOS_example/Pods-macOS_example-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-macOS/openssl_grpc.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-macOS/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities-Environment-Logger/GoogleUtilities.framework", @@ -1741,7 +1993,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-macOS_example/Pods-macOS_example-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 6E622C7A20F52C8300B7E93A /* Run Script */ = { @@ -1810,13 +2062,41 @@ ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-macOS_example-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Firestore_Example_macOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 7E4A6E169B172874E17A3ECA /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/leveldb-library-macOS/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/GoogleTest-macOS/GoogleTest.framework", + "${BUILT_PRODUCTS_DIR}/OCMock-macOS/OCMock.framework", + "${BUILT_PRODUCTS_DIR}/ProtobufCpp-macOS/ProtobufCpp.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtobufCpp.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 83F2AB95D08093BB076EE521 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1879,8 +2159,8 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/leveldb-library-iOS/leveldb.framework", - "${BUILT_PRODUCTS_DIR}/GoogleTest/GoogleTest.framework", - "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", + "${BUILT_PRODUCTS_DIR}/GoogleTest-iOS/GoogleTest.framework", + "${BUILT_PRODUCTS_DIR}/OCMock-iOS/OCMock.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -1964,6 +2244,163 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 544AB18E2248072200F851E6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E11DDA3DD75705F26245E295 /* FIRCollectionReferenceTests.mm in Sources */, + 46999832F7D1709B4C29FAA8 /* FIRDocumentReferenceTests.mm in Sources */, + 6FD2369F24E884A9D767DD80 /* FIRDocumentSnapshotTests.mm in Sources */, + C39CBADA58F442C8D66C3DA2 /* FIRFieldPathTests.mm in Sources */, + F3F09BC931A717CEFF4E14B9 /* FIRFieldValueTests.mm in Sources */, + D59FAEE934987D4C4B2A67B2 /* FIRFirestoreTests.mm in Sources */, + 18CF41A17EA3292329E1119D /* FIRGeoPointTests.mm in Sources */, + 113190791F42202FDE1ABC14 /* FIRQuerySnapshotTests.mm in Sources */, + 38430E0E07C54FCD399AE919 /* FIRQueryTests.mm in Sources */, + 2EAD77559EC654E6CA4D3E21 /* FIRSnapshotMetadataTests.mm in Sources */, + CD0AA9E5D83C00CAAE7C2F67 /* FIRTimestampTest.m in Sources */, + 9D71628E38D9F64C965DF29E /* FSTAPIHelpers.mm in Sources */, + 10B7426844685A48234C093B /* FSTArraySortedDictionaryTests.m in Sources */, + B89EF6551734723BDC6AB79C /* FSTDocumentKeyTests.mm in Sources */, + F8D3EF0C9044BB3F3E81C6CA /* FSTDocumentSetTests.mm in Sources */, + E980E1DCF759D5EF9F6B98F2 /* FSTDocumentTests.mm in Sources */, + F4F00BF4E87D7F0F0F8831DB /* FSTEventAccumulator.mm in Sources */, + 21F821BF241244BA7BF070D9 /* FSTEventManagerTests.mm in Sources */, + F007A46BE03A01C077EFCBD8 /* FSTFieldValueTests.mm in Sources */, + 0A6FBE65A7FE048BAD562A15 /* FSTGoogleTestTests.mm in Sources */, + 939C898FE9D129F6A2EA259C /* FSTHelpers.mm in Sources */, + 6672B445E006A7708B8531ED /* FSTImmutableSortedDictionary+Testing.m in Sources */, + DEF036EA1ECEECD8E3ECC362 /* FSTImmutableSortedSet+Testing.m in Sources */, + C4055D868A38221B332CD03D /* FSTIntegrationTestCase.mm in Sources */, + 10CA552415BE0954221A1626 /* FSTLRUGarbageCollectorTests.mm in Sources */, + 939A15D3AD941CF7242DA9FA /* FSTLevelDBLRUGarbageCollectorTests.mm in Sources */, + A7470B7B2433264FFDCC7AC3 /* FSTLevelDBLocalStoreTests.mm in Sources */, + E9558682F8A4DD3E7C85C067 /* FSTLevelDBMigrationsTests.mm in Sources */, + 1E52635E55FD6FAB78FD29D8 /* FSTLevelDBMutationQueueTests.mm in Sources */, + A38F4AE525A87FDEA41DED47 /* FSTLevelDBQueryCacheTests.mm in Sources */, + 927D22C6D294B82D1580C48D /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */, + EC80A217F3D66EB0272B36B0 /* FSTLevelDBSpecTests.mm in Sources */, + 63BB61B6366E7F80C348419D /* FSTLevelDBTransactionTests.mm in Sources */, + 54080260D85A6F583E61DA1D /* FSTLocalSerializerTests.mm in Sources */, + 904DA0AE915C02154AE547FC /* FSTLocalStoreTests.mm in Sources */, + 34387C13A92D31B212BC0CA9 /* FSTMemoryLRUGarbageCollectorTests.mm in Sources */, + 3958F87E768E5CF40B87EF90 /* FSTMemoryLocalStoreTests.mm in Sources */, + 70D96C9129976DB01AC58BAC /* FSTMemoryMutationQueueTests.mm in Sources */, + 6B8806528FD3757D33D8B8AE /* FSTMemoryQueryCacheTests.mm in Sources */, + C5DEDF6148FD41B3000DDD5C /* FSTMemoryRemoteDocumentCacheTests.mm in Sources */, + 6FF2B680CC8631B06C7BD7AB /* FSTMemorySpecTests.mm in Sources */, + F3261CBFC169DB375A0D9492 /* FSTMockDatastore.mm in Sources */, + 38F973FA8ADEAFE9541C25EA /* FSTMutationQueueTests.mm in Sources */, + A6543DD0A56F8523C6D518E1 /* FSTMutationTests.mm in Sources */, + 8943A7C0750CEB0B98D21209 /* FSTPersistenceTestHelpers.mm in Sources */, + AD86162AC78673BA969F3467 /* FSTQueryCacheTests.mm in Sources */, + 550FB7562D0CF9C3E1984000 /* FSTQueryListenerTests.mm in Sources */, + 29FF9029315C3A9FB0E0D79E /* FSTQueryTests.mm in Sources */, + D063F56AC89E074F9AB05DD3 /* FSTRemoteDocumentCacheTests.mm in Sources */, + BCD9AEA4A890E804922BF72F /* FSTRemoteEventTests.mm in Sources */, + 0D67722B43147F775891EA43 /* FSTSerializerBetaTests.mm in Sources */, + A907244EE37BC32C8D82948E /* FSTSpecTests.mm in Sources */, + 072D805A94E767DE4D371881 /* FSTSyncEngineTestDriver.mm in Sources */, + 156B042479C42DB2C3190C63 /* FSTTreeSortedDictionaryTests.m in Sources */, + 3021937CBABFD9270A051900 /* FSTViewSnapshotTest.mm in Sources */, + B67BB1DA1E247A87B4755C26 /* FSTViewTests.mm in Sources */, + 6DCA8E54E652B78EFF3EEDAC /* XCTestCase+Await.mm in Sources */, + 45939AFF906155EA27D281AB /* annotations.pb.cc in Sources */, + FF3405218188DFCE586FB26B /* app_testing.mm in Sources */, + B192F30DECA8C28007F9B1D0 /* array_sorted_map_test.cc in Sources */, + 4F857404731D45F02C5EE4C3 /* async_queue_libdispatch_test.mm in Sources */, + 83A9CD3B6E791A860CE81FA1 /* async_queue_std_test.cc in Sources */, + 0B7B24194E2131F5C325FE0E /* async_queue_test.cc in Sources */, + 1733601ECCEA33E730DEAF45 /* autoid_test.cc in Sources */, + 0DAA255C2FEB387895ADEE12 /* bits_test.cc in Sources */, + 5556B648B9B1C2F79A706B4F /* common.pb.cc in Sources */, + 08D853C9D3A4DC919C55671A /* comparison_test.cc in Sources */, + AAA50E56B9A7EF3EFDA62172 /* create_noop_connectivity_monitor.cc in Sources */, + B49311BDE5EB6DF811E03C1B /* credentials_provider_test.cc in Sources */, + 9774A6C2AA02A12D80B34C3C /* database_id_test.cc in Sources */, + 11F8EE69182C9699E90A9E3D /* database_info_test.cc in Sources */, + 3F2DF1DDDF7F5830F0669992 /* datastore_test.mm in Sources */, + 62DA31B79FE97A90EEF28B0B /* delayed_constructor_test.cc in Sources */, + FF4FA5757D13A2B7CEE40F04 /* document.pb.cc in Sources */, + 5B62003FEA9A3818FDF4E2DD /* document_key_test.cc in Sources */, + 355A9171EF3F7AD44A9C60CB /* document_test.cc in Sources */, + 3BCEBA50E9678123245C0272 /* empty_credentials_provider_test.cc in Sources */, + AC6C1E57B18730428CB15E03 /* executor_libdispatch_test.mm in Sources */, + E7D415B8717701B952C344E5 /* executor_std_test.cc in Sources */, + 470A37727BBF516B05ED276A /* executor_test.cc in Sources */, + 2E0BBA7E627EB240BA11B0D0 /* exponential_backoff_test.cc in Sources */, + 135429EEF1D7FA9D1E329392 /* fake_credentials_provider.cc in Sources */, + 07B1E8C62772758BC82FEBEE /* field_mask_test.cc in Sources */, + D9366A834BFF13246DC3AF9E /* field_path_test.cc in Sources */, + 3D11B104A8F01F85180B38F6 /* field_transform_test.mm in Sources */, + 9D0E720F5A6DBD48FF325016 /* field_value_test.cc in Sources */, + 60C72F86D2231B1B6592A5E6 /* filesystem_test.cc in Sources */, + A61AE3D94C975A87EFA82ADA /* firebase_credentials_provider_test.mm in Sources */, + C5655568EC2A9F6B5E6F9141 /* firestore.pb.cc in Sources */, + B8062EBDB8E5B680E46A6DD1 /* geo_point_test.cc in Sources */, + 056542AD1D0F78E29E22EFA9 /* grpc_connection_test.cc in Sources */, + 4D98894EB5B3D778F5628456 /* grpc_stream_test.cc in Sources */, + 71DF9A27169F25383C762F85 /* grpc_stream_tester.cc in Sources */, + E6821243C510797EFFC7BCE2 /* grpc_streaming_reader_test.cc in Sources */, + 3DFBA7413965F3E6F366E923 /* grpc_unary_call_test.cc in Sources */, + A1F57CC739211F64F2E9232D /* hard_assert_test.cc in Sources */, + 9783FAEA4CF758E8C4C2D76E /* hashing_test.cc in Sources */, + E82F8EBBC8CC37299A459E73 /* hashing_test_apple.mm in Sources */, + 897F3C1936612ACB018CA1DD /* http.pb.cc in Sources */, + 036F975093414351FE952F08 /* index_manager_test.mm in Sources */, + E084921EFB7CF8CB1E950D6C /* iterator_adaptors_test.cc in Sources */, + 49C04B97AB282FFA82FD98CD /* latlng.pb.cc in Sources */, + E4C0CC7FB88D8F6CB1B972C6 /* leveldb_index_manager_test.mm in Sources */, + 568EC1C0F68A7B95E57C8C6C /* leveldb_key_test.cc in Sources */, + 66FAB8EAC012A3822BD4D0C9 /* leveldb_util_test.cc in Sources */, + 974FF09E6AFD24D5A39B898B /* local_serializer_test.cc in Sources */, + DBDC8E997E909804F1B43E92 /* log_test.cc in Sources */, + 12158DFCEE09D24B7988A340 /* maybe_document.pb.cc in Sources */, + D44DA2F61B854E8771E4E446 /* memory_index_manager_test.mm in Sources */, + C5F1E2220E30ED5EAC9ABD9E /* mutation.pb.cc in Sources */, + 1CC9BABDD52B2A1E37E2698D /* mutation_test.cc in Sources */, + 9720B8BD354CCB64C0C627E6 /* nanopb_string_test.cc in Sources */, + 051D3E20184AF195266EF678 /* no_document_test.cc in Sources */, + D572B4D4DBDD6B9235781646 /* objc_compatibility_apple_test.mm in Sources */, + 72AD91671629697074F2545B /* ordered_code_test.cc in Sources */, + DB7E9C5A59CCCDDB7F0C238A /* path_test.cc in Sources */, + 0455FC6E2A281BD755FD933A /* precondition_test.cc in Sources */, + 938F2AF6EC5CD0B839300DB0 /* query.pb.cc in Sources */, + 7EF540911720DAAF516BEDF0 /* query_test.cc in Sources */, + 37EC6C6EA9169BB99078CA96 /* reference_set_test.cc in Sources */, + C7F174164D7C55E35A526009 /* resource_path_test.cc in Sources */, + 4DAF501EE4B4DB79ED4239B0 /* secure_random_test.cc in Sources */, + D57F4CB3C92CE3D4DF329B78 /* serializer_test.cc in Sources */, + 5D45CC300ED037358EF33A8F /* snapshot_version_test.cc in Sources */, + 862B1AC9EDAB309BBF4FB18C /* sorted_map_test.cc in Sources */, + 4A62B708A6532DD45414DA3A /* sorted_set_test.cc in Sources */, + C9F96C511F45851D38EC449C /* status.pb.cc in Sources */, + 4DC660A62BC2B6369DA5C563 /* status_test.cc in Sources */, + 74985DE2C7EF4150D7A455FD /* statusor_test.cc in Sources */, + C5C01A1FB216DA4BA8BF1A02 /* stream_test.mm in Sources */, + F9DC01FCBE76CD4F0453A67C /* strerror_test.cc in Sources */, + 5EFBAD082CB0F86CD0711979 /* string_apple_test.mm in Sources */, + 56D85436D3C864B804851B15 /* string_format_apple_test.mm in Sources */, + 1F998DDECB54A66222CC66AA /* string_format_test.cc in Sources */, + 8C39F6D4B3AA9074DF00CFB8 /* string_util_test.cc in Sources */, + 229D1A9381F698D71F229471 /* string_win_test.cc in Sources */, + 4A3FF3B16A39A5DC6B7EBA51 /* target.pb.cc in Sources */, + E764F0F389E7119220EB212C /* target_id_generator_test.cc in Sources */, + 32A95242C56A1A230231DB6A /* testutil.cc in Sources */, + ACC9369843F5ED3BD2284078 /* timestamp_test.cc in Sources */, + 2AAEABFD550255271E3BAC91 /* to_string_apple_test.mm in Sources */, + 1E2AE064CF32A604DC7BFD4D /* to_string_test.cc in Sources */, + 4F67086B5CC1787F612AE503 /* token_test.cc in Sources */, + 8DA258092DD856D829D973B5 /* transform_operations_test.mm in Sources */, + 5F19F66D8B01BA2B97579017 /* tree_sorted_map_test.cc in Sources */, + 16FE432587C1B40AF08613D2 /* type_traits_apple_test.mm in Sources */, + 16F52ECC6FA8A0587CD779EB /* user_test.cc in Sources */, + E3C0E5F834A82EEE9F8C4519 /* watch_change_test.mm in Sources */, + 53AB47E44D897C81A94031F6 /* write.pb.cc in Sources */, + 2E6E6164F44B9E3C6BB88313 /* xcgmock_test.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54C9EDED2040E16300A969CD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2199,6 +2636,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 544AB1982248072200F851E6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */; + targetProxy = 544AB1972248072200F851E6 /* PBXContainerItemProxy */; + }; 54C9EDF72040E16300A969CD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 6003F589195388D20070C39A /* Firestore_Example_iOS */; @@ -2277,6 +2719,156 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 544AB1992248072200F851E6 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BD01F0E43E4E2A07B8B05099 /* Pods-Firestore_Tests_macOS.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/ProtobufCpp/src\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"z\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"GTMSessionFetcher\"", + "-framework", + "\"GoogleTest\"", + "-framework", + "\"GoogleUtilities\"", + "-framework", + "\"OCMock\"", + "-framework", + "\"Protobuf\"", + "-framework", + "\"ProtobufCpp\"", + "-framework", + "\"Security\"", + "-framework", + "\"SystemConfiguration\"", + "-framework", + "\"grpc\"", + "-framework", + "\"grpcpp\"", + "-framework", + "\"leveldb\"", + "-framework", + "\"nanopb\"", + "-framework", + "\"openssl_grpc\"", + "-framework", + "\"FirebaseFirestore\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-Tests-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SYSTEM_HEADER_SEARCH_PATHS = "\"${PODS_ROOT}/nanopb\""; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_macOS.app/Contents/MacOS/Firestore_Example_macOS"; + }; + name = Debug; + }; + 544AB19A2248072200F851E6 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 397FB002E298B780F1E223E2 /* Pods-Firestore_Tests_macOS.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/ProtobufCpp/src\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"z\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"GTMSessionFetcher\"", + "-framework", + "\"GoogleTest\"", + "-framework", + "\"GoogleUtilities\"", + "-framework", + "\"OCMock\"", + "-framework", + "\"Protobuf\"", + "-framework", + "\"ProtobufCpp\"", + "-framework", + "\"Security\"", + "-framework", + "\"SystemConfiguration\"", + "-framework", + "\"grpc\"", + "-framework", + "\"grpcpp\"", + "-framework", + "\"leveldb\"", + "-framework", + "\"nanopb\"", + "-framework", + "\"openssl_grpc\"", + "-framework", + "\"FirebaseFirestore\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-Tests-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SYSTEM_HEADER_SEARCH_PATHS = "\"${PODS_ROOT}/nanopb\""; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_macOS.app/Contents/MacOS/Firestore_Example_macOS"; + }; + name = Release; + }; 54C9EDF82040E16300A969CD /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */; @@ -2881,7 +3473,7 @@ }; DAFF0D0321E64AC40062958F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 98366480BD1FD44A1FEDD982 /* Pods-macOS_example.debug.xcconfig */; + baseConfigurationReference = 98366480BD1FD44A1FEDD982 /* Pods-Firestore_Example_macOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -2901,16 +3493,14 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; - CODE_SIGN_ENTITLEMENTS = App/macOS_example/macOS_example.entitlements; - CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = 63PT2H4G8K; + DEVELOPMENT_TEAM = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = c99; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "$(SRCROOT)/App/macOS_example/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/App/macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; @@ -2954,7 +3544,7 @@ }; DAFF0D0421E64AC40062958F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DF148C0D5EEC4A2CD9FA484C /* Pods-macOS_example.release.xcconfig */; + baseConfigurationReference = DF148C0D5EEC4A2CD9FA484C /* Pods-Firestore_Example_macOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -2974,17 +3564,15 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; - CODE_SIGN_ENTITLEMENTS = App/macOS_example/macOS_example.entitlements; - CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = 63PT2H4G8K; + DEVELOPMENT_TEAM = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = c99; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "$(SRCROOT)/App/macOS_example/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/App/macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; @@ -3121,6 +3709,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 544AB19B2248072200F851E6 /* Build configuration list for PBXNativeTarget "Firestore_Tests_macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 544AB1992248072200F851E6 /* Debug */, + 544AB19A2248072200F851E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 54C9EDFA2040E16300A969CD /* Build configuration list for PBXNativeTarget "Firestore_SwiftTests_iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3175,7 +3772,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - DAFF0D0521E64AC40062958F /* Build configuration list for PBXNativeTarget "macOS_example" */ = { + DAFF0D0521E64AC40062958F /* Build configuration list for PBXNativeTarget "Firestore_Example_macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( DAFF0D0321E64AC40062958F /* Debug */, diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_macOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_macOS.xcscheme new file mode 100644 index 00000000000..494fcb36418 --- /dev/null +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_macOS.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_macOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_macOS.xcscheme new file mode 100644 index 00000000000..72bccff25c7 --- /dev/null +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_macOS.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index 00b6faf61c8..01be81c33e2 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -60,10 +60,20 @@ target 'Firestore_Example_iOS' do end end -target 'macOS_example' do +target 'Firestore_Example_macOS' do platform :osx, '10.10' pod 'FirebaseAuth', :path => '../../' pod 'FirebaseCore', :path => '../../' pod 'FirebaseFirestore', :path => '../../' + + target 'Firestore_Tests_macOS' do + inherit! :search_paths + + pod 'GoogleTest', :podspec => 'GoogleTest.podspec' + pod 'ProtobufCpp', :podspec => 'ProtobufCpp.podspec' + + pod 'OCMock' + pod 'leveldb-library' + end end diff --git a/scripts/sync_project.rb b/scripts/sync_project.rb index 8fd6d29b542..96c842dc372 100755 --- a/scripts/sync_project.rb +++ b/scripts/sync_project.rb @@ -62,20 +62,22 @@ def sync_firestore() 'SwiftTests', ] - s.target 'Firestore_Tests_iOS' do |t| - t.source_files = [ - 'Firestore/Example/Tests/**', - 'Firestore/core/test/**', - 'Firestore/Protos/cpp/**', - 'Firestore/third_party/Immutable/Tests/**', - ] - t.exclude_files = [ - # needs to be in project but not in target - 'Firestore/Example/Tests/Tests-Info.plist', - - # These files are integration tests, handled below - 'Firestore/Example/Tests/Integration/**', - ] + ['Firestore_Tests_iOS', 'Firestore_Tests_macOS'].each do |target_name| + s.target target_name do |t| + t.source_files = [ + 'Firestore/Example/Tests/**', + 'Firestore/core/test/**', + 'Firestore/Protos/cpp/**', + 'Firestore/third_party/Immutable/Tests/**', + ] + t.exclude_files = [ + # needs to be in project but not in target + 'Firestore/Example/Tests/Tests-Info.plist', + + # These files are integration tests, handled below + 'Firestore/Example/Tests/Integration/**', + ] + end end s.target 'Firestore_IntegrationTests_iOS' do |t| From d87b15fa69a59ac80e92c1ea76d95e26e7363432 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 26 Mar 2019 16:25:46 -0700 Subject: [PATCH 102/214] fix analyze warning in fcm (#2650) --- Example/Messaging/Tests/FIRMessagingReceiverTest.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Example/Messaging/Tests/FIRMessagingReceiverTest.m b/Example/Messaging/Tests/FIRMessagingReceiverTest.m index 95e6dd9c497..e0fd7e78f73 100644 --- a/Example/Messaging/Tests/FIRMessagingReceiverTest.m +++ b/Example/Messaging/Tests/FIRMessagingReceiverTest.m @@ -47,6 +47,8 @@ - (void)tearDown { } - (void)testUseMessagingDelegate { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" XCTAssertFalse(_messaging.useMessagingDelegateForDirectChannel); _messaging.useMessagingDelegateForDirectChannel = YES; @@ -67,7 +69,7 @@ - (void)testUseMessagingDelegateFlagOverridedByPlistWithTrueValue { OCMStub([bundleMock objectForInfoDictionaryKey:kFIRMessagingPlistUseMessagingDelegate]) .andReturn(@YES); XCTAssertTrue(_messaging.useMessagingDelegateForDirectChannel); - +#pragma clang diagnostic pop [bundleMock stopMocking]; } @end From 208f053e932ff6d755b5ff10748e8b0bda840f73 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 26 Mar 2019 17:20:46 -0700 Subject: [PATCH 103/214] Disable gRPC death tests (#2643) * Remove default app that writes to Firestore. This ends up running during unit test execution and that doesn't work on Travis. * Remove gRPC death tests These are expensive because they actually are catching abort(). --- Firestore/Example/App/macOS/AppDelegate.m | 23 ------------------- .../firestore/remote/grpc_stream_test.cc | 12 ---------- .../remote/grpc_streaming_reader_test.cc | 4 ---- .../firestore/remote/grpc_unary_call_test.cc | 17 -------------- 4 files changed, 56 deletions(-) diff --git a/Firestore/Example/App/macOS/AppDelegate.m b/Firestore/Example/App/macOS/AppDelegate.m index 0c753a7eed7..03c73970596 100644 --- a/Firestore/Example/App/macOS/AppDelegate.m +++ b/Firestore/Example/App/macOS/AppDelegate.m @@ -26,29 +26,6 @@ @interface AppDelegate () @implementation AppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - // create a firestore db - NSString *filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" - ofType:@"plist"]; - FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath]; - [FIRApp configureWithOptions:options]; - FIRFirestore *db = [FIRFirestore firestore]; - - // do the timestamp fix - FIRFirestoreSettings *settings = db.settings; - db.settings = settings; - - // create a doc - FIRDocumentReference *docRef = [[db collectionWithPath:@"junk"] documentWithPath:@"test_doc"]; - NSDictionary *data = @{@"msg" : @"hello"}; - - [docRef setData:data - completion:^(NSError *_Nullable error) { - if (error != nil) { - NSLog(@"created error: %@", error); - } else { - NSLog(@"Yay!"); - } - }]; } - (void)applicationWillTerminate:(NSNotification *)aNotification { diff --git a/Firestore/core/test/firebase/firestore/remote/grpc_stream_test.cc b/Firestore/core/test/firebase/firestore/remote/grpc_stream_test.cc index 265ee9c0643..681413a7f1a 100644 --- a/Firestore/core/test/firebase/firestore/remote/grpc_stream_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/grpc_stream_test.cc @@ -196,18 +196,6 @@ TEST_F(GrpcStreamTest, CanGetResponseHeadersAfterFinishing) { }); } -// Death tests should contain the word "DeathTest" in their name -- see -// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#death-test-naming -using GrpcStreamDeathTest = GrpcStreamTest; - -TEST_F(GrpcStreamDeathTest, CannotRestart) { - worker_queue.EnqueueBlocking([&] { stream->Start(); }); - KeepPollingGrpcQueue(); - worker_queue.EnqueueBlocking([&] { stream->FinishImmediately(); }); - worker_queue.EnqueueBlocking( - [&] { EXPECT_DEATH_IF_SUPPORTED(stream->Start(), ""); }); -} - // Read and write TEST_F(GrpcStreamTest, ReadIsAutomaticallyReadded) { diff --git a/Firestore/core/test/firebase/firestore/remote/grpc_streaming_reader_test.cc b/Firestore/core/test/firebase/firestore/remote/grpc_streaming_reader_test.cc index 0e73cdd4a94..0a1a4c1cb4a 100644 --- a/Firestore/core/test/firebase/firestore/remote/grpc_streaming_reader_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/grpc_streaming_reader_test.cc @@ -136,10 +136,6 @@ TEST_F(GrpcStreamingReaderTest, CanGetResponseHeadersAfterFinishing) { // Method prerequisites -- incorrect usage -// Death tests should contain the word "DeathTest" in their name -- see -// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#death-test-naming -using GrpcStreamingReaderDeathTest = GrpcStreamingReaderTest; - TEST_F(GrpcStreamingReaderTest, CannotFinishAndNotifyBeforeStarting) { // No callback has been assigned. worker_queue.EnqueueBlocking( diff --git a/Firestore/core/test/firebase/firestore/remote/grpc_unary_call_test.cc b/Firestore/core/test/firebase/firestore/remote/grpc_unary_call_test.cc index 017303ea66e..4d19b235d18 100644 --- a/Firestore/core/test/firebase/firestore/remote/grpc_unary_call_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/grpc_unary_call_test.cc @@ -119,23 +119,6 @@ TEST_F(GrpcUnaryCallTest, CanGetResponseHeadersAfterFinishing) { }); } -// Method prerequisites -- incorrect usage - -// Death tests should contain the word "DeathTest" in their name -- see -// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#death-test-naming -using GrpcUnaryCallDeathTest = GrpcUnaryCallTest; - -TEST_F(GrpcUnaryCallDeathTest, CannotStartTwice) { - StartCall(); - EXPECT_DEATH_IF_SUPPORTED(StartCall(), ""); -} - -TEST_F(GrpcUnaryCallDeathTest, CannotRestart) { - StartCall(); - ForceFinish({{Type::Finish, CompletionResult::Ok}}); - EXPECT_DEATH_IF_SUPPORTED(StartCall(), ""); -} - TEST_F(GrpcUnaryCallTest, CannotFinishAndNotifyBeforeStarting) { // No callback has been assigned. worker_queue.EnqueueBlocking( From 0091515b980d611761b5ff2d0779010250ef77c7 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 26 Mar 2019 20:15:34 -0700 Subject: [PATCH 104/214] add image on notification support (#2644) --- Example/Firebase.xcodeproj/project.pbxproj | 7 ++ .../Tests/FIRMessagingExtensionHelperTest.m | 109 ++++++++++++++++ Firebase/Messaging/CHANGELOG.md | 3 + Firebase/Messaging/FIRMMessageCode.h | 7 ++ Firebase/Messaging/FIRMessaging.m | 10 ++ .../Messaging/FIRMessagingExtensionHelper.m | 117 ++++++++++++++++++ Firebase/Messaging/Public/FIRMessaging.h | 13 ++ .../Public/FIRMessagingExtensionHelper.h | 34 +++++ Firebase/Messaging/Public/FirebaseMessaging.h | 1 + 9 files changed, 301 insertions(+) create mode 100644 Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m create mode 100644 Firebase/Messaging/FIRMessagingExtensionHelper.m create mode 100644 Firebase/Messaging/Public/FIRMessagingExtensionHelper.h diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index be4844b799f..1e07faddddb 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -149,6 +149,8 @@ 511DD2A92225C8C40094D78D /* FIRMessagingAnalyticsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE37C63A2163D5F30025D03E /* FIRMessagingAnalyticsTest.m */; }; 511DD2AA2225C8D50094D78D /* FIRMessagingTestUtilities.h in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242A21EA364600BB24C6 /* FIRMessagingTestUtilities.h */; }; 511DD2AB2225C8D50094D78D /* FIRMessagingTestUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242B21EA364600BB24C6 /* FIRMessagingTestUtilities.m */; }; + 51284D16224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 51284D15224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m */; }; + 51284D17224ABE1E00274321 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 51284D15224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m */; }; 518854D92230652900CA4141 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 518854D82230652900CA4141 /* AppDelegate.m */; }; 518854DC2230652900CA4141 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 518854DB2230652900CA4141 /* ViewController.m */; }; 518854DF2230652900CA4141 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 518854DD2230652900CA4141 /* Main.storyboard */; }; @@ -1068,6 +1070,7 @@ 511DD27F2225C4D20094D78D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 511DD2882225C5A80094D78D /* Messaging_Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Messaging_Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 511DD2AC2226005D0094D78D /* FirebaseMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FirebaseMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 51284D15224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRMessagingExtensionHelperTest.m; sourceTree = ""; }; 518854D52230652900CA4141 /* InstanceID_Example_tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InstanceID_Example_tvOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; 518854D72230652900CA4141 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 518854D82230652900CA4141 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -2539,6 +2542,7 @@ DE9315C21E8738B70083EDBF /* Tests */ = { isa = PBXGroup; children = ( + 51284D15224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m */, DE8DB550221F5B470068BB0E /* FIRInstanceIDWithFCMTest.m */, DE9315C81E8738B70083EDBF /* FIRMessagingFakeConnection.h */, DE9315CA1E8738B70083EDBF /* FIRMessagingFakeSocket.h */, @@ -3790,6 +3794,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, "es-MX", @@ -4307,6 +4312,7 @@ 511DD2972225C8C40094D78D /* FIRMessagingCodedInputStreamTest.m in Sources */, 511DD2982225C8C40094D78D /* FIRMessagingConnectionTest.m in Sources */, 511DD2992225C8C40094D78D /* FIRMessagingContextManagerServiceTest.m in Sources */, + 51284D17224ABE1E00274321 /* FIRMessagingExtensionHelperTest.m in Sources */, 511DD29A2225C8C40094D78D /* FIRMessagingDataMessageManagerTest.m in Sources */, 511DD29B2225C8C40094D78D /* FIRMessagingFakeConnection.m in Sources */, 511DD29C2225C8C40094D78D /* FIRMessagingFakeSocket.m in Sources */, @@ -4937,6 +4943,7 @@ DE9315FF1E8738E60083EDBF /* FIRMessagingRemoteNotificationsProxyTest.m in Sources */, DEF61BFD216E8B1100A738D4 /* FIRMessagingReceiverTest.m in Sources */, DE9315F81E8738E60083EDBF /* FIRMessagingDataMessageManagerTest.m in Sources */, + 51284D16224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m in Sources */, DE9316051E8738E60083EDBF /* FIRMessagingTestNotificationUtilities.m in Sources */, DE37C63B2163D5F30025D03E /* FIRMessagingAnalyticsTest.m in Sources */, DE9315F61E8738E60083EDBF /* FIRMessagingConnectionTest.m in Sources */, diff --git a/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m new file mode 100644 index 00000000000..8bf554db4e5 --- /dev/null +++ b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m @@ -0,0 +1,109 @@ +/* + * Copyright 2019 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 +#import + +#import + +#import "FIRMessaging.h" +#import "FIRMessagingExtensionHelper.h" + +typedef void (^FIRMessagingContentHandler)(UNNotificationContent *content); + +static NSString *const kFCMPayloadOptionsName = @"fcm_options"; +static NSString *const kFCMPayloadOptionsImageURLName = @"image"; +static NSString *const kValidImageURL = + @"https://firebasestorage.googleapis.com/v0/b/fcm-ios-f7f9c.appspot.com/o/" + @"chubbyBunny.jpg?alt=media&token=d6c56a57-c007-4b27-b20f-f267cc83e9e5"; + +@interface FIRMessagingExtensionHelper (ExposedForTest) +#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +- (void)loadAttachmentForURL:(NSURL *)attachmentURL + completionHandler:(void (^)(UNNotificationAttachment *))completionHandler; +#endif +@end + +@interface FIRMessagingExtensionHelperTest : XCTestCase { + id _mockExtensionHelper; +} +@end + +@implementation FIRMessagingExtensionHelperTest + +- (void)setUp { + [super setUp]; + FIRMessagingExtensionHelper *extensionHelper = [FIRMessaging extensionHelper]; + _mockExtensionHelper = OCMPartialMock(extensionHelper); +} + +- (void)tearDown { + [_mockExtensionHelper stopMocking]; +} + +#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +- (void)testModifyNotificationWithValidPayloadData { + XCTestExpectation *validPayloadExpectation = + [self expectationWithDescription:@"Test payload is valid."]; + + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.userInfo = @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : kValidImageURL}}; + FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { + [validPayloadExpectation fulfill]; + }; + [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; + + OCMVerify([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] + completionHandler:[OCMArg any]]); + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +- (void)testModifyNotificationWithInvalidPayloadData { + XCTestExpectation *validPayloadExpectation = + [self expectationWithDescription:@"Test payload is valid."]; + + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.userInfo = + @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : @"a invalid URL"}}; + FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { + [validPayloadExpectation fulfill]; + }; + [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; + + OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] + completionHandler:[OCMArg any]]); + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +- (void)testModifyNotificationWithEmptyPayloadData { + XCTestExpectation *validPayloadExpectation = + [self expectationWithDescription:@"Test payload is valid."]; + + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.userInfo = + @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : @"a invalid URL"}}; + FIRMessagingContentHandler handler = ^(UNNotificationContent *content) { + [validPayloadExpectation fulfill]; + }; + [_mockExtensionHelper populateNotificationContent:content withContentHandler:handler]; + + OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any] + completionHandler:[OCMArg any]]); + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} +#endif + +@end diff --git a/Firebase/Messaging/CHANGELOG.md b/Firebase/Messaging/CHANGELOG.md index be8cbc927d3..8e6c901cac0 100644 --- a/Firebase/Messaging/CHANGELOG.md +++ b/Firebase/Messaging/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2019-04-02 -- v3.5.0 +- Add image support for notification. (#2644) + # 2019-03-19 -- v3.4.0 - Adding community support for tvOS. (#2428) diff --git a/Firebase/Messaging/FIRMMessageCode.h b/Firebase/Messaging/FIRMMessageCode.h index 4b16189affb..2a85d963df6 100644 --- a/Firebase/Messaging/FIRMMessageCode.h +++ b/Firebase/Messaging/FIRMMessageCode.h @@ -188,4 +188,11 @@ typedef NS_ENUM(NSInteger, FIRMessagingMessageCode) { kFIRMessagingMessageCodeAnalyticsInvalidEvent = 19006, // I-FCM019006 kFIRMessagingMessageCodeAnalytics007 = 19007, // I-FCM019007 kFIRMessagingMessageCodeAnalyticsCouldNotInvokeAnalyticsLog = 19008, // I-FCM019008 + + // FIRMessagingExtensionHelper.m + kFIRMessagingServiceExtensionImageInvalidURL = 20000, + kFIRMessagingServiceExtensionImageNotDownloaded = 20001, + kFIRMessagingServiceExtensionLocalFileNotCreated = 20002, + kFIRMessagingServiceExtensionImageNotAttached = 20003, + }; diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index d941014169a..6a9870281f6 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -29,6 +29,7 @@ #import "FIRMessagingContextManagerService.h" #import "FIRMessagingDataMessageManager.h" #import "FIRMessagingDefines.h" +#import "FIRMessagingExtensionHelper.h" #import "FIRMessagingLogger.h" #import "FIRMessagingPubSub.h" #import "FIRMessagingReceiver.h" @@ -178,6 +179,15 @@ + (FIRMessaging *)messaging { return messaging; } ++ (FIRMessagingExtensionHelper *)extensionHelper { + static dispatch_once_t once; + static FIRMessagingExtensionHelper *extensionHelper; + dispatch_once(&once, ^{ + extensionHelper = [[FIRMessagingExtensionHelper alloc] init]; + }); + return extensionHelper; +} + - (instancetype)initWithAnalytics:(nullable id)analytics withInstanceID:(FIRInstanceID *)instanceID withUserDefaults:(GULUserDefaults *)defaults { diff --git a/Firebase/Messaging/FIRMessagingExtensionHelper.m b/Firebase/Messaging/FIRMessagingExtensionHelper.m new file mode 100644 index 00000000000..7b436e3cb34 --- /dev/null +++ b/Firebase/Messaging/FIRMessagingExtensionHelper.m @@ -0,0 +1,117 @@ +/* + * Copyright 2019 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 "FIRMessagingExtensionHelper.h" + + #import "FIRMMessageCode.h" +#import "FIRMessagingLogger.h" + + static NSString *const kPayloadOptionsName = @"fcm_options"; +static NSString *const kPayloadOptionsImageURLName = @"image"; + + @interface FIRMessagingExtensionHelper () +@property(nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); +@property(nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; + + @end + + @implementation FIRMessagingExtensionHelper + + - (void)populateNotificationContent:(UNMutableNotificationContent *)content + withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler { + self.contentHandler = [contentHandler copy]; + self.bestAttemptContent = content; + + NSString *currentImageURL = content.userInfo[kPayloadOptionsName][kPayloadOptionsImageURLName]; + if (!currentImageURL) { + [self deliverNotification]; + return; + } +#if TARGET_OS_IOS + NSURL *attachmentURL = [NSURL URLWithString:currentImageURL]; + if (attachmentURL) { + [self loadAttachmentForURL:attachmentURL + completionHandler:^(UNNotificationAttachment *attachment) { + self.bestAttemptContent.attachments = @[ attachment ]; + [self deliverNotification]; + }]; + } else { + FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageInvalidURL, + @"The Image URL provided is invalid %@.", currentImageURL); + [self deliverNotification]; + } +#else + [self deliverNotification]; +#endif +} + + #if TARGET_OS_IOS +- (void)loadAttachmentForURL:(NSURL *)attachmentURL + completionHandler:(void (^)(UNNotificationAttachment *))completionHandler { + __block UNNotificationAttachment *attachment = nil; + + NSURLSession *session = [NSURLSession + sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + [[session + downloadTaskWithURL:attachmentURL + completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) { + if (error != nil) { + FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotDownloaded, + @"Failed to download image given URL %@, error: %@\n", + attachmentURL, error); + completionHandler(attachment); + return; + } + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *fileExtension = + [NSString stringWithFormat:@".%@", [response.suggestedFilename pathExtension]]; + NSURL *localURL = [NSURL + fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExtension]]; + [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error]; + if (error) { + FIRMessagingLoggerError( + kFIRMessagingServiceExtensionLocalFileNotCreated, + @"Failed to move the image file to local location: %@, error: %@\n", localURL, + error); + completionHandler(attachment); + return; + } + + attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" + URL:localURL + options:nil + error:&error]; + if (error) { + FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotAttached, + @"Failed to create attachment with URL %@, error: %@\n", + localURL, error); + completionHandler(attachment); + return; + } + completionHandler(attachment); + }] resume]; +} +#endif + + - (void)deliverNotification { + if (self.contentHandler) { + self.contentHandler(self.bestAttemptContent); + } +} + + @end + diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h index 5b8e7ad4d2d..10b84dd4d33 100644 --- a/Firebase/Messaging/Public/FIRMessaging.h +++ b/Firebase/Messaging/Public/FIRMessaging.h @@ -245,6 +245,8 @@ NS_SWIFT_NAME(MessagingRemoteMessage) @end @class FIRMessaging; +@class FIRMessagingExtensionHelper; + /** * A protocol to handle token update or data message delivery from FCM. * @@ -331,6 +333,17 @@ NS_SWIFT_NAME(Messaging) */ + (instancetype)messaging NS_SWIFT_NAME(messaging()); +/** + * FIRMessagingExtensionHelper + * + * Use FIRMessagingExtensionHelper to populate rich UI contents for your notifications. + * e.g. If an image URL is set in your notification payload or on the console, call + * FIRMessagingExtensionHelper API to render it on your notification. + * + * @return An instance of FIRMessagingExtensionHelper that handles the extensions API. + */ ++ (FIRMessagingExtensionHelper *)extensionHelper NS_SWIFT_NAME(serviceExtension()) NS_AVAILABLE_IOS(10.0); + /** * Unavailable. Use +messaging instead. */ diff --git a/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h b/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h new file mode 100644 index 00000000000..f50478ecf82 --- /dev/null +++ b/Firebase/Messaging/Public/FIRMessagingExtensionHelper.h @@ -0,0 +1,34 @@ +/* + * Copyright 2019 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. + */ +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +#import +#endif + + NS_ASSUME_NONNULL_BEGIN + + __IOS_AVAILABLE(10.0) +@interface FIRMessagingExtensionHelper : NSObject + + /// Call this API to complete your notification content modification. If you like to +/// overwrite some properties of the content instead of using the default payload, +/// make sure to make your customized motification to the content before passing it to +/// this call. +- (void)populateNotificationContent:(UNMutableNotificationContent *)content + withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler; + + @end + + NS_ASSUME_NONNULL_END diff --git a/Firebase/Messaging/Public/FirebaseMessaging.h b/Firebase/Messaging/Public/FirebaseMessaging.h index ef081c90f55..c5d0bd05046 100755 --- a/Firebase/Messaging/Public/FirebaseMessaging.h +++ b/Firebase/Messaging/Public/FirebaseMessaging.h @@ -15,3 +15,4 @@ */ #import "FIRMessaging.h" +#import "FIRMessagingExtensionHelper.h" From 3a24929307482ae4e2c42e1125738b52f54c5850 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 27 Mar 2019 07:45:19 -0700 Subject: [PATCH 105/214] M46 Core Changelog (#2655) --- Firebase/Core/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index 05eb0cd3a03..337d91a7a55 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,3 +1,8 @@ +# 2019-04-02 -- v5.4.1 -- M46 +- [changed] Avoid using NSRegularExpression in FIRApp. +- [changed] Improve error meessage for invalid app names. (#2614) +- [changed] FIRApp thread safety fixes. (#2639) + # 2019-03-19 -- v5.4.0 -- M45 - [changed] Allow Bundle IDs that have a valid prefix to enable richer extension support. (#2515) - [changed] Deprecated `FIRAnalyticsConfiguration` API in favor of new methods on the Analytics SDK. From ca8e7f2c27de9a06cf6498fab137b2cda495dc08 Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 27 Mar 2019 08:36:09 -0700 Subject: [PATCH 106/214] Build Firestore macOS on Travis (#2645) * Add support for testing Firestore macOS on Travis * Firestore_{Swift,Integration}Tests_macOS don't exist yet --- .travis.yml | 8 ++++++++ scripts/build.sh | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/.travis.yml b/.travis.yml index b0ab6b686f4..fe33f8b9d4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -155,6 +155,14 @@ jobs: script: - travis_retry ./scripts/if_changed.sh ./scripts/build.sh $PROJECT $PLATFORM + - stage: test + env: + - PROJECT=Firestore PLATFORM=macOS METHOD=xcodebuild + before_install: + - ./scripts/if_changed.sh ./scripts/install_prereqs.sh + script: + - travis_retry ./scripts/if_changed.sh ./scripts/build.sh $PROJECT $PLATFORM + # Firestore sanitizers - stage: test diff --git a/scripts/build.sh b/scripts/build.sh index 0441ae1a057..5dfa97b0b89 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -304,7 +304,16 @@ case "$product-$method-$platform" in -scheme "Firestore_IntegrationTests_$platform" \ "${xcb_flags[@]}" \ build + ;; + Firestore-xcodebuild-macOS) + # TODO(wilhuff): Combine with above once all targets exist + RunXcodebuild \ + -workspace 'Firestore/Example/Firestore.xcworkspace' \ + -scheme "Firestore_Tests_$platform" \ + "${xcb_flags[@]}" \ + build \ + test ;; Firestore-cmake-macOS) From 6603b86988f42694e9b76009ec815ab23f49e407 Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 27 Mar 2019 09:00:44 -0700 Subject: [PATCH 107/214] Update CHANGELOG for Firestore v1.2.0 (#2656) --- Firestore/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 1ff2bf5c443..5d02091e1e4 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,4 +1,7 @@ # Unreleased + +# 1.2.0 +- [feature] Added community support for macOS (#434). - [fixed] Fixed the way gRPC certificates are loaded on macOS (#2604). # 1.1.0 From 6e5221ba41883a7389e9ba4028ad9cff3bce4962 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Wed, 27 Mar 2019 12:26:23 -0400 Subject: [PATCH 108/214] Eliminate 'instanceof' checks for FSTFieldValue (#2651) Previously, we checked for FSTFieldValue subtypes via: `[value isKindOfClass:[FSTStringValue class]]` Now, we check via a new type parameter on FSTFieldValue, i.e.: `value.type == firebase::firestore::model::FieldValue::Type::String` Or with the appropriate using declaration: `value.type == FieldValue::Type::String` This should enable easier transition to C++. --- .../Example/Tests/Model/FSTFieldValueTests.mm | 15 +++ Firestore/Example/Tests/Util/FSTHelpers.mm | 2 +- Firestore/Source/API/FIRDocumentSnapshot.mm | 8 +- Firestore/Source/API/FIRQuery.mm | 4 +- Firestore/Source/Core/FSTQuery.mm | 6 +- Firestore/Source/Model/FSTFieldValue.h | 11 +- Firestore/Source/Model/FSTFieldValue.mm | 123 +++++++++++++----- .../firebase/firestore/model/field_value.h | 8 ++ .../firestore/model/transform_operations.h | 13 +- 9 files changed, 146 insertions(+), 44 deletions(-) diff --git a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm index 76ff572c2c5..94b6807fef6 100644 --- a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm +++ b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm @@ -27,12 +27,14 @@ #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_value.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::FieldValue; /** Helper to wrap the values in a set of equality groups using FSTTestFieldValue(). */ NSArray *FSTWrapGroups(NSArray *groups) { @@ -91,6 +93,7 @@ - (void)testWrapIntegers { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTIntegerValue class]); XCTAssertEqualObjects([wrapped value], @([value longLongValue])); + XCTAssertEqual(wrapped.type, FieldValue::Type::Integer); } } @@ -105,6 +108,7 @@ - (void)testWrapsDoubles { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTDoubleValue class]); XCTAssertEqualObjects([wrapped value], value); + XCTAssertEqual(wrapped.type, FieldValue::Type::Double); } } @@ -113,6 +117,7 @@ - (void)testWrapsNilAndNSNull { XCTAssertEqual(FSTTestFieldValue(nil), nullValue); XCTAssertEqual(FSTTestFieldValue([NSNull null]), nullValue); XCTAssertEqual([nullValue value], [NSNull null]); + XCTAssertEqual(nullValue.type, FieldValue::Type::Null); } - (void)testWrapsBooleans { @@ -121,6 +126,7 @@ - (void)testWrapsBooleans { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTBooleanValue class]); XCTAssertEqualObjects([wrapped value], value); + XCTAssertEqual(wrapped.type, FieldValue::Type::Boolean); } // Unsigned chars could conceivably be handled consistently with signed chars but on arm64 these @@ -219,6 +225,7 @@ - (void)testWrapStrings { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTStringValue class]); XCTAssertEqualObjects([wrapped value], value); + XCTAssertEqual(wrapped.type, FieldValue::Type::String); } } @@ -229,6 +236,7 @@ - (void)testWrapDates { XCTAssertEqualObjects([wrapped class], [FSTTimestampValue class]); XCTAssertEqualObjects([[wrapped value] class], [FIRTimestamp class]); XCTAssertEqualObjects([wrapped value], [FIRTimestamp timestampWithDate:value]); + XCTAssertEqual(wrapped.type, FieldValue::Type::Timestamp); } } @@ -239,6 +247,7 @@ - (void)testWrapGeoPoints { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTGeoPointValue class]); XCTAssertEqualObjects([wrapped value], value); + XCTAssertEqual(wrapped.type, FieldValue::Type::GeoPoint); } } @@ -248,6 +257,7 @@ - (void)testWrapBlobs { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTBlobValue class]); XCTAssertEqualObjects([wrapped value], value); + XCTAssertEqual(wrapped.type, FieldValue::Type::Blob); } } @@ -262,11 +272,13 @@ - (void)testWrapResourceNames { XCTAssertEqualObjects([wrapped value], [FSTDocumentKey keyWithDocumentKey:value.key]); XCTAssertTrue(*((FSTReferenceValue *)wrapped).databaseID == *(const DatabaseId *)(value.databaseID)); + XCTAssertEqual(wrapped.type, FieldValue::Type::Reference); } } - (void)testWrapsEmptyObjects { XCTAssertEqualObjects(FSTTestFieldValue(@{}), [FSTObjectValue objectValue]); + XCTAssertEqual(FSTTestFieldValue(@{}).type, FieldValue::Type::Object); } - (void)testWrapsSimpleObjects { @@ -279,6 +291,7 @@ - (void)testWrapsSimpleObjects { @"d" : [FSTNullValue nullValue] }]; XCTAssertEqualObjects(actual, expected); + XCTAssertEqual(actual.type, FieldValue::Type::Object); } - (void)testWrapsNestedObjects { @@ -291,6 +304,7 @@ - (void)testWrapsNestedObjects { }] }]; XCTAssertEqualObjects(actual, expected); + XCTAssertEqual(actual.type, FieldValue::Type::Object); } - (void)testExtractsFields { @@ -414,6 +428,7 @@ - (void)testArrays { FSTArrayValue *actual = (FSTArrayValue *)FSTTestFieldValue(@[ @"value", @YES ]); XCTAssertEqualObjects(actual, expected); + XCTAssertEqual(actual.type, FieldValue::Type::Array); } - (void)testValueEquality { diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm index db77d439c9e..1ce94651b84 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.mm +++ b/Firestore/Example/Tests/Util/FSTHelpers.mm @@ -147,7 +147,7 @@ FSTObjectValue *FSTTestObjectValue(NSDictionary *data) { FSTFieldValue *wrapped = FSTTestFieldValue(data); - HARD_ASSERT([wrapped isKindOfClass:[FSTObjectValue class]], "Unsupported value: %s", data); + HARD_ASSERT(wrapped.type == FieldValue::Type::Object, "Unsupported value: %s", data); return (FSTObjectValue *)wrapped; } diff --git a/Firestore/Source/API/FIRDocumentSnapshot.mm b/Firestore/Source/API/FIRDocumentSnapshot.mm index eb210d96e02..8597e7edd49 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot.mm +++ b/Firestore/Source/API/FIRDocumentSnapshot.mm @@ -34,6 +34,7 @@ #include "Firestore/core/src/firebase/firestore/api/firestore.h" #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_value.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" @@ -42,6 +43,7 @@ using firebase::firestore::api::Firestore; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::FieldValue; using firebase::firestore::util::WrapNSString; NS_ASSUME_NONNULL_BEGIN @@ -185,11 +187,11 @@ - (FSTFieldValueOptions *)optionsForServerTimestampBehavior: } - (id)convertedValue:(FSTFieldValue *)value options:(FSTFieldValueOptions *)options { - if ([value isKindOfClass:[FSTObjectValue class]]) { + if (value.type == FieldValue::Type::Object) { return [self convertedObject:(FSTObjectValue *)value options:options]; - } else if ([value isKindOfClass:[FSTArrayValue class]]) { + } else if (value.type == FieldValue::Type::Array) { return [self convertedArray:(FSTArrayValue *)value options:options]; - } else if ([value isKindOfClass:[FSTReferenceValue class]]) { + } else if (value.type == FieldValue::Type::Reference) { FSTReferenceValue *ref = (FSTReferenceValue *)value; const DatabaseId *refDatabase = ref.databaseID; const DatabaseId *database = &_snapshot.firestore()->database_id(); diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 507e1f347b2..097ac5ef419 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -42,6 +42,7 @@ #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/field_value.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/error_apple.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" @@ -54,6 +55,7 @@ using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldPath; +using firebase::firestore::model::FieldValue; using firebase::firestore::model::ResourcePath; using firebase::firestore::util::MakeNSError; using firebase::firestore::util::StatusOr; @@ -598,7 +600,7 @@ - (FSTBound *)boundFromSnapshot:(FIRDocumentSnapshot *)snapshot isBefore:(BOOL)i } else { FSTFieldValue *value = [document fieldForPath:sortOrder.field]; - if ([value isKindOfClass:[FSTServerTimestampValue class]]) { + if (value.type == FieldValue::Type::ServerTimestamp) { FSTThrowInvalidUsage(@"InvalidQueryException", @"Invalid query. You are trying to start or end a query using a " "document for which the field '%s' is an uncommitted server " diff --git a/Firestore/Source/Core/FSTQuery.mm b/Firestore/Source/Core/FSTQuery.mm index d057c87c438..9576bee86e5 100644 --- a/Firestore/Source/Core/FSTQuery.mm +++ b/Firestore/Source/Core/FSTQuery.mm @@ -28,6 +28,7 @@ #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/field_value.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" @@ -36,6 +37,7 @@ namespace util = firebase::firestore::util; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldPath; +using firebase::firestore::model::FieldValue; using firebase::firestore::model::ResourcePath; NS_ASSUME_NONNULL_BEGIN @@ -183,7 +185,7 @@ - (BOOL)isEqual:(id)other { - (BOOL)matchesDocument:(FSTDocument *)document { if (_field.IsKeyFieldPath()) { - HARD_ASSERT([self.value isKindOfClass:[FSTReferenceValue class]], + HARD_ASSERT(self.value.type == FieldValue::Type::Reference, "Comparing on key, but filter value not a FSTReferenceValue."); HARD_ASSERT(self.filterOperator != FSTRelationFilterOperatorArrayContains, "arrayContains queries don't make sense on document keys."); @@ -472,7 +474,7 @@ - (BOOL)sortsBeforeDocument:(FSTDocument *)document FSTSortOrder *sortOrderComponent = sortOrder[idx]; NSComparisonResult comparison; if (sortOrderComponent.field == FieldPath::KeyFieldPath()) { - HARD_ASSERT([fieldValue isKindOfClass:[FSTReferenceValue class]], + HARD_ASSERT(fieldValue.type == FieldValue::Type::Reference, "FSTBound has a non-key value where the key path is being used %s", fieldValue); FSTReferenceValue *refValue = (FSTReferenceValue *)fieldValue; comparison = CompareKeys(refValue.value.key, document.key); diff --git a/Firestore/Source/Model/FSTFieldValue.h b/Firestore/Source/Model/FSTFieldValue.h index 5a9b696ce08..bf00c3875c5 100644 --- a/Firestore/Source/Model/FSTFieldValue.h +++ b/Firestore/Source/Model/FSTFieldValue.h @@ -21,6 +21,9 @@ #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_path.h" +#include "Firestore/core/src/firebase/firestore/model/field_value.h" + +using firebase::firestore::model::FieldValue; @class FSTDocumentKey; @class FIRTimestamp; @@ -85,8 +88,14 @@ enum class ServerTimestampBehavior { None, Estimate, Previous }; */ @interface FSTFieldValue<__covariant T> : NSObject +/** + * Returns the 'type' of this FSTFieldValue. Used for RTTI (rather than isKindOfClass) + * to ease migration to C++. + */ +@property(nonatomic, assign, readonly) FieldValue::Type type; + /** Returns the FSTTypeOrder for this value. */ -- (FSTTypeOrder)typeOrder; +@property(nonatomic, assign, readonly) FSTTypeOrder typeOrder; /** * Converts an FSTFieldValue into the value that users will see in document snapshots. diff --git a/Firestore/Source/Model/FSTFieldValue.mm b/Firestore/Source/Model/FSTFieldValue.mm index 7730f3987c1..92c351f8be3 100644 --- a/Firestore/Source/Model/FSTFieldValue.mm +++ b/Firestore/Source/Model/FSTFieldValue.mm @@ -34,6 +34,7 @@ using firebase::firestore::model::DatabaseId; using firebase::firestore::model::FieldMask; using firebase::firestore::model::FieldPath; +using firebase::firestore::model::FieldValue; using firebase::firestore::util::Comparator; using firebase::firestore::util::CompareMixedNumber; using firebase::firestore::util::DoubleBitwiseEquals; @@ -69,6 +70,13 @@ - (NSComparisonResult)defaultCompare:(FSTFieldValue *)other; @implementation FSTFieldValue +@dynamic type; +@dynamic typeOrder; + +- (FieldValue::Type)type { + @throw FSTAbstractMethodException(); // NOLINT +} + - (FSTTypeOrder)typeOrder { @throw FSTAbstractMethodException(); // NOLINT } @@ -123,6 +131,10 @@ + (instancetype)nullValue { return sharedInstance; } +- (FieldValue::Type)type { + return FieldValue::Type::Null; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderNull; } @@ -189,6 +201,10 @@ - (id)initWithValue:(BOOL)value { return self; } +- (FieldValue::Type)type { + return FieldValue::Type::Boolean; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderBoolean; } @@ -207,7 +223,7 @@ - (NSUInteger)hash { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTBooleanValue class]]) { + if (other.type == FieldValue::Type::Boolean) { return WrapCompare(self.internalValue, ((FSTBooleanValue *)other).internalValue); } else { return [self defaultCompare:other]; @@ -225,26 +241,24 @@ - (FSTTypeOrder)typeOrder { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if (![other isKindOfClass:[FSTNumberValue class]]) { + if (!FieldValue::IsNumber(other.type)) { return [self defaultCompare:other]; } else { - if ([self isKindOfClass:[FSTDoubleValue class]]) { + if (self.type == FieldValue::Type::Double) { double thisDouble = ((FSTDoubleValue *)self).internalValue; - if ([other isKindOfClass:[FSTDoubleValue class]]) { + if (other.type == FieldValue::Type::Double) { return WrapCompare(thisDouble, ((FSTDoubleValue *)other).internalValue); } else { - HARD_ASSERT([other isKindOfClass:[FSTIntegerValue class]], "Unknown number value: %s", - other); + HARD_ASSERT(other.type == FieldValue::Type::Integer, "Unknown number value: %s", other); auto result = CompareMixedNumber(thisDouble, ((FSTIntegerValue *)other).internalValue); return static_cast(result); } } else { int64_t thisInt = ((FSTIntegerValue *)self).internalValue; - if ([other isKindOfClass:[FSTIntegerValue class]]) { + if (other.type == FieldValue::Type::Integer) { return WrapCompare(thisInt, ((FSTIntegerValue *)other).internalValue); } else { - HARD_ASSERT([other isKindOfClass:[FSTDoubleValue class]], "Unknown number value: %s", - other); + HARD_ASSERT(other.type == FieldValue::Type::Double, "Unknown number value: %s", other); double otherDouble = ((FSTDoubleValue *)other).internalValue; auto result = ReverseOrder(CompareMixedNumber(otherDouble, thisInt)); return static_cast(result); @@ -279,10 +293,15 @@ - (id)value { return @(self.internalValue); } +- (FieldValue::Type)type { + return FieldValue::Type::Integer; +} + - (BOOL)isEqual:(id)other { // NOTE: DoubleValue and LongValue instances may compare: the same, but that doesn't make them // equal via isEqual: - return [other isKindOfClass:[FSTIntegerValue class]] && + return [other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::Integer && self.internalValue == ((FSTIntegerValue *)other).internalValue; } @@ -331,13 +350,18 @@ - (id)value { return @(self.internalValue); } +- (FieldValue::Type)type { + return FieldValue::Type::Double; +} + - (BOOL)isEqual:(id)other { // NOTE: DoubleValue and LongValue instances may compare: the same, but that doesn't make them // equal via isEqual: // NOTE: isEqual: should compare NaN equal to itself and -0.0 not equal to 0.0. - return [other isKindOfClass:[FSTDoubleValue class]] && + return [other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::Double && DoubleBitwiseEquals(self.internalValue, ((FSTDoubleValue *)other).internalValue); } @@ -381,6 +405,10 @@ - (id)initWithValue:(NSString *)value { return self; } +- (FieldValue::Type)type { + return FieldValue::Type::String; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderString; } @@ -390,7 +418,8 @@ - (id)value { } - (BOOL)isEqual:(id)other { - return [other isKindOfClass:[FSTStringValue class]] && + return [other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::String && [self.internalValue isEqualToString:((FSTStringValue *)other).internalValue]; } @@ -399,7 +428,7 @@ - (NSUInteger)hash { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTStringValue class]]) { + if (other.type == FieldValue::Type::String) { return WrapCompare(self.internalValue, ((FSTStringValue *)other).internalValue); } else { return [self defaultCompare:other]; @@ -428,6 +457,10 @@ - (id)initWithValue:(FIRTimestamp *)value { return self; } +- (FieldValue::Type)type { + return FieldValue::Type::Timestamp; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderTimestamp; } @@ -445,7 +478,8 @@ - (id)valueWithOptions:(FSTFieldValueOptions *)options { } - (BOOL)isEqual:(id)other { - return [other isKindOfClass:[FSTTimestampValue class]] && + return [other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::Timestamp && [self.internalValue isEqual:((FSTTimestampValue *)other).internalValue]; } @@ -454,9 +488,9 @@ - (NSUInteger)hash { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTTimestampValue class]]) { + if (other.type == FieldValue::Type::Timestamp) { return [self.internalValue compare:((FSTTimestampValue *)other).internalValue]; - } else if ([other isKindOfClass:[FSTServerTimestampValue class]]) { + } else if (other.type == FieldValue::Type::ServerTimestamp) { // Concrete timestamps come before server timestamps. return NSOrderedAscending; } else { @@ -485,6 +519,10 @@ - (id)initWithLocalWriteTime:(FIRTimestamp *)localWriteTime return self; } +- (FieldValue::Type)type { + return FieldValue::Type::ServerTimestamp; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderTimestamp; } @@ -507,7 +545,8 @@ - (id)valueWithOptions:(FSTFieldValueOptions *)options { } - (BOOL)isEqual:(id)other { - return [other isKindOfClass:[FSTServerTimestampValue class]] && + return [other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::ServerTimestamp && [self.localWriteTime isEqual:((FSTServerTimestampValue *)other).localWriteTime]; } @@ -520,9 +559,9 @@ - (NSString *)description { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTServerTimestampValue class]]) { + if (other.type == FieldValue::Type::ServerTimestamp) { return [self.localWriteTime compare:((FSTServerTimestampValue *)other).localWriteTime]; - } else if ([other isKindOfClass:[FSTTimestampValue class]]) { + } else if (other.type == FieldValue::Type::Timestamp) { // Server timestamps come after all concrete timestamps. return NSOrderedDescending; } else { @@ -552,6 +591,10 @@ - (id)initWithValue:(FIRGeoPoint *)value { return self; } +- (FieldValue::Type)type { + return FieldValue::Type::GeoPoint; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderGeoPoint; } @@ -561,7 +604,8 @@ - (id)value { } - (BOOL)isEqual:(id)other { - return [other isKindOfClass:[FSTGeoPointValue class]] && + return [other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::GeoPoint && [self.internalValue isEqual:((FSTGeoPointValue *)other).internalValue]; } @@ -570,7 +614,7 @@ - (NSUInteger)hash { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTGeoPointValue class]]) { + if (other.type == FieldValue::Type::GeoPoint) { return [self.internalValue compare:((FSTGeoPointValue *)other).internalValue]; } else { return [self defaultCompare:other]; @@ -615,6 +659,10 @@ - (id)initWithValue:(NSData *)value { return self; } +- (FieldValue::Type)type { + return FieldValue::Type::Blob; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderBlob; } @@ -624,7 +672,8 @@ - (id)value { } - (BOOL)isEqual:(id)other { - return [other isKindOfClass:[FSTBlobValue class]] && + return [other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::Blob && [self.internalValue isEqual:((FSTBlobValue *)other).internalValue]; } @@ -633,7 +682,7 @@ - (NSUInteger)hash { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTBlobValue class]]) { + if (other.type == FieldValue::Type::Blob) { return CompareBytes(self.internalValue, ((FSTBlobValue *)other).internalValue); } else { return [self defaultCompare:other]; @@ -667,6 +716,10 @@ - (id)value { return self.key; } +- (FieldValue::Type)type { + return FieldValue::Type::Reference; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderReference; } @@ -675,7 +728,8 @@ - (BOOL)isEqual:(id)other { if (other == self) { return YES; } - if (![other isKindOfClass:[FSTReferenceValue class]]) { + if (!([other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::Reference)) { return NO; } @@ -690,7 +744,7 @@ - (NSUInteger)hash { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTReferenceValue class]]) { + if (other.type == FieldValue::Type::Reference) { FSTReferenceValue *ref = (FSTReferenceValue *)other; NSComparisonResult cmp = WrapCompare(self.databaseID->project_id(), ref.databaseID->project_id()); @@ -764,6 +818,10 @@ - (id)valueWithOptions:(FSTFieldValueOptions *)options { return result; } +- (FieldValue::Type)type { + return FieldValue::Type::Object; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderObject; } @@ -772,7 +830,8 @@ - (BOOL)isEqual:(id)other { if (other == self) { return YES; } - if (![other isKindOfClass:[FSTObjectValue class]]) { + if (!([other isKindOfClass:[FSTFieldValue class]] && + ((FSTFieldValue *)other).type == FieldValue::Type::Object)) { return NO; } @@ -785,7 +844,7 @@ - (NSUInteger)hash { } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTObjectValue class]]) { + if (other.type == FieldValue::Type::Object) { FSTImmutableSortedDictionary *selfDict = self.internalValue; FSTImmutableSortedDictionary *otherDict = ((FSTObjectValue *)other).internalValue; NSEnumerator *enumerator1 = [selfDict keyEnumerator]; @@ -838,7 +897,7 @@ - (FSTObjectValue *)objectBySettingValue:(FSTFieldValue *)value // around the result. FSTFieldValue *child = [_internalValue objectForKey:childName]; FSTObjectValue *childObject; - if ([child isKindOfClass:[FSTObjectValue class]]) { + if (child.type == FieldValue::Type::Object) { childObject = (FSTObjectValue *)child; } else { // If the child is not found or is a primitive type, pretend as if an empty object lived @@ -858,7 +917,7 @@ - (FSTObjectValue *)objectByDeletingPath:(const FieldPath &)fieldPath { initWithImmutableDictionary:[_internalValue dictionaryByRemovingObjectForKey:childName]]; } else { FSTFieldValue *child = _internalValue[childName]; - if ([child isKindOfClass:[FSTObjectValue class]]) { + if (child.type == FieldValue::Type::Object) { FSTObjectValue *newChild = [((FSTObjectValue *)child) objectByDeletingPath:fieldPath.PopFirst()]; return [self objectBySettingValue:newChild forField:childName]; @@ -941,12 +1000,16 @@ - (id)valueWithOptions:(FSTFieldValueOptions *)options { return result; } +- (FieldValue::Type)type { + return FieldValue::Type::Array; +} + - (FSTTypeOrder)typeOrder { return FSTTypeOrderArray; } - (NSComparisonResult)compare:(FSTFieldValue *)other { - if ([other isKindOfClass:[FSTArrayValue class]]) { + if (other.type == FieldValue::Type::Array) { NSArray *selfArray = self.internalValue; NSArray *otherArray = ((FSTArrayValue *)other).internalValue; NSUInteger minLength = MIN(selfArray.count, otherArray.count); diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h index c7137c12185..c91a1688b90 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.h +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -80,6 +80,14 @@ class FieldValue { // position instead, see the doc comment above. }; + /** + * Checks if the given type is a numeric, such as Type::Integer or + * Type::Double. + */ + static bool IsNumber(Type type) { + return type == Type::Integer || type == Type::Double; + } + FieldValue() { } diff --git a/Firestore/core/src/firebase/firestore/model/transform_operations.h b/Firestore/core/src/firebase/firestore/model/transform_operations.h index 8002907037f..1930bc72954 100644 --- a/Firestore/core/src/firebase/firestore/model/transform_operations.h +++ b/Firestore/core/src/firebase/firestore/model/transform_operations.h @@ -27,6 +27,7 @@ #import "Firestore/Source/Model/FSTFieldValue.h" #include "Firestore/core/include/firebase/firestore/timestamp.h" +#include "Firestore/core/src/firebase/firestore/model/field_value.h" namespace firebase { namespace firestore { @@ -259,18 +260,18 @@ class NumericIncrementTransform : public TransformOperation { FIRTimestamp* /* localWriteTime */) const override { // Return an integer value only if the previous value and the operand is an // integer. - if ([previousValue isKindOfClass:[FSTIntegerValue class]] && - [operand_ isKindOfClass:[FSTIntegerValue class]]) { + if (previousValue.type == FieldValue::Type::Integer && + operand_.type == FieldValue::Type::Integer) { int64_t sum = SafeIncrement( (static_cast(previousValue)).internalValue, (static_cast(operand_)).internalValue); return [FSTIntegerValue integerValue:sum]; - } else if ([previousValue isKindOfClass:[FSTIntegerValue class]]) { + } else if (previousValue.type == FieldValue::Type::Integer) { double sum = (static_cast(previousValue)).internalValue + OperandAsDouble(); return [FSTDoubleValue doubleValue:sum]; - } else if ([previousValue isKindOfClass:[FSTDoubleValue class]]) { + } else if (previousValue.type == FieldValue::Type::Double) { double sum = (static_cast(previousValue)).internalValue + OperandAsDouble(); return [FSTDoubleValue doubleValue:sum]; @@ -329,9 +330,9 @@ class NumericIncrementTransform : public TransformOperation { } double OperandAsDouble() const { - if ([operand_ isKindOfClass:[FSTDoubleValue class]]) { + if (operand_.type == FieldValue::Type::Double) { return (static_cast(operand_)).internalValue; - } else if ([operand_ isKindOfClass:[FSTIntegerValue class]]) { + } else if (operand_.type == FieldValue::Type::Integer) { return (static_cast(operand_)).internalValue; } else { HARD_FAIL("Expected 'operand' to be of FSTNumerValue type, but was %s", From 36d92cfb0f1b4e27e325828b8c71be311cd0e0ca Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Wed, 27 Mar 2019 13:45:07 -0400 Subject: [PATCH 109/214] GULAppDelegateSwizzlerTests fix (#2657) --- GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m | 3 +-- .../Example/GoogleUtilities.xcodeproj/project.pbxproj | 2 ++ .../xcshareddata/xcschemes/Example_iOS.xcscheme | 1 + .../Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m index bb71991b0e7..8bbabad3778 100644 --- a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m +++ b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m @@ -277,9 +277,8 @@ + (void)createSubclassWithObject:(id)anObject { // Create GUL__ NSString *classNameWithPrefix = [kGULAppDelegatePrefix stringByAppendingString:NSStringFromClass(realClass)]; - NSTimeInterval timestamp = [NSDate date].timeIntervalSince1970; NSString *newClassName = - [NSString stringWithFormat:@"%@-%0.0f", classNameWithPrefix, timestamp * 1000]; + [NSString stringWithFormat:@"%@-%@", classNameWithPrefix, [NSUUID UUID].UUIDString]; if (NSClassFromString(newClassName)) { GULLogError(kGULLoggerSwizzler, NO, diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj index 754333d6676..08778f3f85c 100644 --- a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; + 9A7C37C2224BD9C600033B0D /* GULAppDelegateSwizzlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EFBE67F92101401100E756A7 /* GULAppDelegateSwizzlerTest.m */; }; DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */; }; DE84BBC421D7EC900048A176 /* GULUserDefaultsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */; }; DE84BBC521D7EC900048A176 /* GULUserDefaultsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */; }; @@ -618,6 +619,7 @@ DEC977D920F68C3300014E20 /* GULNetworkTest.m in Sources */, EFBE67FA2101401100E756A7 /* GULSwizzlerTest.m in Sources */, EFBE67FD2101401100E756A7 /* GULObjectSwizzlerTest.m in Sources */, + 9A7C37C2224BD9C600033B0D /* GULAppDelegateSwizzlerTest.m in Sources */, EFBE67FE2101401100E756A7 /* GULRuntimeClassSnapshotTests.m in Sources */, EFBE67FC2101401100E756A7 /* GULRuntimeClassDiffTests.m in Sources */, DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */, diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_iOS.xcscheme b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_iOS.xcscheme index f4ef27750b9..31b6b579ff4 100644 --- a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_iOS.xcscheme +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_iOS.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + codeCoverageEnabled = "YES" shouldUseLaunchSchemeArgsEnv = "YES"> Date: Wed, 27 Mar 2019 13:51:18 -0700 Subject: [PATCH 110/214] Upgrade gRPC-C++ to 0.0.8, enabling experimental tvOS support (#2648) --- FirebaseFirestore.podspec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 644ec427ace..f1981baed3f 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -18,6 +18,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' + s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' s.static_framework = true @@ -51,13 +52,14 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.dependency 'FirebaseAuthInterop', '~> 1.0' s.dependency 'FirebaseCore', '~> 5.2' - s.dependency 'gRPC-C++', '0.0.6' + s.dependency 'gRPC-C++', '0.0.8' s.dependency 'leveldb-library', '~> 1.20' s.dependency 'Protobuf', '~> 3.1' s.dependency 'nanopb', '~> 0.3.901' s.ios.frameworks = 'MobileCoreServices', 'SystemConfiguration' s.osx.frameworks = 'SystemConfiguration' + s.tvos.frameworks = 'SystemConfiguration' s.library = 'c++' s.pod_target_xcconfig = { From f6747f7b07b3f85a5672c7cdf2c5556c2840c230 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 27 Mar 2019 15:15:02 -0700 Subject: [PATCH 111/214] Fix build warning (#2661) --- Example/DynamicLinks/Tests/FIRDynamicLinksTest.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m b/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m index 1eb5e227585..17df2e05fe2 100644 --- a/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m +++ b/Example/DynamicLinks/Tests/FIRDynamicLinksTest.m @@ -848,7 +848,7 @@ - (void)testPassMatchesShortLinkFormat { - (void)testFailMatchesShortLinkFormat { NSArray *urlStrings = @[ @"https://test.app.goo.gl", @"https://test.app.goo.gl?link=", @"https://test.app.goo.gl/", - @"https://sample.page.link?link=https://google.com/test&ibi=com.google.sample&ius=79306483" + @"https://sample.page.link?link=https://google.com/test&ibi=com.google.sample&ius=79306483", @"https://sample.page.link/?link=https://google.com/test&ibi=com.google.sample&ius=79306483" ]; From 13c1c80a48bd5bad7167f856a4fbf8bc8e8c87c3 Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 27 Mar 2019 16:05:55 -0700 Subject: [PATCH 112/214] Add support for running Firestore integration tests (#2660) * Add Firestore_IntegrationTests_macOS to Xcode * Change deployment target from 10.14 to 10.10 * Make standard target edits * Force C99/C++11 * Use shared test info.plist * Remove CODE_SIGN_IDENTITY * Add Firestore_IntegrationTests_macOS to the Podfile * Have sync_project handle macOS integration tests * Pod install and sync_project * Add header search paths and ldflags * Build integration tests on macos * Remove integration tests from unit tests * Make Firestore_IntegrationTests_macOS buildable from the command-line --- .../Firestore.xcodeproj/project.pbxproj | 329 +++++++++++++++++- .../Firestore_Example_macOS.xcscheme | 10 + .../Firestore_IntegrationTests_macOS.xcscheme | 99 ++++++ .../xcschemes/Firestore_Tests_macOS.xcscheme | 9 - Firestore/Example/Podfile | 9 + scripts/build.sh | 6 + scripts/sync_project.rb | 26 +- 7 files changed, 456 insertions(+), 32 deletions(-) create mode 100644 Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_macOS.xcscheme diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 56065961f82..5328c8231b2 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -36,6 +36,7 @@ 0B7B24194E2131F5C325FE0E /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; }; 0D67722B43147F775891EA43 /* FSTSerializerBetaTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */; }; 0DAA255C2FEB387895ADEE12 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; + 0F54634745BA07B09BDC14D7 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; 10B7426844685A48234C093B /* FSTArraySortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF07E1F3D0B6E003D0CDC /* FSTArraySortedDictionaryTests.m */; }; 10CA552415BE0954221A1626 /* FSTLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650220A0E93200A2D6A1 /* FSTLRUGarbageCollectorTests.mm */; }; 113190791F42202FDE1ABC14 /* FIRQuerySnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */; }; @@ -50,6 +51,8 @@ 16FE432587C1B40AF08613D2 /* type_traits_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */; }; 1733601ECCEA33E730DEAF45 /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; }; 18CF41A17EA3292329E1119D /* FIRGeoPointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */; }; + 198F193BD9484E49375A7BE7 /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; + 1C7254742A9F6F7042C9D78E /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; 1CAA9012B25F975D445D5978 /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; 1CC9BABDD52B2A1E37E2698D /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; 1E2AE064CF32A604DC7BFD4D /* to_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B696858D2214B53900271095 /* to_string_test.cc */; }; @@ -57,6 +60,7 @@ 1F998DDECB54A66222CC66AA /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; 21F821BF241244BA7BF070D9 /* FSTEventManagerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */; }; 229D1A9381F698D71F229471 /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; + 27E46C94AAB087C80A97FF7F /* FIRServerTimestampTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */; }; 29FF9029315C3A9FB0E0D79E /* FSTQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E061202154B900B64F25 /* FSTQueryTests.mm */; }; 2AAEABFD550255271E3BAC91 /* to_string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68B1E002213A764008977EF /* to_string_apple_test.mm */; }; 2E0BBA7E627EB240BA11B0D0 /* exponential_backoff_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D1B68420E2AB1A00B35856 /* exponential_backoff_test.cc */; }; @@ -68,6 +72,7 @@ 333FCB7BB0C9986B5DF28FC8 /* grpc_stream_tester.cc in Sources */ = {isa = PBXBuildFile; fileRef = B1A7E1959AF8141FA7E6B888 /* grpc_stream_tester.cc */; }; 34387C13A92D31B212BC0CA9 /* FSTMemoryLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650420A0E9BD00A2D6A1 /* FSTMemoryLRUGarbageCollectorTests.mm */; }; 355A9171EF3F7AD44A9C60CB /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; }; + 358DBA8B2560C65D9EB23C35 /* Pods_Firestore_IntegrationTests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39B832380209CC5BAF93BC52 /* Pods_Firestore_IntegrationTests_macOS.framework */; }; 36FD4CE79613D18BC783C55B /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; 37EC6C6EA9169BB99078CA96 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; 38430E0E07C54FCD399AE919 /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E046202154AA00B64F25 /* FIRQueryTests.mm */; }; @@ -78,12 +83,14 @@ 3D11B104A8F01F85180B38F6 /* field_transform_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352320A3AEC3003E0143 /* field_transform_test.mm */; }; 3DFBA7413965F3E6F366E923 /* grpc_unary_call_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964942163E63900EB9CFB /* grpc_unary_call_test.cc */; }; 3F2DF1DDDF7F5830F0669992 /* datastore_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 546854A820A36867004BDBD5 /* datastore_test.mm */; }; + 42063E6AE9ADF659AA6D4E18 /* FSTSmokeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */; }; 45939AFF906155EA27D281AB /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; 46999832F7D1709B4C29FAA8 /* FIRDocumentReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */; }; 470A37727BBF516B05ED276A /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; 49C04B97AB282FFA82FD98CD /* latlng.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */; }; 4A3FF3B16A39A5DC6B7EBA51 /* target.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */; }; 4A62B708A6532DD45414DA3A /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; + 4A64A339BCA77B9F875D1D8B /* FSTDatastoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */; }; 4AA4ABE36065DB79CD76DD8D /* Pods_Firestore_Benchmarks_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F694C3CE4B77B3C0FA4BBA53 /* Pods_Firestore_Benchmarks_iOS.framework */; }; 4D1F46B2DD91198C8867C04C /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; 4D98894EB5B3D778F5628456 /* grpc_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6BBE42F21262CF400C6A53E /* grpc_stream_test.cc */; }; @@ -220,7 +227,9 @@ 5CC9650720A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650620A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm */; }; 5D405BE298CE4692CB00790A /* Pods_Firestore_Tests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */; }; 5D45CC300ED037358EF33A8F /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; + 5E5B3B8B3A41C8EB70035A6B /* FSTTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */; }; 5EFBAD082CB0F86CD0711979 /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; + 5F05A801B1EA44BC1264E55A /* FIRTypeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E071202154D600B64F25 /* FIRTypeTests.mm */; }; 5F19F66D8B01BA2B97579017 /* tree_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */; }; 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; @@ -246,8 +255,10 @@ 61F72C5620BC48FD001A68CB /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; 62DA31B79FE97A90EEF28B0B /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; 63BB61B6366E7F80C348419D /* FSTLevelDBTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */; }; + 660E99DEDA0A6FC1CCB200F9 /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; 6672B445E006A7708B8531ED /* FSTImmutableSortedDictionary+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0801F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m */; }; 66FAB8EAC012A3822BD4D0C9 /* leveldb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */; }; + 6A4F6B42C628D55CCE0C311F /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E069202154D500B64F25 /* FIRQueryTests.mm */; }; 6B8806528FD3757D33D8B8AE /* FSTMemoryQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08B2021552B00B64F25 /* FSTMemoryQueryCacheTests.mm */; }; 6DCA8E54E652B78EFF3EEDAC /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; 6E59498D20F55BA800ECD9A5 /* FuzzingResources in Resources */ = {isa = PBXBuildFile; fileRef = 6ED6DEA120F5502700FC6076 /* FuzzingResources */; }; @@ -266,22 +277,29 @@ 71DF9A27169F25383C762F85 /* grpc_stream_tester.cc in Sources */ = {isa = PBXBuildFile; fileRef = B1A7E1959AF8141FA7E6B888 /* grpc_stream_tester.cc */; }; 72AD91671629697074F2545B /* ordered_code_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D03201BC6E400D97691 /* ordered_code_test.cc */; }; 731541612214AFFA0037F4DC /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; + 736C4E82689F1CA1859C4A3F /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; 73866AA12082B0A5009BB4FF /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; 73F1F73C2210F3D800E1F692 /* memory_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */; }; 73F1F73D2210F3D800E1F692 /* index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */; }; 73F1F7412211FEF300E1F692 /* leveldb_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */; }; 73FE5066020EF9B2892C86BF /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; 74985DE2C7EF4150D7A455FD /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; + 7DD67E9621C52B790E844B16 /* FIRDatabaseTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */; }; 7EF540911720DAAF516BEDF0 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; 83A9CD3B6E791A860CE81FA1 /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; + 8403D519C916C72B9C7F2FA1 /* FIRValidationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06D202154D600B64F25 /* FIRValidationTests.mm */; }; + 8460C97C9209D7DAF07090BD /* FIRFieldsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */; }; 84DBE646DCB49305879D3500 /* nanopb_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */; }; 862B1AC9EDAB309BBF4FB18C /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; + 8705C4856498F66E471A0997 /* FIRWriteBatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 8943A7C0750CEB0B98D21209 /* FSTPersistenceTestHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08D2021552B00B64F25 /* FSTPersistenceTestHelpers.mm */; }; 897F3C1936612ACB018CA1DD /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; + 8A6C809B9F81C30B7333FCAA /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; 8C39F6D4B3AA9074DF00CFB8 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; 8C82D4D3F9AB63E79CC52DC8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */; }; 8DA258092DD856D829D973B5 /* transform_operations_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352220A3AEC3003E0143 /* transform_operations_test.mm */; }; + 8F4F40E9BC7ED588F67734D5 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; 904DA0AE915C02154AE547FC /* FSTLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */; }; 927D22C6D294B82D1580C48D /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0922021552B00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm */; }; 938F2AF6EC5CD0B839300DB0 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; @@ -294,12 +312,15 @@ 9794E074439ABE5457E60F35 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; 9D0E720F5A6DBD48FF325016 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; 9D71628E38D9F64C965DF29E /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; + A17DBC8F24127DA8A381F865 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; A1F57CC739211F64F2E9232D /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; A38F4AE525A87FDEA41DED47 /* FSTLevelDBQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */; }; + A55266E6C986251D283CE948 /* FIRCursorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E070202154D600B64F25 /* FIRCursorTests.mm */; }; A61AE3D94C975A87EFA82ADA /* firebase_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */; }; A6543DD0A56F8523C6D518E1 /* FSTMutationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B72021555100B64F25 /* FSTMutationTests.mm */; }; A7470B7B2433264FFDCC7AC3 /* FSTLevelDBLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08F2021552B00B64F25 /* FSTLevelDBLocalStoreTests.mm */; }; A907244EE37BC32C8D82948E /* FSTSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */; }; + A94884460990CD48CC0AD070 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; AAA50E56B9A7EF3EFDA62172 /* create_noop_connectivity_monitor.cc in Sources */ = {isa = PBXBuildFile; fileRef = B67BF448216EB43000CA9097 /* create_noop_connectivity_monitor.cc */; }; AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; @@ -321,6 +342,7 @@ AC6C1E57B18730428CB15E03 /* executor_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */; }; ACC9369843F5ED3BD2284078 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; AD86162AC78673BA969F3467 /* FSTQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0892021552A00B64F25 /* FSTQueryCacheTests.mm */; }; + B03F286F3AEC3781C386C646 /* FIRNumericTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */; }; B192F30DECA8C28007F9B1D0 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; B49311BDE5EB6DF811E03C1B /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D9342023966E000A432D /* credentials_provider_test.cc */; }; B60894F72170207200EBC644 /* fake_credentials_provider.cc in Sources */ = {isa = PBXBuildFile; fileRef = B60894F62170207100EBC644 /* fake_credentials_provider.cc */; }; @@ -396,6 +418,7 @@ DEF036EA1ECEECD8E3ECC362 /* FSTImmutableSortedSet+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0821F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m */; }; E084921EFB7CF8CB1E950D6C /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; E11DDA3DD75705F26245E295 /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; }; + E2B15548A3B6796CE5A01975 /* FIRListenerRegistrationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06B202154D500B64F25 /* FIRListenerRegistrationTests.mm */; }; E3C0E5F834A82EEE9F8C4519 /* watch_change_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68FC0E421F6848700A7055C /* watch_change_test.mm */; }; E4C0CC7FB88D8F6CB1B972C6 /* leveldb_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */; }; E6821243C510797EFFC7BCE2 /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; @@ -424,6 +447,13 @@ remoteGlobalIDString = DAFF0CF421E64AC30062958F; remoteInfo = Firestore_Example_macOS; }; + 54B8E4AF224BDC4100930F18 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = DAFF0CF421E64AC30062958F; + remoteInfo = Firestore_Example_macOS; + }; 54C9EDF62040E16300A969CD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6003F582195388D10070C39A /* Project object */; @@ -492,10 +522,12 @@ 132E3BB3D5C42282B4ACFB20 /* FSTLevelDBBenchmarkTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBBenchmarkTests.mm; sourceTree = ""; }; 2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = type_traits_apple_test.mm; sourceTree = ""; }; 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2F901F31BC62444A476B779F /* Pods-Firestore_IntegrationTests_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_macOS/Pods-Firestore_IntegrationTests_macOS.debug.xcconfig"; sourceTree = ""; }; 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_util_test.cc; sourceTree = ""; }; 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = nanopb_string_test.cc; path = nanopb/nanopb_string_test.cc; sourceTree = ""; }; 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = strerror_test.cc; sourceTree = ""; }; 397FB002E298B780F1E223E2 /* Pods-Firestore_Tests_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS.release.xcconfig"; sourceTree = ""; }; + 39B832380209CC5BAF93BC52 /* Pods_Firestore_IntegrationTests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = ""; }; 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = ""; }; 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.release.xcconfig"; sourceTree = ""; }; @@ -604,6 +636,7 @@ 54A0352C20A3B3D7003E0143 /* status_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = status_test.cc; sourceTree = ""; }; 54A0352D20A3B3D7003E0143 /* statusor_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = statusor_test.cc; sourceTree = ""; }; 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iterator_adaptors_test.cc; sourceTree = ""; }; + 54B8E4AA224BDC4100930F18 /* Firestore_IntegrationTests_macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_IntegrationTests_macOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54C2294E1FECABAE007D065B /* log_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log_test.cc; sourceTree = ""; }; 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_SwiftTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54C9EDF52040E16300A969CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -735,6 +768,7 @@ B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = executor_libdispatch_test.mm; sourceTree = ""; }; B6FB468A208F9B9100554BA2 /* executor_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = executor_test.h; sourceTree = ""; }; B79CA87A1A01FC5329031C9B /* Pods_Firestore_FuzzTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_FuzzTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B953604968FBF5483BD20F5A /* Pods-Firestore_IntegrationTests_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_macOS/Pods-Firestore_IntegrationTests_macOS.release.xcconfig"; sourceTree = ""; }; B9C261C26C5D311E1E3C0CB9 /* query_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = query_test.cc; sourceTree = ""; }; BA6E5B9D53CCF301F58A62D7 /* xcgmock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = xcgmock.h; sourceTree = ""; }; BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -789,6 +823,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 54B8E4A7224BDC4100930F18 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 358DBA8B2560C65D9EB23C35 /* Pods_Firestore_IntegrationTests_macOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54C9EDEE2040E16300A969CD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1080,6 +1122,7 @@ DAFF0CF521E64AC30062958F /* Firestore_Example_macOS.app */, 6EDD3B5B20BF247500C33877 /* Firestore_FuzzTests_iOS.xctest */, DE03B2E91F2149D600A30B9C /* Firestore_IntegrationTests_iOS.xctest */, + 54B8E4AA224BDC4100930F18 /* Firestore_IntegrationTests_macOS.xctest */, 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */, 6003F5AE195388D20070C39A /* Firestore_Tests_iOS.xctest */, 544AB1922248072200F851E6 /* Firestore_Tests_macOS.xctest */, @@ -1098,10 +1141,11 @@ E42355285B9EF55ABD785792 /* Pods_Firestore_Example_macOS.framework */, B79CA87A1A01FC5329031C9B /* Pods_Firestore_FuzzTests_iOS.framework */, ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */, + 39B832380209CC5BAF93BC52 /* Pods_Firestore_IntegrationTests_macOS.framework */, 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */, + 759E964B6A03E6775C992710 /* Pods_Firestore_Tests_macOS.framework */, 6003F591195388D20070C39A /* UIKit.framework */, 6003F5AF195388D20070C39A /* XCTest.framework */, - 759E964B6A03E6775C992710 /* Pods_Firestore_Tests_macOS.framework */, ); name = Frameworks; sourceTree = ""; @@ -1275,6 +1319,8 @@ 97C492D2524E92927C11F425 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */, 1277F98C20D2DF0867496976 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */, F354C0FE92645B56A6C6FD44 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */, + 2F901F31BC62444A476B779F /* Pods-Firestore_IntegrationTests_macOS.debug.xcconfig */, + B953604968FBF5483BD20F5A /* Pods-Firestore_IntegrationTests_macOS.release.xcconfig */, E592181BFD7C53C305123739 /* Pods-Firestore_Tests_iOS.debug.xcconfig */, B3F5B3AAE791A5911B9EAA82 /* Pods-Firestore_Tests_iOS.release.xcconfig */, BD01F0E43E4E2A07B8B05099 /* Pods-Firestore_Tests_macOS.debug.xcconfig */, @@ -1555,6 +1601,26 @@ productReference = 544AB1922248072200F851E6 /* Firestore_Tests_macOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 54B8E4A9224BDC4100930F18 /* Firestore_IntegrationTests_macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 54B8E4B3224BDC4100930F18 /* Build configuration list for PBXNativeTarget "Firestore_IntegrationTests_macOS" */; + buildPhases = ( + 54D4C01B433CAC3C4EEDB1F9 /* [CP] Check Pods Manifest.lock */, + 54B8E4A6224BDC4100930F18 /* Sources */, + 54B8E4A7224BDC4100930F18 /* Frameworks */, + 54B8E4A8224BDC4100930F18 /* Resources */, + C164AD918C826AF88B418DA5 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 54B8E4B0224BDC4100930F18 /* PBXTargetDependency */, + ); + name = Firestore_IntegrationTests_macOS; + productName = Firestore_IntegrationTests_macOS; + productReference = 54B8E4AA224BDC4100930F18 /* Firestore_IntegrationTests_macOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 54C9EDF02040E16300A969CD /* Firestore_SwiftTests_iOS */ = { isa = PBXNativeTarget; buildConfigurationList = 54C9EDFA2040E16300A969CD /* Build configuration list for PBXNativeTarget "Firestore_SwiftTests_iOS" */; @@ -1710,6 +1776,11 @@ ProvisioningStyle = Automatic; TestTargetID = DAFF0CF421E64AC30062958F; }; + 54B8E4A9224BDC4100930F18 = { + CreatedOnToolsVersion = 10.1; + ProvisioningStyle = Automatic; + TestTargetID = DAFF0CF421E64AC30062958F; + }; 54C9EDF02040E16300A969CD = { CreatedOnToolsVersion = 9.2; ProvisioningStyle = Automatic; @@ -1766,6 +1837,7 @@ 5CAE131820FFFED600BE9A4A /* Firestore_Benchmarks_iOS */, DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */, 544AB1912248072200F851E6 /* Firestore_Tests_macOS */, + 54B8E4A9224BDC4100930F18 /* Firestore_IntegrationTests_macOS */, ); }; /* End PBXProject section */ @@ -1775,19 +1847,26 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 546877D82248206A005E3DE0 /* limit_spec_test.json in Resources */, - 546877DC2248206A005E3DE0 /* perf_spec_test.json in Resources */, 546877D52248206A005E3DE0 /* collection_spec_test.json in Resources */, - 546877DF2248206A005E3DE0 /* remote_store_spec_test.json in Resources */, + 546877D62248206A005E3DE0 /* existence_filter_spec_test.json in Resources */, + 546877D72248206A005E3DE0 /* limbo_spec_test.json in Resources */, + 546877D82248206A005E3DE0 /* limit_spec_test.json in Resources */, + 546877D92248206A005E3DE0 /* listen_spec_test.json in Resources */, 546877DA2248206A005E3DE0 /* offline_spec_test.json in Resources */, - 546877DD2248206A005E3DE0 /* persistence_spec_test.json in Resources */, 546877DB2248206A005E3DE0 /* orderby_spec_test.json in Resources */, - 546877E12248206A005E3DE0 /* write_spec_test.json in Resources */, - 546877D62248206A005E3DE0 /* existence_filter_spec_test.json in Resources */, + 546877DC2248206A005E3DE0 /* perf_spec_test.json in Resources */, + 546877DD2248206A005E3DE0 /* persistence_spec_test.json in Resources */, 546877DE2248206A005E3DE0 /* query_spec_test.json in Resources */, + 546877DF2248206A005E3DE0 /* remote_store_spec_test.json in Resources */, 546877E02248206A005E3DE0 /* resume_token_spec_test.json in Resources */, - 546877D92248206A005E3DE0 /* listen_spec_test.json in Resources */, - 546877D72248206A005E3DE0 /* limbo_spec_test.json in Resources */, + 546877E12248206A005E3DE0 /* write_spec_test.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 54B8E4A8224BDC4100930F18 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1964,6 +2043,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 54D4C01B433CAC3C4EEDB1F9 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Firestore_IntegrationTests_macOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 6A86E48DF663B6AA1CB5BA83 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2191,6 +2292,32 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + C164AD918C826AF88B418DA5 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Firestore_IntegrationTests_macOS/Pods-Firestore_IntegrationTests_macOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/leveldb-library-macOS/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/GoogleTest-macOS/GoogleTest.framework", + "${BUILT_PRODUCTS_DIR}/OCMock-macOS/OCMock.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_IntegrationTests_macOS/Pods-Firestore_IntegrationTests_macOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; D2D94DFA64939EF6DECDF908 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2401,6 +2528,35 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 54B8E4A6224BDC4100930F18 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 660E99DEDA0A6FC1CCB200F9 /* FIRArrayTransformTests.mm in Sources */, + A55266E6C986251D283CE948 /* FIRCursorTests.mm in Sources */, + 7DD67E9621C52B790E844B16 /* FIRDatabaseTests.mm in Sources */, + 8460C97C9209D7DAF07090BD /* FIRFieldsTests.mm in Sources */, + 8A6C809B9F81C30B7333FCAA /* FIRFirestoreSourceTests.mm in Sources */, + E2B15548A3B6796CE5A01975 /* FIRListenerRegistrationTests.mm in Sources */, + B03F286F3AEC3781C386C646 /* FIRNumericTransformTests.mm in Sources */, + 6A4F6B42C628D55CCE0C311F /* FIRQueryTests.mm in Sources */, + 27E46C94AAB087C80A97FF7F /* FIRServerTimestampTests.mm in Sources */, + 5F05A801B1EA44BC1264E55A /* FIRTypeTests.mm in Sources */, + 8403D519C916C72B9C7F2FA1 /* FIRValidationTests.mm in Sources */, + 8705C4856498F66E471A0997 /* FIRWriteBatchTests.mm in Sources */, + 4A64A339BCA77B9F875D1D8B /* FSTDatastoreTests.mm in Sources */, + 1C7254742A9F6F7042C9D78E /* FSTEventAccumulator.mm in Sources */, + 198F193BD9484E49375A7BE7 /* FSTHelpers.mm in Sources */, + 0F54634745BA07B09BDC14D7 /* FSTIntegrationTestCase.mm in Sources */, + 42063E6AE9ADF659AA6D4E18 /* FSTSmokeTests.mm in Sources */, + 5E5B3B8B3A41C8EB70035A6B /* FSTTransactionTests.mm in Sources */, + 736C4E82689F1CA1859C4A3F /* XCTestCase+Await.mm in Sources */, + 8F4F40E9BC7ED588F67734D5 /* app_testing.mm in Sources */, + A17DBC8F24127DA8A381F865 /* testutil.cc in Sources */, + A94884460990CD48CC0AD070 /* xcgmock_test.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54C9EDED2040E16300A969CD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2641,6 +2797,11 @@ target = DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */; targetProxy = 544AB1972248072200F851E6 /* PBXContainerItemProxy */; }; + 54B8E4B0224BDC4100930F18 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */; + targetProxy = 54B8E4AF224BDC4100930F18 /* PBXContainerItemProxy */; + }; 54C9EDF72040E16300A969CD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 6003F589195388D20070C39A /* Firestore_Example_iOS */; @@ -2747,7 +2908,7 @@ ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -2822,7 +2983,7 @@ ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -2869,6 +3030,143 @@ }; name = Release; }; + 54B8E4B1224BDC4100930F18 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2F901F31BC62444A476B779F /* Pods-Firestore_IntegrationTests_macOS.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"z\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"GTMSessionFetcher\"", + "-framework", + "\"GoogleTest\"", + "-framework", + "\"GoogleUtilities\"", + "-framework", + "\"OCMock\"", + "-framework", + "\"Protobuf\"", + "-framework", + "\"Security\"", + "-framework", + "\"SystemConfiguration\"", + "-framework", + "\"grpc\"", + "-framework", + "\"grpcpp\"", + "-framework", + "\"leveldb\"", + "-framework", + "\"nanopb\"", + "-framework", + "\"openssl_grpc\"", + "-framework", + FirebaseFirestore, + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-IntegrationTests-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_macOS.app/Contents/MacOS/Firestore_Example_macOS"; + }; + name = Debug; + }; + 54B8E4B2224BDC4100930F18 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B953604968FBF5483BD20F5A /* Pods-Firestore_IntegrationTests_macOS.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"z\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"GTMSessionFetcher\"", + "-framework", + "\"GoogleTest\"", + "-framework", + "\"GoogleUtilities\"", + "-framework", + "\"OCMock\"", + "-framework", + "\"Protobuf\"", + "-framework", + "\"Security\"", + "-framework", + "\"SystemConfiguration\"", + "-framework", + "\"grpc\"", + "-framework", + "\"grpcpp\"", + "-framework", + "\"leveldb\"", + "-framework", + "\"nanopb\"", + "-framework", + "\"openssl_grpc\"", + "-framework", + FirebaseFirestore, + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-IntegrationTests-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_macOS.app/Contents/MacOS/Firestore_Example_macOS"; + }; + name = Release; + }; 54C9EDF82040E16300A969CD /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */; @@ -3718,6 +4016,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 54B8E4B3224BDC4100930F18 /* Build configuration list for PBXNativeTarget "Firestore_IntegrationTests_macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 54B8E4B1224BDC4100930F18 /* Debug */, + 54B8E4B2224BDC4100930F18 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 54C9EDFA2040E16300A969CD /* Build configuration list for PBXNativeTarget "Firestore_SwiftTests_iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_macOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_macOS.xcscheme index 494fcb36418..5387d731d19 100644 --- a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_macOS.xcscheme +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_macOS.xcscheme @@ -38,6 +38,16 @@ ReferencedContainer = "container:Firestore.xcodeproj"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_macOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_macOS.xcscheme index 72bccff25c7..fe6da1f5211 100644 --- a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_macOS.xcscheme +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_macOS.xcscheme @@ -68,15 +68,6 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> - - - - diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index 01be81c33e2..f6a632ea258 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -76,4 +76,13 @@ target 'Firestore_Example_macOS' do pod 'OCMock' pod 'leveldb-library' end + + target 'Firestore_IntegrationTests_macOS' do + inherit! :search_paths + + pod 'GoogleTest', :podspec => 'GoogleTest.podspec' + + pod 'OCMock' + pod 'leveldb-library' + end end diff --git a/scripts/build.sh b/scripts/build.sh index 5dfa97b0b89..ddba97209df 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -314,6 +314,12 @@ case "$product-$method-$platform" in "${xcb_flags[@]}" \ build \ test + + RunXcodebuild \ + -workspace 'Firestore/Example/Firestore.xcworkspace' \ + -scheme "Firestore_IntegrationTests_$platform" \ + "${xcb_flags[@]}" \ + build ;; Firestore-cmake-macOS) diff --git a/scripts/sync_project.rb b/scripts/sync_project.rb index 96c842dc372..8d4aab89f28 100755 --- a/scripts/sync_project.rb +++ b/scripts/sync_project.rb @@ -62,8 +62,8 @@ def sync_firestore() 'SwiftTests', ] - ['Firestore_Tests_iOS', 'Firestore_Tests_macOS'].each do |target_name| - s.target target_name do |t| + ['iOS', 'macOS'].each do |platform| + s.target "Firestore_Tests_#{platform}" do |t| t.source_files = [ 'Firestore/Example/Tests/**', 'Firestore/core/test/**', @@ -80,16 +80,18 @@ def sync_firestore() end end - s.target 'Firestore_IntegrationTests_iOS' do |t| - t.source_files = [ - 'Firestore/Example/Tests/Integration/**', - 'Firestore/Example/Tests/Util/FSTEventAccumulator.mm', - 'Firestore/Example/Tests/Util/FSTHelpers.mm', - 'Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm', - 'Firestore/Example/Tests/Util/XCTestCase+Await.mm', - 'Firestore/Example/Tests/en.lproj/InfoPlist.strings', - 'Firestore/core/test/firebase/firestore/testutil/**', - ] + ['iOS', 'macOS'].each do |platform| + s.target "Firestore_IntegrationTests_#{platform}" do |t| + t.source_files = [ + 'Firestore/Example/Tests/Integration/**', + 'Firestore/Example/Tests/Util/FSTEventAccumulator.mm', + 'Firestore/Example/Tests/Util/FSTHelpers.mm', + 'Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm', + 'Firestore/Example/Tests/Util/XCTestCase+Await.mm', + 'Firestore/Example/Tests/en.lproj/InfoPlist.strings', + 'Firestore/core/test/firebase/firestore/testutil/**', + ] + end end s.sync() From 0729c9dfe9889bcfb0dfcd0cc9ae1c1f3190424c Mon Sep 17 00:00:00 2001 From: Gil Date: Thu, 28 Mar 2019 11:43:43 -0700 Subject: [PATCH 113/214] Add tvOS support to Firestore (#2662) * Create tvOS host app * Standard project edits * Remove code signing * Force C99/C++11 * Adjust plist file for new location * header search paths * LDFLAGS * Add Firestore_{Tests,IntegrationTests}_tvOS * Add tvOS tests to the Podfile * Add Firestore tvOS xcodebuild to build.sh * Teach sync_project to keep tvOS up-to-date * Add Firestore tvOS to Travis --- .travis.yml | 8 + Firestore/CHANGELOG.md | 1 + Firestore/Example/App/tvOS/AppDelegate.h | 23 + Firestore/Example/App/tvOS/AppDelegate.m | 61 + .../Content.imageset/Contents.json | 11 + .../Back.imagestacklayer/Contents.json | 6 + .../Contents.json | 17 + .../Content.imageset/Contents.json | 11 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 11 + .../Middle.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 16 + .../Back.imagestacklayer/Contents.json | 6 + .../App Icon.imagestack/Contents.json | 17 + .../Content.imageset/Contents.json | 16 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 16 + .../Middle.imagestacklayer/Contents.json | 6 + .../Contents.json | 32 + .../Contents.json | 24 + .../Top Shelf Image.imageset/Contents.json | 24 + .../App/tvOS/Assets.xcassets/Contents.json | 6 + .../Launch Image.launchimage/Contents.json | 22 + .../App/tvOS/Base.lproj/Main.storyboard | 28 + Firestore/Example/App/tvOS/Info.plist | 32 + Firestore/Example/App/tvOS/ViewController.h | 21 + Firestore/Example/App/tvOS/ViewController.m | 30 + Firestore/Example/App/tvOS/main.m | 24 + .../Firestore.xcodeproj/project.pbxproj | 1159 ++++++++++++++++- .../xcschemes/Firestore_Example_tvOS.xcscheme | 111 ++ .../Firestore_IntegrationTests_tvOS.xcscheme | 99 ++ .../xcschemes/Firestore_Tests_tvOS.xcscheme | 99 ++ Firestore/Example/GoogleBenchmark.podspec | 1 + Firestore/Example/GoogleTest.podspec | 1 + Firestore/Example/LibFuzzer.podspec | 1 + Firestore/Example/Podfile | 27 + Firestore/Example/ProtobufCpp.podspec | 1 + README.md | 4 +- scripts/build.sh | 2 +- scripts/sync_project.rb | 4 +- 40 files changed, 1972 insertions(+), 24 deletions(-) create mode 100644 Firestore/Example/App/tvOS/AppDelegate.h create mode 100644 Firestore/Example/App/tvOS/AppDelegate.m create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/Contents.json create mode 100644 Firestore/Example/App/tvOS/Assets.xcassets/Launch Image.launchimage/Contents.json create mode 100644 Firestore/Example/App/tvOS/Base.lproj/Main.storyboard create mode 100644 Firestore/Example/App/tvOS/Info.plist create mode 100644 Firestore/Example/App/tvOS/ViewController.h create mode 100644 Firestore/Example/App/tvOS/ViewController.m create mode 100644 Firestore/Example/App/tvOS/main.m create mode 100644 Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_tvOS.xcscheme create mode 100644 Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_tvOS.xcscheme create mode 100644 Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_tvOS.xcscheme diff --git a/.travis.yml b/.travis.yml index fe33f8b9d4e..366acb2491d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -163,6 +163,14 @@ jobs: script: - travis_retry ./scripts/if_changed.sh ./scripts/build.sh $PROJECT $PLATFORM + - stage: test + env: + - PROJECT=Firestore PLATFORM=tvOS METHOD=xcodebuild + before_install: + - ./scripts/if_changed.sh ./scripts/install_prereqs.sh + script: + - travis_retry ./scripts/if_changed.sh ./scripts/build.sh $PROJECT $PLATFORM + # Firestore sanitizers - stage: test diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 5d02091e1e4..7a00e406a95 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,4 +1,5 @@ # Unreleased +- [feature] Added community support for tvOS. # 1.2.0 - [feature] Added community support for macOS (#434). diff --git a/Firestore/Example/App/tvOS/AppDelegate.h b/Firestore/Example/App/tvOS/AppDelegate.h new file mode 100644 index 00000000000..b0b8e06e84c --- /dev/null +++ b/Firestore/Example/App/tvOS/AppDelegate.h @@ -0,0 +1,23 @@ +/* + * Copyright 2019 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 + +@interface AppDelegate : UIResponder + +@property(strong, nonatomic) UIWindow *window; + +@end diff --git a/Firestore/Example/App/tvOS/AppDelegate.m b/Firestore/Example/App/tvOS/AppDelegate.m new file mode 100644 index 00000000000..4481af28ddb --- /dev/null +++ b/Firestore/Example/App/tvOS/AppDelegate.m @@ -0,0 +1,61 @@ +/* + * Copyright 2019 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 "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + 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 active 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/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000000..48ecb4fa43e --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json new file mode 100644 index 00000000000..d29f024ed5c --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000000..48ecb4fa43e --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000000..48ecb4fa43e --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000000..16a370df014 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json new file mode 100644 index 00000000000..d29f024ed5c --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000000..16a370df014 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000000..16a370df014 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json new file mode 100644 index 00000000000..db288f368f1 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,32 @@ +{ + "assets" : [ + { + "size" : "1280x768", + "idiom" : "tv", + "filename" : "App Icon - App Store.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "400x240", + "idiom" : "tv", + "filename" : "App Icon.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "2320x720", + "idiom" : "tv", + "filename" : "Top Shelf Image Wide.imageset", + "role" : "top-shelf-image-wide" + }, + { + "size" : "1920x720", + "idiom" : "tv", + "filename" : "Top Shelf Image.imageset", + "role" : "top-shelf-image" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json new file mode 100644 index 00000000000..7dc95020229 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + }, + { + "idiom" : "tv-marketing", + "scale" : "1x" + }, + { + "idiom" : "tv-marketing", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 00000000000..7dc95020229 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + }, + { + "idiom" : "tv-marketing", + "scale" : "1x" + }, + { + "idiom" : "tv-marketing", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Assets.xcassets/Launch Image.launchimage/Contents.json b/Firestore/Example/App/tvOS/Assets.xcassets/Launch Image.launchimage/Contents.json new file mode 100644 index 00000000000..d746a609003 --- /dev/null +++ b/Firestore/Example/App/tvOS/Assets.xcassets/Launch Image.launchimage/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "orientation" : "landscape", + "idiom" : "tv", + "extent" : "full-screen", + "minimum-system-version" : "11.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "tv", + "extent" : "full-screen", + "minimum-system-version" : "9.0", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Firestore/Example/App/tvOS/Base.lproj/Main.storyboard b/Firestore/Example/App/tvOS/Base.lproj/Main.storyboard new file mode 100644 index 00000000000..a5c40f19689 --- /dev/null +++ b/Firestore/Example/App/tvOS/Base.lproj/Main.storyboard @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Firestore/Example/App/tvOS/Info.plist b/Firestore/Example/App/tvOS/Info.plist new file mode 100644 index 00000000000..02942a34f3e --- /dev/null +++ b/Firestore/Example/App/tvOS/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UIUserInterfaceStyle + Automatic + + diff --git a/Firestore/Example/App/tvOS/ViewController.h b/Firestore/Example/App/tvOS/ViewController.h new file mode 100644 index 00000000000..7f02e1a5218 --- /dev/null +++ b/Firestore/Example/App/tvOS/ViewController.h @@ -0,0 +1,21 @@ +/* + * Copyright 2019 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 + +@interface ViewController : UIViewController + +@end diff --git a/Firestore/Example/App/tvOS/ViewController.m b/Firestore/Example/App/tvOS/ViewController.m new file mode 100644 index 00000000000..5421c791313 --- /dev/null +++ b/Firestore/Example/App/tvOS/ViewController.m @@ -0,0 +1,30 @@ +/* + * Copyright 2019 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 "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +@end diff --git a/Firestore/Example/App/tvOS/main.m b/Firestore/Example/App/tvOS/main.m new file mode 100644 index 00000000000..efb3cc9e634 --- /dev/null +++ b/Firestore/Example/App/tvOS/main.m @@ -0,0 +1,24 @@ +/* + * Copyright 2019 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 +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 5328c8231b2..ca232d6e0c4 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -23,67 +23,126 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 0087625FD31D76E1365C589E /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; + 00B7AFE2A7C158DD685EB5EE /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; }; + 01C0A2CF788A93EF2CEB6100 /* memory_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */; }; 020AFD89BB40E5175838BB76 /* local_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F8043813A5D16963EC02B182 /* local_serializer_test.cc */; }; + 023829DB2198383927233318 /* FSTLocalSerializerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08A2021552A00B64F25 /* FSTLocalSerializerTests.mm */; }; + 0265CCC8BBB76AE013F52411 /* FSTViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05E202154B900B64F25 /* FSTViewTests.mm */; }; + 02C953A7B0FA5EF87DB0361A /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; 036F975093414351FE952F08 /* index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */; }; 0455FC6E2A281BD755FD933A /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; 051D3E20184AF195266EF678 /* no_document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908720322E8800CC290A /* no_document_test.cc */; }; 0535C1B65DADAE1CE47FA3CA /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; 056542AD1D0F78E29E22EFA9 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; }; 072D805A94E767DE4D371881 /* FSTSyncEngineTestDriver.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */; }; + 07A64E6C4EB700E3AF3FD496 /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; }; 07B1E8C62772758BC82FEBEE /* field_mask_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5320A36E1F00BCEB75 /* field_mask_test.cc */; }; + 086E10B1B37666FB746D56BC /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; 08D853C9D3A4DC919C55671A /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; }; + 08F44F7DF9A3EF0D35C8FB57 /* FIRNumericTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */; }; + 0963F6D7B0F9AE1E24B82866 /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; 0A6FBE65A7FE048BAD562A15 /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; 0B7B24194E2131F5C325FE0E /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; }; + 0D2D25522A94AA8195907870 /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; }; 0D67722B43147F775891EA43 /* FSTSerializerBetaTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */; }; 0DAA255C2FEB387895ADEE12 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; + 0E4C94369FFF7EC0C9229752 /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; 0F54634745BA07B09BDC14D7 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; + 0FA4D5601BE9F0CB5EC2882C /* local_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F8043813A5D16963EC02B182 /* local_serializer_test.cc */; }; + 0FBDD5991E8F6CD5F8542474 /* latlng.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */; }; 10B7426844685A48234C093B /* FSTArraySortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF07E1F3D0B6E003D0CDC /* FSTArraySortedDictionaryTests.m */; }; 10CA552415BE0954221A1626 /* FSTLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650220A0E93200A2D6A1 /* FSTLRUGarbageCollectorTests.mm */; }; + 1115DB1F1DCE93B63E03BA8C /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; }; 113190791F42202FDE1ABC14 /* FIRQuerySnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */; }; 11F8EE69182C9699E90A9E3D /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; }; 12158DFCEE09D24B7988A340 /* maybe_document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */; }; + 1291D9F5300AFACD1FBD262D /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; + 12BB9ED1CA98AA52B92F497B /* log_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54C2294E1FECABAE007D065B /* log_test.cc */; }; + 12DB753599571E24DCED0C2C /* FIRValidationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06D202154D600B64F25 /* FIRValidationTests.mm */; }; 132E3483789344640A52F223 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; 132E3E53179DE287D875F3F2 /* FSTLevelDBTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */; }; 132E3EE56C143B2C9ACB6187 /* FSTLevelDBBenchmarkTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E3BB3D5C42282B4ACFB20 /* FSTLevelDBBenchmarkTests.mm */; }; 135429EEF1D7FA9D1E329392 /* fake_credentials_provider.cc in Sources */ = {isa = PBXBuildFile; fileRef = B60894F62170207100EBC644 /* fake_credentials_provider.cc */; }; + 1357806B4CD3A62A8F5DE86D /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; + 13D8F4196528BAB19DBB18A7 /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; + 152543FD706D5E8851C8DA92 /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; + 153F3E4E9E3A0174E29550B4 /* mutation.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8220B89AAC00B5BCE7 /* mutation.pb.cc */; }; 156B042479C42DB2C3190C63 /* FSTTreeSortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0841F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m */; }; + 169D01E6FF2CDF994B32B491 /* create_noop_connectivity_monitor.cc in Sources */ = {isa = PBXBuildFile; fileRef = B67BF448216EB43000CA9097 /* create_noop_connectivity_monitor.cc */; }; 16F52ECC6FA8A0587CD779EB /* user_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93220239654000A432D /* user_test.cc */; }; 16FE432587C1B40AF08613D2 /* type_traits_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */; }; 1733601ECCEA33E730DEAF45 /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; }; + 17638F813B9B556FE7718C0C /* FIRQuerySnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */; }; + 178FE1E277C63B3E7120BE56 /* watch_change_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68FC0E421F6848700A7055C /* watch_change_test.mm */; }; + 18638EAED9E126FC5D895B14 /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; 18CF41A17EA3292329E1119D /* FIRGeoPointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */; }; 198F193BD9484E49375A7BE7 /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; + 1C19D796DB6715368407387A /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; 1C7254742A9F6F7042C9D78E /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; 1CAA9012B25F975D445D5978 /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; 1CC9BABDD52B2A1E37E2698D /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; + 1DFAEEE901B4E517A4CB9CAD /* FSTMemoryMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0972021552C00B64F25 /* FSTMemoryMutationQueueTests.mm */; }; 1E2AE064CF32A604DC7BFD4D /* to_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B696858D2214B53900271095 /* to_string_test.cc */; }; 1E52635E55FD6FAB78FD29D8 /* FSTLevelDBMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0872021552A00B64F25 /* FSTLevelDBMutationQueueTests.mm */; }; + 1EF47EF3D03B0847007C2318 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; 1F998DDECB54A66222CC66AA /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; + 20A26E9D0336F7F32A098D05 /* Pods_Firestore_IntegrationTests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2220F583583EFC28DE792ABE /* Pods_Firestore_IntegrationTests_tvOS.framework */; }; + 20EC3A46525B8C3471D7D179 /* index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */; }; + 215643858470A449D3A3E168 /* stream_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B66D8995213609EE0086DA0C /* stream_test.mm */; }; 21F821BF241244BA7BF070D9 /* FSTEventManagerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */; }; + 227CFA0B2A01884C277E4F1D /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; }; 229D1A9381F698D71F229471 /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; + 239B9B357E67036BEA831E3A /* FSTMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0962021552C00B64F25 /* FSTMutationQueueTests.mm */; }; + 25A75DFA730BAD21A5538EC5 /* document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D821C2DDC800EFB9CC /* document.pb.cc */; }; + 25CD471A28606A0DEE9F454A /* FSTMutationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B72021555100B64F25 /* FSTMutationTests.mm */; }; + 25FE27330996A59F31713A0C /* FIRDocumentReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */; }; + 26CB3D7C871BC56456C6021E /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; 27E46C94AAB087C80A97FF7F /* FIRServerTimestampTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */; }; + 28E4B4A53A739AE2C9CF4159 /* FIRDocumentSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */; }; + 29FDE0C0BA643E3804D8546C /* FSTMemoryLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650420A0E9BD00A2D6A1 /* FSTMemoryLRUGarbageCollectorTests.mm */; }; 29FF9029315C3A9FB0E0D79E /* FSTQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E061202154B900B64F25 /* FSTQueryTests.mm */; }; 2AAEABFD550255271E3BAC91 /* to_string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68B1E002213A764008977EF /* to_string_apple_test.mm */; }; + 2B1E95FAFD350C191B525F3B /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93620239689000A432D /* empty_credentials_provider_test.cc */; }; 2E0BBA7E627EB240BA11B0D0 /* exponential_backoff_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D1B68420E2AB1A00B35856 /* exponential_backoff_test.cc */; }; + 2E169CF1E9E499F054BB873A /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; 2E6E6164F44B9E3C6BB88313 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; 2EAD77559EC654E6CA4D3E21 /* FIRSnapshotMetadataTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */; }; + 2F6E23D7888FC82475C63010 /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */; }; + 300D9D215F4128E69068B863 /* FSTQueryListenerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05D202154B900B64F25 /* FSTQueryListenerTests.mm */; }; 3021937CBABFD9270A051900 /* FSTViewSnapshotTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05C202154B800B64F25 /* FSTViewSnapshotTest.mm */; }; + 31A396C81A107D1DEFDF4A34 /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; + 31BDB4CB0E7458C650A77ED0 /* FIRFirestoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */; }; + 31D8E3D925FA3F70AA20ACCE /* FSTMockDatastore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */; }; 32A95242C56A1A230231DB6A /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; 32F022CB75AEE48CDDAF2982 /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; 333FCB7BB0C9986B5DF28FC8 /* grpc_stream_tester.cc in Sources */ = {isa = PBXBuildFile; fileRef = B1A7E1959AF8141FA7E6B888 /* grpc_stream_tester.cc */; }; 34387C13A92D31B212BC0CA9 /* FSTMemoryLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650420A0E9BD00A2D6A1 /* FSTMemoryLRUGarbageCollectorTests.mm */; }; 355A9171EF3F7AD44A9C60CB /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; }; 358DBA8B2560C65D9EB23C35 /* Pods_Firestore_IntegrationTests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39B832380209CC5BAF93BC52 /* Pods_Firestore_IntegrationTests_macOS.framework */; }; + 36E174A66C323891AEA16A2A /* FIRTimestampTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */; }; 36FD4CE79613D18BC783C55B /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; 37EC6C6EA9169BB99078CA96 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; + 38208AC761FF994BA69822BE /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; 38430E0E07C54FCD399AE919 /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E046202154AA00B64F25 /* FIRQueryTests.mm */; }; 38F973FA8ADEAFE9541C25EA /* FSTMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0962021552C00B64F25 /* FSTMutationQueueTests.mm */; }; + 392F527F144BADDAC69C5485 /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; 3958F87E768E5CF40B87EF90 /* FSTMemoryLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0882021552A00B64F25 /* FSTMemoryLocalStoreTests.mm */; }; + 3A7CB01751697ED599F2D9A1 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; + 3B47E82ED2A3C59AB5002640 /* FSTMemoryLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0882021552A00B64F25 /* FSTMemoryLocalStoreTests.mm */; }; 3B843E4C1F3A182900548890 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; }; 3BCEBA50E9678123245C0272 /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93620239689000A432D /* empty_credentials_provider_test.cc */; }; 3D11B104A8F01F85180B38F6 /* field_transform_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352320A3AEC3003E0143 /* field_transform_test.mm */; }; + 3D9619906F09108E34FF0C95 /* FSTSmokeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */; }; 3DFBA7413965F3E6F366E923 /* grpc_unary_call_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964942163E63900EB9CFB /* grpc_unary_call_test.cc */; }; + 3E0C71810093ADFBAD9B453F /* FSTEventManagerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */; }; 3F2DF1DDDF7F5830F0669992 /* datastore_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 546854A820A36867004BDBD5 /* datastore_test.mm */; }; + 406939B62E5A6A22ADAB6FE6 /* FSTTreeSortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0841F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m */; }; + 40708C00B429E39CB20BA0F1 /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E046202154AA00B64F25 /* FIRQueryTests.mm */; }; + 409C0F2BFC2E1BECFFAC4D32 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; + 41EAC526C543064B8F3F7EDA /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; }; 42063E6AE9ADF659AA6D4E18 /* FSTSmokeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */; }; + 4247980BACA0070FB3E4A7A3 /* FSTMemoryRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08C2021552B00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm */; }; 45939AFF906155EA27D281AB /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; 46999832F7D1709B4C29FAA8 /* FIRDocumentReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */; }; 470A37727BBF516B05ED276A /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; @@ -92,12 +151,22 @@ 4A62B708A6532DD45414DA3A /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; 4A64A339BCA77B9F875D1D8B /* FSTDatastoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */; }; 4AA4ABE36065DB79CD76DD8D /* Pods_Firestore_Benchmarks_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F694C3CE4B77B3C0FA4BBA53 /* Pods_Firestore_Benchmarks_iOS.framework */; }; + 4AD9809C9CE9FA09AC40992F /* async_queue_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4680208EA0BE00554BA2 /* async_queue_libdispatch_test.mm */; }; + 4B52774AABF4ED7C2FA5C1C5 /* FSTMemoryQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08B2021552B00B64F25 /* FSTMemoryQueryCacheTests.mm */; }; + 4B57AE178F715ADE738C4F78 /* field_transform_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352320A3AEC3003E0143 /* field_transform_test.mm */; }; + 4BB325E8B87A2FA4483AA070 /* FSTViewSnapshotTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05C202154B800B64F25 /* FSTViewSnapshotTest.mm */; }; + 4C10843309CD11C455CF3B2B /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0922021552B00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm */; }; + 4CC78CA0E9E03F5DCF13FEBD /* Pods_Firestore_Tests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7DF4A6F740086A2D8C0E28E /* Pods_Firestore_Tests_tvOS.framework */; }; 4D1F46B2DD91198C8867C04C /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; + 4D42E5C756229C08560DD731 /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; 4D98894EB5B3D778F5628456 /* grpc_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6BBE42F21262CF400C6A53E /* grpc_stream_test.cc */; }; 4DAF501EE4B4DB79ED4239B0 /* secure_random_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A531FC913E500713A1A /* secure_random_test.cc */; }; 4DC660A62BC2B6369DA5C563 /* status_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352C20A3B3D7003E0143 /* status_test.cc */; }; + 4E679B9AA80202184D459569 /* FSTDocumentTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */; }; + 4E8085FB9DBE40BAE11F0F4E /* fake_credentials_provider.cc in Sources */ = {isa = PBXBuildFile; fileRef = B60894F62170207100EBC644 /* fake_credentials_provider.cc */; }; 4F67086B5CC1787F612AE503 /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */; }; 4F857404731D45F02C5EE4C3 /* async_queue_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4680208EA0BE00554BA2 /* async_queue_libdispatch_test.mm */; }; + 535F51F2FF2AB52A6E629091 /* FSTLevelDBMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0872021552A00B64F25 /* FSTLevelDBMutationQueueTests.mm */; }; 53AB47E44D897C81A94031F6 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; 54080260D85A6F583E61DA1D /* FSTLocalSerializerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08A2021552A00B64F25 /* FSTLocalSerializerTests.mm */; }; 54131E9720ADE679001DF3FF /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; @@ -203,6 +272,25 @@ 54A0352F20A3B3D8003E0143 /* status_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352C20A3B3D7003E0143 /* status_test.cc */; }; 54A0353020A3B3D8003E0143 /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; 54A0353520A3D8CB003E0143 /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; + 54AA3393224BF935006CE580 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 54AA3392224BF935006CE580 /* AppDelegate.m */; }; + 54AA3396224BF935006CE580 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 54AA3395224BF935006CE580 /* ViewController.m */; }; + 54AA3399224BF935006CE580 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54AA3397224BF935006CE580 /* Main.storyboard */; }; + 54AA339B224BF936006CE580 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54AA339A224BF936006CE580 /* Assets.xcassets */; }; + 54AA339E224BF936006CE580 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 54AA339D224BF936006CE580 /* main.m */; }; + 54ACB6C9224C11F400172E69 /* collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129C1F315EE100DD57A1 /* collection_spec_test.json */; }; + 54ACB6CA224C11F400172E69 /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; + 54ACB6CB224C11F400172E69 /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; }; + 54ACB6CC224C11F400172E69 /* limit_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129F1F315EE100DD57A1 /* limit_spec_test.json */; }; + 54ACB6CD224C11F400172E69 /* listen_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A01F315EE100DD57A1 /* listen_spec_test.json */; }; + 54ACB6CE224C11F400172E69 /* offline_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A11F315EE100DD57A1 /* offline_spec_test.json */; }; + 54ACB6CF224C11F400172E69 /* orderby_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A21F315EE100DD57A1 /* orderby_spec_test.json */; }; + 54ACB6D0224C11F400172E69 /* perf_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */; }; + 54ACB6D1224C11F400172E69 /* persistence_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A31F315EE100DD57A1 /* persistence_spec_test.json */; }; + 54ACB6D2224C11F400172E69 /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; + 54ACB6D3224C11F400172E69 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; }; + 54ACB6D4224C11F400172E69 /* resume_token_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A41F315EE100DD57A1 /* resume_token_spec_test.json */; }; + 54ACB6D5224C11F400172E69 /* write_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A51F315EE100DD57A1 /* write_spec_test.json */; }; + 54ACB6D6224C125B00172E69 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 54D400D32148BACE001D2BCC /* GoogleService-Info.plist */; }; 54C2294F1FECABAE007D065B /* log_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54C2294E1FECABAE007D065B /* log_test.cc */; }; 54D400D42148BACE001D2BCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 54D400D32148BACE001D2BCC /* GoogleService-Info.plist */; }; 54DA12A61F315EE100DD57A1 /* collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129C1F315EE100DD57A1 /* collection_spec_test.json */; }; @@ -218,19 +306,30 @@ 54EB764D202277B30088B8F3 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; 550FB7562D0CF9C3E1984000 /* FSTQueryListenerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05D202154B900B64F25 /* FSTQueryListenerTests.mm */; }; 5556B648B9B1C2F79A706B4F /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; + 55BDA39A16C4229A1AECB796 /* FSTLevelDBLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650620A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm */; }; + 5686B35D611C1CFF6BFE7215 /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D9342023966E000A432D /* credentials_provider_test.cc */; }; 568EC1C0F68A7B95E57C8C6C /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; 56D85436D3C864B804851B15 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; + 58E377DCCC64FE7D2C6B59A1 /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; + 596C782EFB68131380F8EEF8 /* user_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93220239654000A432D /* user_test.cc */; }; + 59D1E0A722CE68E00A3F85AA /* FSTLevelDBTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */; }; 5A080105CCBFDB6BF3F3772D /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; 5B62003FEA9A3818FDF4E2DD /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; + 5BE49546D57C43DDFCDB6FBD /* to_string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68B1E002213A764008977EF /* to_string_apple_test.mm */; }; + 5C7FAF228D0F52CFFE9E41B5 /* transform_operations_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352220A3AEC3003E0143 /* transform_operations_test.mm */; }; 5CC9650320A0E93200A2D6A1 /* FSTLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650220A0E93200A2D6A1 /* FSTLRUGarbageCollectorTests.mm */; }; 5CC9650520A0E9BD00A2D6A1 /* FSTMemoryLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650420A0E9BD00A2D6A1 /* FSTMemoryLRUGarbageCollectorTests.mm */; }; 5CC9650720A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650620A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm */; }; 5D405BE298CE4692CB00790A /* Pods_Firestore_Tests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */; }; 5D45CC300ED037358EF33A8F /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; + 5D5E24E3FA1128145AA117D2 /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; }; 5E5B3B8B3A41C8EB70035A6B /* FSTTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */; }; + 5E6F9184B271F6D5312412FF /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; 5EFBAD082CB0F86CD0711979 /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; 5F05A801B1EA44BC1264E55A /* FIRTypeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E071202154D600B64F25 /* FIRTypeTests.mm */; }; 5F19F66D8B01BA2B97579017 /* tree_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */; }; + 5FA3DB52A478B01384D3A2ED /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; + 5FE047FE866758FD6A6A6478 /* FIRFieldPathTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */; }; 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; @@ -243,6 +342,7 @@ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; + 60985657831B8DDE2C65AC8B /* FIRFieldsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */; }; 60C72F86D2231B1B6592A5E6 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; 6161B5032047140C00A99DBB /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; 618BBEA620B89AAC00B5BCE7 /* target.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */; }; @@ -252,23 +352,29 @@ 618BBEAF20B89AAC00B5BCE7 /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; 618BBEB020B89AAC00B5BCE7 /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; 618BBEB120B89AAC00B5BCE7 /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; }; + 61D1EB3438B92F61F6CAC191 /* FSTRemoteEventTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */; }; 61F72C5620BC48FD001A68CB /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; + 627253FDEC6BB5549FE77F4E /* tree_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */; }; 62DA31B79FE97A90EEF28B0B /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; 63BB61B6366E7F80C348419D /* FSTLevelDBTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */; }; 660E99DEDA0A6FC1CCB200F9 /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; 6672B445E006A7708B8531ED /* FSTImmutableSortedDictionary+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0801F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m */; }; 66FAB8EAC012A3822BD4D0C9 /* leveldb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */; }; + 69ED7BC38B3F981DE91E7933 /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; 6A4F6B42C628D55CCE0C311F /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E069202154D500B64F25 /* FIRQueryTests.mm */; }; 6B8806528FD3757D33D8B8AE /* FSTMemoryQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08B2021552B00B64F25 /* FSTMemoryQueryCacheTests.mm */; }; 6DCA8E54E652B78EFF3EEDAC /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; + 6E4854B19B120C6F0F8192CC /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; 6E59498D20F55BA800ECD9A5 /* FuzzingResources in Resources */ = {isa = PBXBuildFile; fileRef = 6ED6DEA120F5502700FC6076 /* FuzzingResources */; }; 6E8302E021022309003E1EA3 /* FSTFuzzTestFieldPath.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6E8302DF21022309003E1EA3 /* FSTFuzzTestFieldPath.mm */; }; 6EA39FDE20FE820E008D461F /* FSTFuzzTestSerializer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EA39FDD20FE820E008D461F /* FSTFuzzTestSerializer.mm */; }; + 6EB896CD1B64A60E6C82D8CC /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; 6EC28BB8C38E3FD126F68211 /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; 6EDD3B4620BF247500C33877 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6EDD3B4820BF247500C33877 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6EDD3B4920BF247500C33877 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 6EDD3B6020BF25AE00C33877 /* FSTFuzzTestsPrincipal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EDD3B5E20BF24D000C33877 /* FSTFuzzTestsPrincipal.mm */; }; + 6EEA00A737690EF82A3C91C6 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; 6F3CAC76D918D6B0917EDF92 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; 6FD2369F24E884A9D767DD80 /* FIRDocumentSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */; }; 6FF2B680CC8631B06C7BD7AB /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; }; @@ -279,49 +385,88 @@ 731541612214AFFA0037F4DC /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; 736C4E82689F1CA1859C4A3F /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; 73866AA12082B0A5009BB4FF /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; + 73E42D984FB36173A2BDA57C /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; 73F1F73C2210F3D800E1F692 /* memory_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */; }; 73F1F73D2210F3D800E1F692 /* index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */; }; 73F1F7412211FEF300E1F692 /* leveldb_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */; }; 73FE5066020EF9B2892C86BF /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; + 7400AC9377419A28B782B5EC /* objc_compatibility_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B696858F221770F000271095 /* objc_compatibility_apple_test.mm */; }; + 7495E3BAE536CD839EE20F31 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; 74985DE2C7EF4150D7A455FD /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; + 75D124966E727829A5F99249 /* FIRTypeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E071202154D600B64F25 /* FIRTypeTests.mm */; }; + 78E38BEDF502B85E27D50C3B /* nanopb_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */; }; + 7A3BE0ED54933C234FDE23D1 /* leveldb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */; }; + 7A7EC216A0015D7620B4FF3E /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; + 7B8D7BAC1A075DB773230505 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; + 7BBE0389D855242DDB83334B /* grpc_stream_tester.cc in Sources */ = {isa = PBXBuildFile; fileRef = B1A7E1959AF8141FA7E6B888 /* grpc_stream_tester.cc */; }; + 7DBE7DB90CF83B589A94980F /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; 7DD67E9621C52B790E844B16 /* FIRDatabaseTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */; }; + 7E4218DB09B85F8E379C73CB /* FSTLevelDBQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */; }; 7EF540911720DAAF516BEDF0 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; + 804B0C6CCE3933CF3948F249 /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; + 81B23D2D4E061074958AF12F /* target.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */; }; + 81D1B1D2B66BD8310AC5707F /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; + 8388418F43042605FB9BFB92 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; 83A9CD3B6E791A860CE81FA1 /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; 8403D519C916C72B9C7F2FA1 /* FIRValidationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06D202154D600B64F25 /* FIRValidationTests.mm */; }; + 8413BD9958F6DD52C466D70F /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; 8460C97C9209D7DAF07090BD /* FIRFieldsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */; }; 84DBE646DCB49305879D3500 /* nanopb_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */; }; + 85B8918FC8C5DC62482E39C3 /* resource_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2B02024FFD70028D6BE /* resource_path_test.cc */; }; + 8612F3C7E4A7D17221442699 /* grpc_unary_call_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964942163E63900EB9CFB /* grpc_unary_call_test.cc */; }; 862B1AC9EDAB309BBF4FB18C /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; + 8683BBC3AC7B01937606A83B /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; + 86E6FC2B7657C35B342E1436 /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; 8705C4856498F66E471A0997 /* FIRWriteBatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; + 88FD82A1FC5FEC5D56B481D8 /* maybe_document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */; }; 8943A7C0750CEB0B98D21209 /* FSTPersistenceTestHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08D2021552B00B64F25 /* FSTPersistenceTestHelpers.mm */; }; 897F3C1936612ACB018CA1DD /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; + 8A0749707105A077728119C2 /* FSTImmutableSortedDictionary+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0801F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m */; }; 8A6C809B9F81C30B7333FCAA /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; 8C39F6D4B3AA9074DF00CFB8 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; 8C82D4D3F9AB63E79CC52DC8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */; }; 8DA258092DD856D829D973B5 /* transform_operations_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352220A3AEC3003E0143 /* transform_operations_test.mm */; }; + 8F3AE423677A4C50F7E0E5C0 /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; }; 8F4F40E9BC7ED588F67734D5 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; + 900D0E9F18CE3DB954DD0D1E /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; }; 904DA0AE915C02154AE547FC /* FSTLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */; }; 927D22C6D294B82D1580C48D /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0922021552B00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm */; }; + 9328C93759C78A10FDBF68E0 /* FSTLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */; }; 938F2AF6EC5CD0B839300DB0 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; 939A15D3AD941CF7242DA9FA /* FSTLevelDBLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650620A0E9C600A2D6A1 /* FSTLevelDBLRUGarbageCollectorTests.mm */; }; 939C898FE9D129F6A2EA259C /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; + 94E5399FA5EA82CCB0549AB5 /* FSTDocumentKeyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */; }; + 95ED06D2B0078D3CDB821B68 /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; + 9664E5831CE35D515CDBC12A /* FSTRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */; }; 9720B8BD354CCB64C0C627E6 /* nanopb_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */; }; 974FF09E6AFD24D5A39B898B /* local_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F8043813A5D16963EC02B182 /* local_serializer_test.cc */; }; 9774A6C2AA02A12D80B34C3C /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; 9783FAEA4CF758E8C4C2D76E /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; }; 9794E074439ABE5457E60F35 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; + 9A29D572C64CA1FA62F591D4 /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E069202154D500B64F25 /* FIRQueryTests.mm */; }; + 9AC28D928902C6767A11F5FC /* type_traits_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */; }; + 9BD7DC8F5ADA0FE64AFAFA75 /* FSTLRUGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CC9650220A0E93200A2D6A1 /* FSTLRUGarbageCollectorTests.mm */; }; 9D0E720F5A6DBD48FF325016 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; 9D71628E38D9F64C965DF29E /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; A17DBC8F24127DA8A381F865 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; A1F57CC739211F64F2E9232D /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; A38F4AE525A87FDEA41DED47 /* FSTLevelDBQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */; }; + A4ECA8335000CBDF94586C94 /* FSTDatastoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */; }; A55266E6C986251D283CE948 /* FIRCursorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E070202154D600B64F25 /* FIRCursorTests.mm */; }; + A57EC303CD2D6AA4F4745551 /* FIRFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */; }; + A5AB1815C45FFC762981E481 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; A61AE3D94C975A87EFA82ADA /* firebase_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */; }; + A64B1CD2776BC118C74503A7 /* leveldb_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */; }; A6543DD0A56F8523C6D518E1 /* FSTMutationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B72021555100B64F25 /* FSTMutationTests.mm */; }; + A6D29E15ED1221352DBE0CF2 /* FSTPersistenceTestHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08D2021552B00B64F25 /* FSTPersistenceTestHelpers.mm */; }; A7470B7B2433264FFDCC7AC3 /* FSTLevelDBLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08F2021552B00B64F25 /* FSTLevelDBLocalStoreTests.mm */; }; + A8C9FF6D13E6C83D4AB54EA7 /* secure_random_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A531FC913E500713A1A /* secure_random_test.cc */; }; A907244EE37BC32C8D82948E /* FSTSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */; }; A94884460990CD48CC0AD070 /* xcgmock_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4425A513895DEC60325A139E /* xcgmock_test.mm */; }; AAA50E56B9A7EF3EFDA62172 /* create_noop_connectivity_monitor.cc in Sources */ = {isa = PBXBuildFile; fileRef = B67BF448216EB43000CA9097 /* create_noop_connectivity_monitor.cc */; }; + AAC15E7CCAE79619B2ABB972 /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; + AAF2F02E77A80C9CDE2C0C7A /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; AB380CFE201A2F4500D97691 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; @@ -342,9 +487,12 @@ AC6C1E57B18730428CB15E03 /* executor_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */; }; ACC9369843F5ED3BD2284078 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; AD86162AC78673BA969F3467 /* FSTQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0892021552A00B64F25 /* FSTQueryCacheTests.mm */; }; + AEBF3F80ACC01AA8A27091CD /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; B03F286F3AEC3781C386C646 /* FIRNumericTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */; }; B192F30DECA8C28007F9B1D0 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; + B220E091D8F4E6DE1EA44F57 /* executor_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */; }; B49311BDE5EB6DF811E03C1B /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D9342023966E000A432D /* credentials_provider_test.cc */; }; + B513F723728E923DFF34F60F /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; B60894F72170207200EBC644 /* fake_credentials_provider.cc in Sources */ = {isa = PBXBuildFile; fileRef = B60894F62170207100EBC644 /* fake_credentials_provider.cc */; }; B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; B65D34A9203C995B0076A5E1 /* FIRTimestampTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */; }; @@ -369,11 +517,21 @@ B6FB468E208F9BAB00554BA2 /* executor_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */; }; B6FB468F208F9BAE00554BA2 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; B6FB4690208F9BB300554BA2 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; + B6FDE6F91D3F81D045E962A0 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; B8062EBDB8E5B680E46A6DD1 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; B89EF6551734723BDC6AB79C /* FSTDocumentKeyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */; }; + BA9A65BD6D993B2801A3C768 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; }; + BAB43C839445782040657239 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; + BBFCCD960DD2937EE278D7B6 /* FSTQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0892021552A00B64F25 /* FSTQueryCacheTests.mm */; }; + BC0C98A9201E8F98B9A176A9 /* FIRWriteBatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */; }; + BC2D0A8EA272A0058F6C2B9E /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; BCD9AEA4A890E804922BF72F /* FSTRemoteEventTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */; }; BEE0294A23AB993E5DE0E946 /* leveldb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */; }; + C0AD8DB5A84CAAEE36230899 /* status_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352C20A3B3D7003E0143 /* status_test.cc */; }; + C13502E39B0AEF0FADDDA5F2 /* FSTDocumentSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */; }; C1AA536F90A0A576CA2816EB /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */; }; + C1B859FD314E866619683940 /* FSTImmutableSortedSet+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0821F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m */; }; + C1E35BCE2CFF9B56C28545A2 /* Pods_Firestore_Example_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62E103B28B48A81D682A0DE9 /* Pods_Firestore_Example_tvOS.framework */; }; C21B3A1CCB3AD42E57EA14FC /* Pods_Firestore_Tests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 759E964B6A03E6775C992710 /* Pods_Firestore_Tests_macOS.framework */; }; C39CBADA58F442C8D66C3DA2 /* FIRFieldPathTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */; }; C4055D868A38221B332CD03D /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; @@ -386,17 +544,30 @@ C80B10E79CDD7EF7843C321E /* type_traits_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */; }; C8D3CE2343E53223E6487F2C /* Pods_Firestore_Example_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */; }; C9F96C511F45851D38EC449C /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; }; + CA18CEF2585A6BC4974DB56D /* FSTQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E061202154B900B64F25 /* FSTQueryTests.mm */; }; + CA69FC4DF0C906183CF5DCE9 /* FSTFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */; }; CA989C0E6020C372A62B7062 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; CD0AA9E5D83C00CAAE7C2F67 /* FIRTimestampTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */; }; + CD78EEAA1CD36BE691CA3427 /* hashing_test_apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = B69CF3F02227386500B281C8 /* hashing_test_apple.mm */; }; + CEDDC6DB782989587D0139B2 /* datastore_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 546854A820A36867004BDBD5 /* datastore_test.mm */; }; D063F56AC89E074F9AB05DD3 /* FSTRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */; }; + D22B96C19A0F3DE998D4320C /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; D44DA2F61B854E8771E4E446 /* memory_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7392210F3D800E1F692 /* memory_index_manager_test.mm */; }; D572B4D4DBDD6B9235781646 /* objc_compatibility_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B696858F221770F000271095 /* objc_compatibility_apple_test.mm */; }; D57F4CB3C92CE3D4DF329B78 /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; D59FAEE934987D4C4B2A67B2 /* FIRFirestoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */; }; D5B252EE3F4037405DB1ECE3 /* FIRNumericTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */; }; D5B25CBF07F65E885C9D68AB /* perf_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */; }; + D5E9954FC1C5ABBC7A180B33 /* FSTSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */; }; + D69B97FF4C065EACEDD91886 /* FSTSyncEngineTestDriver.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */; }; + D6DE74259F5C0CCA010D6A0D /* grpc_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6BBE42F21262CF400C6A53E /* grpc_stream_test.cc */; }; + D6E0E54CD1640E726900828A /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; + D77941FD93DBE862AEF1F623 /* FSTTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */; }; D9366A834BFF13246DC3AF9E /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; }; D94A1862B8FB778225DB54A1 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; + D98A0B6007E271E32299C79D /* FIRGeoPointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */; }; + DA4303684707606318E1914D /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; + DAC43DD1FDFBAB1FE1AD6BE5 /* firebase_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */; }; DAFF0CF921E64AC30062958F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFF0CF821E64AC30062958F /* AppDelegate.m */; }; DAFF0CFB21E64AC40062958F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAFF0CFA21E64AC40062958F /* Assets.xcassets */; }; DAFF0CFE21E64AC40062958F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DAFF0CFC21E64AC40062958F /* MainMenu.xib */; }; @@ -404,6 +575,7 @@ DAFF0D0921E653A00062958F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 54D400D32148BACE001D2BCC /* GoogleService-Info.plist */; }; DB7E9C5A59CCCDDB7F0C238A /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; DBDC8E997E909804F1B43E92 /* log_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54C2294E1FECABAE007D065B /* log_test.cc */; }; + DC48407370E87F2233D7AB7E /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; DD213F68A6F79E1D4924BD95 /* Pods_Firestore_Example_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E42355285B9EF55ABD785792 /* Pods_Firestore_Example_macOS.framework */; }; DD5976A45071455FF3FE74B8 /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; DE03B2D41F2149D600A30B9C /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; @@ -417,24 +589,46 @@ DE2EF0881F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0841F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m */; }; DEF036EA1ECEECD8E3ECC362 /* FSTImmutableSortedSet+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0821F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m */; }; E084921EFB7CF8CB1E950D6C /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; + E0E640226A1439C59BBBA9C1 /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; E11DDA3DD75705F26245E295 /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; }; E2B15548A3B6796CE5A01975 /* FIRListenerRegistrationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06B202154D500B64F25 /* FIRListenerRegistrationTests.mm */; }; + E375FBA0632EFB4D14C4E5A9 /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; E3C0E5F834A82EEE9F8C4519 /* watch_change_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68FC0E421F6848700A7055C /* watch_change_test.mm */; }; E4C0CC7FB88D8F6CB1B972C6 /* leveldb_index_manager_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */; }; + E4EEF6AAFCD33303CE9E5408 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; + E500AB82DF2E7F3AFDB1AB3F /* to_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B696858D2214B53900271095 /* to_string_test.cc */; }; + E50187548B537DBCDBF7F9F0 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; E6821243C510797EFFC7BCE2 /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; E764F0F389E7119220EB212C /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; E7D415B8717701B952C344E5 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; E82F8EBBC8CC37299A459E73 /* hashing_test_apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = B69CF3F02227386500B281C8 /* hashing_test_apple.mm */; }; E9558682F8A4DD3E7C85C067 /* FSTLevelDBMigrationsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0862021552A00B64F25 /* FSTLevelDBMigrationsTests.mm */; }; E980E1DCF759D5EF9F6B98F2 /* FSTDocumentTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */; }; + EA38690795FBAA182A9AA63E /* FIRDatabaseTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */; }; + EB04FE18E5794FEC187A09E3 /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; }; EBFC611B1BF195D0EC710AF4 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; + EC160876D8A42166440E0B53 /* FIRCursorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E070202154D600B64F25 /* FIRCursorTests.mm */; }; EC80A217F3D66EB0272B36B0 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; + ED420D8F49DA5C41EEF93913 /* FIRSnapshotMetadataTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */; }; + ED4E2AC80CAF2A8FDDAC3DEE /* field_mask_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5320A36E1F00BCEB75 /* field_mask_test.cc */; }; + EF3518F84255BAF3EBD317F6 /* exponential_backoff_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D1B68420E2AB1A00B35856 /* exponential_backoff_test.cc */; }; F007A46BE03A01C077EFCBD8 /* FSTFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */; }; + F1661B1C5F3E30535FB65046 /* FSTArraySortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF07E1F3D0B6E003D0CDC /* FSTArraySortedDictionaryTests.m */; }; + F19B749671F2552E964422F7 /* FIRListenerRegistrationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06B202154D500B64F25 /* FIRListenerRegistrationTests.mm */; }; F3261CBFC169DB375A0D9492 /* FSTMockDatastore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */; }; F3F09BC931A717CEFF4E14B9 /* FIRFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */; }; + F46394FAA186BC6D19213B59 /* FSTLevelDBLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08F2021552B00B64F25 /* FSTLevelDBLocalStoreTests.mm */; }; + F481368DB694B3B4D0C8E4A2 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; F4F00BF4E87D7F0F0F8831DB /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; + F58A4EE0A1A77F61EF41E5ED /* FSTSerializerBetaTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */; }; + F72DF72447EA7AB9D100816A /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; + F7718C43D3A8FCCDB4BB0071 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; F8D3EF0C9044BB3F3E81C6CA /* FSTDocumentSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */; }; F9DC01FCBE76CD4F0453A67C /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; + FA63B7521A07F1EB2F999859 /* FSTLevelDBMigrationsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0862021552A00B64F25 /* FSTLevelDBMigrationsTests.mm */; }; + FA7837C5CDFB273DE447E447 /* FIRServerTimestampTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */; }; + FD8EA96A604E837092ACA51D /* ordered_code_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D03201BC6E400D97691 /* ordered_code_test.cc */; }; + FEF55ECFB0CA317B351179AB /* no_document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908720322E8800CC290A /* no_document_test.cc */; }; FF3405218188DFCE586FB26B /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; FF4FA5757D13A2B7CEE40F04 /* document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D821C2DDC800EFB9CC /* document.pb.cc */; }; /* End PBXBuildFile section */ @@ -447,6 +641,20 @@ remoteGlobalIDString = DAFF0CF421E64AC30062958F; remoteInfo = Firestore_Example_macOS; }; + 54AA33AB224BFE0A006CE580 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 54AA338E224BF935006CE580; + remoteInfo = Firestore_Example_tvOS; + }; + 54AA33B9224C0035006CE580 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 54AA338E224BF935006CE580; + remoteInfo = Firestore_Example_tvOS; + }; 54B8E4AF224BDC4100930F18 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6003F582195388D10070C39A /* Project object */; @@ -520,12 +728,15 @@ 132E32997D781B896672D30A /* reference_set_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = reference_set_test.cc; sourceTree = ""; }; 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBTransactionTests.mm; sourceTree = ""; }; 132E3BB3D5C42282B4ACFB20 /* FSTLevelDBBenchmarkTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBBenchmarkTests.mm; sourceTree = ""; }; + 2220F583583EFC28DE792ABE /* Pods_Firestore_IntegrationTests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = type_traits_apple_test.mm; sourceTree = ""; }; 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2E48431B0EDA400BEA91D4AB /* Pods-Firestore_Tests_tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_tvOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_tvOS/Pods-Firestore_Tests_tvOS.debug.xcconfig"; sourceTree = ""; }; 2F901F31BC62444A476B779F /* Pods-Firestore_IntegrationTests_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_macOS/Pods-Firestore_IntegrationTests_macOS.debug.xcconfig"; sourceTree = ""; }; 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_util_test.cc; sourceTree = ""; }; 353EEE078EF3F39A9B7279F6 /* nanopb_string_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = nanopb_string_test.cc; path = nanopb/nanopb_string_test.cc; sourceTree = ""; }; 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = strerror_test.cc; sourceTree = ""; }; + 36D235D9F1240D5195CDB670 /* Pods-Firestore_IntegrationTests_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_tvOS/Pods-Firestore_IntegrationTests_tvOS.release.xcconfig"; sourceTree = ""; }; 397FB002E298B780F1E223E2 /* Pods-Firestore_Tests_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS.release.xcconfig"; sourceTree = ""; }; 39B832380209CC5BAF93BC52 /* Pods_Firestore_IntegrationTests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = ""; }; @@ -636,6 +847,17 @@ 54A0352C20A3B3D7003E0143 /* status_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = status_test.cc; sourceTree = ""; }; 54A0352D20A3B3D7003E0143 /* statusor_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = statusor_test.cc; sourceTree = ""; }; 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iterator_adaptors_test.cc; sourceTree = ""; }; + 54AA338F224BF935006CE580 /* Firestore_Example_tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example_tvOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 54AA3391224BF935006CE580 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 54AA3392224BF935006CE580 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 54AA3394224BF935006CE580 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 54AA3395224BF935006CE580 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 54AA3398224BF935006CE580 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 54AA339A224BF936006CE580 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 54AA339C224BF936006CE580 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 54AA339D224BF936006CE580 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 54AA33A6224BFE09006CE580 /* Firestore_Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 54AA33B4224C0035006CE580 /* Firestore_IntegrationTests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_IntegrationTests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54B8E4AA224BDC4100930F18 /* Firestore_IntegrationTests_macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_IntegrationTests_macOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54C2294E1FECABAE007D065B /* log_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log_test.cc; sourceTree = ""; }; 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_SwiftTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -694,7 +916,9 @@ 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = status.pb.cc; sourceTree = ""; }; 618BBE9A20B89AAC00B5BCE7 /* status.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = status.pb.h; sourceTree = ""; }; 61F72C5520BC48FD001A68CB /* serializer_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serializer_test.cc; sourceTree = ""; }; + 62E103B28B48A81D682A0DE9 /* Pods_Firestore_Example_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; sourceTree = ""; }; + 6AE927CDFC7A72BF825BE4CB /* Pods-Firestore_Tests_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_tvOS/Pods-Firestore_Tests_tvOS.release.xcconfig"; sourceTree = ""; }; 6E8302DE210222ED003E1EA3 /* FSTFuzzTestFieldPath.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTFuzzTestFieldPath.h; sourceTree = ""; }; 6E8302DF21022309003E1EA3 /* FSTFuzzTestFieldPath.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTFuzzTestFieldPath.mm; sourceTree = ""; }; 6EA39FDD20FE820E008D461F /* FSTFuzzTestSerializer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTFuzzTestSerializer.mm; sourceTree = ""; }; @@ -710,6 +934,7 @@ 73F1F73A2210F3D800E1F692 /* index_manager_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = index_manager_test.h; sourceTree = ""; }; 73F1F73B2210F3D800E1F692 /* index_manager_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = index_manager_test.mm; sourceTree = ""; }; 73F1F7402211FEF300E1F692 /* leveldb_index_manager_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = leveldb_index_manager_test.mm; sourceTree = ""; }; + 74AC2ADBF1BAD9A8EF30CF41 /* Pods-Firestore_IntegrationTests_tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_tvOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_tvOS/Pods-Firestore_IntegrationTests_tvOS.debug.xcconfig"; sourceTree = ""; }; 759E964B6A03E6775C992710 /* Pods_Firestore_Tests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 79507DF8378D3C42F5B36268 /* string_win_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = string_win_test.cc; sourceTree = ""; }; 84434E57CA72951015FC71BC /* Pods-Firestore_FuzzTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_FuzzTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS.debug.xcconfig"; sourceTree = ""; }; @@ -719,6 +944,7 @@ 98366480BD1FD44A1FEDD982 /* Pods-Firestore_Example_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS.debug.xcconfig"; sourceTree = ""; }; 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_format_apple_test.mm; sourceTree = ""; }; A5FA86650A18F3B7A8162287 /* Pods-Firestore_Benchmarks_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Benchmarks_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS.release.xcconfig"; sourceTree = ""; }; + A70E82DD627B162BEF92B8ED /* Pods-Firestore_Example_tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_tvOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS.debug.xcconfig"; sourceTree = ""; }; AB356EF6200EA5EB0089B766 /* field_value_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = field_value_test.cc; sourceTree = ""; }; AB380CF82019382300D97691 /* target_id_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = target_id_generator_test.cc; sourceTree = ""; }; AB380CFC201A2EE200D97691 /* string_util_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_util_test.cc; sourceTree = ""; }; @@ -778,6 +1004,7 @@ D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = perf_spec_test.json; sourceTree = ""; }; D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRNumericTransformTests.mm; sourceTree = ""; }; + D7DF4A6F740086A2D8C0E28E /* Pods_Firestore_Tests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DAFF0CF521E64AC30062958F /* Firestore_Example_macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example_macOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; DAFF0CF721E64AC30062958F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DAFF0CF821E64AC30062958F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -812,6 +1039,7 @@ F694C3CE4B77B3C0FA4BBA53 /* Pods_Firestore_Benchmarks_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Benchmarks_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F8043813A5D16963EC02B182 /* local_serializer_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = local_serializer_test.cc; sourceTree = ""; }; FA2E9952BA2B299C1156C43C /* Pods-Firestore_Benchmarks_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Benchmarks_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS.debug.xcconfig"; sourceTree = ""; }; + FC738525340E594EBFAB121E /* Pods-Firestore_Example_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -823,6 +1051,30 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 54AA338C224BF935006CE580 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C1E35BCE2CFF9B56C28545A2 /* Pods_Firestore_Example_tvOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 54AA33A3224BFE09006CE580 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CC78CA0E9E03F5DCF13FEBD /* Pods_Firestore_Tests_tvOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 54AA33B1224C0035006CE580 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 20A26E9D0336F7F32A098D05 /* Pods_Firestore_IntegrationTests_tvOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54B8E4A7224BDC4100930F18 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -907,6 +1159,7 @@ children = ( 6003F593195388D20070C39A /* iOS */, DAFF0CF621E64AC30062958F /* macOS */, + 54AA3390224BF935006CE580 /* tvOS */, 54D400D32148BACE001D2BCC /* GoogleService-Info.plist */, ); path = App; @@ -1057,6 +1310,21 @@ path = local; sourceTree = ""; }; + 54AA3390224BF935006CE580 /* tvOS */ = { + isa = PBXGroup; + children = ( + 54AA3391224BF935006CE580 /* AppDelegate.h */, + 54AA3392224BF935006CE580 /* AppDelegate.m */, + 54AA339A224BF936006CE580 /* Assets.xcassets */, + 54AA339C224BF936006CE580 /* Info.plist */, + 54AA3397224BF935006CE580 /* Main.storyboard */, + 54AA3394224BF935006CE580 /* ViewController.h */, + 54AA3395224BF935006CE580 /* ViewController.m */, + 54AA339D224BF936006CE580 /* main.m */, + ); + path = tvOS; + sourceTree = ""; + }; 54C9EDF22040E16300A969CD /* SwiftTests */ = { isa = PBXGroup; children = ( @@ -1120,12 +1388,15 @@ 5CAE131920FFFED600BE9A4A /* Firestore_Benchmarks_iOS.xctest */, 6003F58A195388D20070C39A /* Firestore_Example_iOS.app */, DAFF0CF521E64AC30062958F /* Firestore_Example_macOS.app */, + 54AA338F224BF935006CE580 /* Firestore_Example_tvOS.app */, 6EDD3B5B20BF247500C33877 /* Firestore_FuzzTests_iOS.xctest */, DE03B2E91F2149D600A30B9C /* Firestore_IntegrationTests_iOS.xctest */, 54B8E4AA224BDC4100930F18 /* Firestore_IntegrationTests_macOS.xctest */, + 54AA33B4224C0035006CE580 /* Firestore_IntegrationTests_tvOS.xctest */, 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */, 6003F5AE195388D20070C39A /* Firestore_Tests_iOS.xctest */, 544AB1922248072200F851E6 /* Firestore_Tests_macOS.xctest */, + 54AA33A6224BFE09006CE580 /* Firestore_Tests_tvOS.xctest */, ); name = Products; sourceTree = ""; @@ -1139,11 +1410,14 @@ 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */, BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */, E42355285B9EF55ABD785792 /* Pods_Firestore_Example_macOS.framework */, + 62E103B28B48A81D682A0DE9 /* Pods_Firestore_Example_tvOS.framework */, B79CA87A1A01FC5329031C9B /* Pods_Firestore_FuzzTests_iOS.framework */, ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */, 39B832380209CC5BAF93BC52 /* Pods_Firestore_IntegrationTests_macOS.framework */, + 2220F583583EFC28DE792ABE /* Pods_Firestore_IntegrationTests_tvOS.framework */, 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */, 759E964B6A03E6775C992710 /* Pods_Firestore_Tests_macOS.framework */, + D7DF4A6F740086A2D8C0E28E /* Pods_Firestore_Tests_tvOS.framework */, 6003F591195388D20070C39A /* UIKit.framework */, 6003F5AF195388D20070C39A /* XCTest.framework */, ); @@ -1315,16 +1589,22 @@ 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */, 98366480BD1FD44A1FEDD982 /* Pods-Firestore_Example_macOS.debug.xcconfig */, DF148C0D5EEC4A2CD9FA484C /* Pods-Firestore_Example_macOS.release.xcconfig */, + A70E82DD627B162BEF92B8ED /* Pods-Firestore_Example_tvOS.debug.xcconfig */, + FC738525340E594EBFAB121E /* Pods-Firestore_Example_tvOS.release.xcconfig */, 84434E57CA72951015FC71BC /* Pods-Firestore_FuzzTests_iOS.debug.xcconfig */, 97C492D2524E92927C11F425 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */, 1277F98C20D2DF0867496976 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */, F354C0FE92645B56A6C6FD44 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */, 2F901F31BC62444A476B779F /* Pods-Firestore_IntegrationTests_macOS.debug.xcconfig */, B953604968FBF5483BD20F5A /* Pods-Firestore_IntegrationTests_macOS.release.xcconfig */, + 74AC2ADBF1BAD9A8EF30CF41 /* Pods-Firestore_IntegrationTests_tvOS.debug.xcconfig */, + 36D235D9F1240D5195CDB670 /* Pods-Firestore_IntegrationTests_tvOS.release.xcconfig */, E592181BFD7C53C305123739 /* Pods-Firestore_Tests_iOS.debug.xcconfig */, B3F5B3AAE791A5911B9EAA82 /* Pods-Firestore_Tests_iOS.release.xcconfig */, BD01F0E43E4E2A07B8B05099 /* Pods-Firestore_Tests_macOS.debug.xcconfig */, 397FB002E298B780F1E223E2 /* Pods-Firestore_Tests_macOS.release.xcconfig */, + 2E48431B0EDA400BEA91D4AB /* Pods-Firestore_Tests_tvOS.debug.xcconfig */, + 6AE927CDFC7A72BF825BE4CB /* Pods-Firestore_Tests_tvOS.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -1601,6 +1881,65 @@ productReference = 544AB1922248072200F851E6 /* Firestore_Tests_macOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 54AA338E224BF935006CE580 /* Firestore_Example_tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 54AA33A1224BF936006CE580 /* Build configuration list for PBXNativeTarget "Firestore_Example_tvOS" */; + buildPhases = ( + 8748E45246D96175497949A5 /* [CP] Check Pods Manifest.lock */, + 54AA338B224BF935006CE580 /* Sources */, + 54AA338C224BF935006CE580 /* Frameworks */, + 54AA338D224BF935006CE580 /* Resources */, + 264B3405701AA9DC9F07658B /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Firestore_Example_tvOS; + productName = Firestore_Example_tvOS; + productReference = 54AA338F224BF935006CE580 /* Firestore_Example_tvOS.app */; + productType = "com.apple.product-type.application"; + }; + 54AA33A5224BFE09006CE580 /* Firestore_Tests_tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 54AA33AF224BFE0A006CE580 /* Build configuration list for PBXNativeTarget "Firestore_Tests_tvOS" */; + buildPhases = ( + A4274FBF1C966A0513CBD0F6 /* [CP] Check Pods Manifest.lock */, + 54AA33A2224BFE09006CE580 /* Sources */, + 54AA33A3224BFE09006CE580 /* Frameworks */, + 54AA33A4224BFE09006CE580 /* Resources */, + 1B1BCDC6BB656D6B79D246DD /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 54AA33AC224BFE0A006CE580 /* PBXTargetDependency */, + ); + name = Firestore_Tests_tvOS; + productName = Firestore_Tests_tvOS; + productReference = 54AA33A6224BFE09006CE580 /* Firestore_Tests_tvOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 54AA33B3224C0035006CE580 /* Firestore_IntegrationTests_tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 54AA33BB224C0035006CE580 /* Build configuration list for PBXNativeTarget "Firestore_IntegrationTests_tvOS" */; + buildPhases = ( + 6800EBA4F597F7115445FCB5 /* [CP] Check Pods Manifest.lock */, + 54AA33B0224C0035006CE580 /* Sources */, + 54AA33B1224C0035006CE580 /* Frameworks */, + 54AA33B2224C0035006CE580 /* Resources */, + 76368D74F155BC9491DC124E /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 54AA33BA224C0035006CE580 /* PBXTargetDependency */, + ); + name = Firestore_IntegrationTests_tvOS; + productName = Firestore_IntegrationTests_tvOS; + productReference = 54AA33B4224C0035006CE580 /* Firestore_IntegrationTests_tvOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 54B8E4A9224BDC4100930F18 /* Firestore_IntegrationTests_macOS */ = { isa = PBXNativeTarget; buildConfigurationList = 54B8E4B3224BDC4100930F18 /* Build configuration list for PBXNativeTarget "Firestore_IntegrationTests_macOS" */; @@ -1776,6 +2115,20 @@ ProvisioningStyle = Automatic; TestTargetID = DAFF0CF421E64AC30062958F; }; + 54AA338E224BF935006CE580 = { + CreatedOnToolsVersion = 10.1; + ProvisioningStyle = Automatic; + }; + 54AA33A5224BFE09006CE580 = { + CreatedOnToolsVersion = 10.1; + ProvisioningStyle = Automatic; + TestTargetID = 54AA338E224BF935006CE580; + }; + 54AA33B3224C0035006CE580 = { + CreatedOnToolsVersion = 10.1; + ProvisioningStyle = Automatic; + TestTargetID = 54AA338E224BF935006CE580; + }; 54B8E4A9224BDC4100930F18 = { CreatedOnToolsVersion = 10.1; ProvisioningStyle = Automatic; @@ -1838,6 +2191,9 @@ DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */, 544AB1912248072200F851E6 /* Firestore_Tests_macOS */, 54B8E4A9224BDC4100930F18 /* Firestore_IntegrationTests_macOS */, + 54AA338E224BF935006CE580 /* Firestore_Example_tvOS */, + 54AA33A5224BFE09006CE580 /* Firestore_Tests_tvOS */, + 54AA33B3224C0035006CE580 /* Firestore_IntegrationTests_tvOS */, ); }; /* End PBXProject section */ @@ -1863,6 +2219,43 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 54AA338D224BF935006CE580 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 54AA339B224BF936006CE580 /* Assets.xcassets in Resources */, + 54ACB6D6224C125B00172E69 /* GoogleService-Info.plist in Resources */, + 54AA3399224BF935006CE580 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 54AA33A4224BFE09006CE580 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 54ACB6C9224C11F400172E69 /* collection_spec_test.json in Resources */, + 54ACB6CA224C11F400172E69 /* existence_filter_spec_test.json in Resources */, + 54ACB6CB224C11F400172E69 /* limbo_spec_test.json in Resources */, + 54ACB6CC224C11F400172E69 /* limit_spec_test.json in Resources */, + 54ACB6CD224C11F400172E69 /* listen_spec_test.json in Resources */, + 54ACB6CE224C11F400172E69 /* offline_spec_test.json in Resources */, + 54ACB6CF224C11F400172E69 /* orderby_spec_test.json in Resources */, + 54ACB6D0224C11F400172E69 /* perf_spec_test.json in Resources */, + 54ACB6D1224C11F400172E69 /* persistence_spec_test.json in Resources */, + 54ACB6D2224C11F400172E69 /* query_spec_test.json in Resources */, + 54ACB6D3224C11F400172E69 /* remote_store_spec_test.json in Resources */, + 54ACB6D4224C11F400172E69 /* resume_token_spec_test.json in Resources */, + 54ACB6D5224C11F400172E69 /* write_spec_test.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 54AA33B2224C0035006CE580 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54B8E4A8224BDC4100930F18 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1947,6 +2340,34 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 1B1BCDC6BB656D6B79D246DD /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Firestore_Tests_tvOS/Pods-Firestore_Tests_tvOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/leveldb-library-tvOS/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/GoogleTest-tvOS/GoogleTest.framework", + "${BUILT_PRODUCTS_DIR}/OCMock-tvOS/OCMock.framework", + "${BUILT_PRODUCTS_DIR}/ProtobufCpp-tvOS/ProtobufCpp.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtobufCpp.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Tests_tvOS/Pods-Firestore_Tests_tvOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 1EE692C7509A98D7EB03CA51 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1979,6 +2400,42 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 264B3405701AA9DC9F07658B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-tvOS/openssl_grpc.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-tvOS/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities-Environment-Logger-tvOS/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/Protobuf-tvOS10.0/Protobuf.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-C++-tvOS/grpcpp.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-Core-tvOS/grpc.framework", + "${BUILT_PRODUCTS_DIR}/leveldb-library-tvOS/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/nanopb-tvOS/nanopb.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpcpp.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 30108B32BF2B385AECDB7FB2 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2065,6 +2522,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 6800EBA4F597F7115445FCB5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Firestore_IntegrationTests_tvOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 6A86E48DF663B6AA1CB5BA83 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2074,7 +2553,7 @@ "${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-macOS/openssl_grpc.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-macOS/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities-Environment-Logger/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities-Environment-Logger-macOS/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/Protobuf-macOS10.10/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/gRPC-C++-macOS/grpcpp.framework", "${BUILT_PRODUCTS_DIR}/gRPC-Core-macOS/grpc.framework", @@ -2152,6 +2631,32 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 76368D74F155BC9491DC124E /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Firestore_IntegrationTests_tvOS/Pods-Firestore_IntegrationTests_tvOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/leveldb-library-tvOS/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/GoogleTest-tvOS/GoogleTest.framework", + "${BUILT_PRODUCTS_DIR}/OCMock-tvOS/OCMock.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_IntegrationTests_tvOS/Pods-Firestore_IntegrationTests_tvOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 7C2467DCD3E3E16FB0A737DE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2216,6 +2721,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 8748E45246D96175497949A5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Firestore_Example_tvOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 8B469EB6DA9E6404589402E2 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2234,6 +2761,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + A4274FBF1C966A0513CBD0F6 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Firestore_Tests_tvOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; A827A009A65B69DC1B80EAD4 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2528,6 +3077,202 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 54AA338B224BF935006CE580 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 54AA3393224BF935006CE580 /* AppDelegate.m in Sources */, + 54AA3396224BF935006CE580 /* ViewController.m in Sources */, + 54AA339E224BF936006CE580 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 54AA33A2224BFE09006CE580 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00B7AFE2A7C158DD685EB5EE /* FIRCollectionReferenceTests.mm in Sources */, + 25FE27330996A59F31713A0C /* FIRDocumentReferenceTests.mm in Sources */, + 28E4B4A53A739AE2C9CF4159 /* FIRDocumentSnapshotTests.mm in Sources */, + 5FE047FE866758FD6A6A6478 /* FIRFieldPathTests.mm in Sources */, + A57EC303CD2D6AA4F4745551 /* FIRFieldValueTests.mm in Sources */, + 31BDB4CB0E7458C650A77ED0 /* FIRFirestoreTests.mm in Sources */, + D98A0B6007E271E32299C79D /* FIRGeoPointTests.mm in Sources */, + 17638F813B9B556FE7718C0C /* FIRQuerySnapshotTests.mm in Sources */, + 40708C00B429E39CB20BA0F1 /* FIRQueryTests.mm in Sources */, + ED420D8F49DA5C41EEF93913 /* FIRSnapshotMetadataTests.mm in Sources */, + 36E174A66C323891AEA16A2A /* FIRTimestampTest.m in Sources */, + 6E4854B19B120C6F0F8192CC /* FSTAPIHelpers.mm in Sources */, + F1661B1C5F3E30535FB65046 /* FSTArraySortedDictionaryTests.m in Sources */, + 94E5399FA5EA82CCB0549AB5 /* FSTDocumentKeyTests.mm in Sources */, + C13502E39B0AEF0FADDDA5F2 /* FSTDocumentSetTests.mm in Sources */, + 4E679B9AA80202184D459569 /* FSTDocumentTests.mm in Sources */, + 73E42D984FB36173A2BDA57C /* FSTEventAccumulator.mm in Sources */, + 3E0C71810093ADFBAD9B453F /* FSTEventManagerTests.mm in Sources */, + CA69FC4DF0C906183CF5DCE9 /* FSTFieldValueTests.mm in Sources */, + E375FBA0632EFB4D14C4E5A9 /* FSTGoogleTestTests.mm in Sources */, + F72DF72447EA7AB9D100816A /* FSTHelpers.mm in Sources */, + 8A0749707105A077728119C2 /* FSTImmutableSortedDictionary+Testing.m in Sources */, + C1B859FD314E866619683940 /* FSTImmutableSortedSet+Testing.m in Sources */, + AEBF3F80ACC01AA8A27091CD /* FSTIntegrationTestCase.mm in Sources */, + 9BD7DC8F5ADA0FE64AFAFA75 /* FSTLRUGarbageCollectorTests.mm in Sources */, + 55BDA39A16C4229A1AECB796 /* FSTLevelDBLRUGarbageCollectorTests.mm in Sources */, + F46394FAA186BC6D19213B59 /* FSTLevelDBLocalStoreTests.mm in Sources */, + FA63B7521A07F1EB2F999859 /* FSTLevelDBMigrationsTests.mm in Sources */, + 535F51F2FF2AB52A6E629091 /* FSTLevelDBMutationQueueTests.mm in Sources */, + 7E4218DB09B85F8E379C73CB /* FSTLevelDBQueryCacheTests.mm in Sources */, + 4C10843309CD11C455CF3B2B /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */, + 7495E3BAE536CD839EE20F31 /* FSTLevelDBSpecTests.mm in Sources */, + 59D1E0A722CE68E00A3F85AA /* FSTLevelDBTransactionTests.mm in Sources */, + 023829DB2198383927233318 /* FSTLocalSerializerTests.mm in Sources */, + 9328C93759C78A10FDBF68E0 /* FSTLocalStoreTests.mm in Sources */, + 29FDE0C0BA643E3804D8546C /* FSTMemoryLRUGarbageCollectorTests.mm in Sources */, + 3B47E82ED2A3C59AB5002640 /* FSTMemoryLocalStoreTests.mm in Sources */, + 1DFAEEE901B4E517A4CB9CAD /* FSTMemoryMutationQueueTests.mm in Sources */, + 4B52774AABF4ED7C2FA5C1C5 /* FSTMemoryQueryCacheTests.mm in Sources */, + 4247980BACA0070FB3E4A7A3 /* FSTMemoryRemoteDocumentCacheTests.mm in Sources */, + EB04FE18E5794FEC187A09E3 /* FSTMemorySpecTests.mm in Sources */, + 31D8E3D925FA3F70AA20ACCE /* FSTMockDatastore.mm in Sources */, + 239B9B357E67036BEA831E3A /* FSTMutationQueueTests.mm in Sources */, + 25CD471A28606A0DEE9F454A /* FSTMutationTests.mm in Sources */, + A6D29E15ED1221352DBE0CF2 /* FSTPersistenceTestHelpers.mm in Sources */, + BBFCCD960DD2937EE278D7B6 /* FSTQueryCacheTests.mm in Sources */, + 300D9D215F4128E69068B863 /* FSTQueryListenerTests.mm in Sources */, + CA18CEF2585A6BC4974DB56D /* FSTQueryTests.mm in Sources */, + 9664E5831CE35D515CDBC12A /* FSTRemoteDocumentCacheTests.mm in Sources */, + 61D1EB3438B92F61F6CAC191 /* FSTRemoteEventTests.mm in Sources */, + F58A4EE0A1A77F61EF41E5ED /* FSTSerializerBetaTests.mm in Sources */, + D5E9954FC1C5ABBC7A180B33 /* FSTSpecTests.mm in Sources */, + D69B97FF4C065EACEDD91886 /* FSTSyncEngineTestDriver.mm in Sources */, + 406939B62E5A6A22ADAB6FE6 /* FSTTreeSortedDictionaryTests.m in Sources */, + 4BB325E8B87A2FA4483AA070 /* FSTViewSnapshotTest.mm in Sources */, + 0265CCC8BBB76AE013F52411 /* FSTViewTests.mm in Sources */, + AAC15E7CCAE79619B2ABB972 /* XCTestCase+Await.mm in Sources */, + 1C19D796DB6715368407387A /* annotations.pb.cc in Sources */, + 6EEA00A737690EF82A3C91C6 /* app_testing.mm in Sources */, + 1291D9F5300AFACD1FBD262D /* array_sorted_map_test.cc in Sources */, + 4AD9809C9CE9FA09AC40992F /* async_queue_libdispatch_test.mm in Sources */, + 38208AC761FF994BA69822BE /* async_queue_std_test.cc in Sources */, + 900D0E9F18CE3DB954DD0D1E /* async_queue_test.cc in Sources */, + 5D5E24E3FA1128145AA117D2 /* autoid_test.cc in Sources */, + B6FDE6F91D3F81D045E962A0 /* bits_test.cc in Sources */, + 18638EAED9E126FC5D895B14 /* common.pb.cc in Sources */, + 1115DB1F1DCE93B63E03BA8C /* comparison_test.cc in Sources */, + 169D01E6FF2CDF994B32B491 /* create_noop_connectivity_monitor.cc in Sources */, + 5686B35D611C1CFF6BFE7215 /* credentials_provider_test.cc in Sources */, + 58E377DCCC64FE7D2C6B59A1 /* database_id_test.cc in Sources */, + 8F3AE423677A4C50F7E0E5C0 /* database_info_test.cc in Sources */, + CEDDC6DB782989587D0139B2 /* datastore_test.mm in Sources */, + D22B96C19A0F3DE998D4320C /* delayed_constructor_test.cc in Sources */, + 25A75DFA730BAD21A5538EC5 /* document.pb.cc in Sources */, + D6E0E54CD1640E726900828A /* document_key_test.cc in Sources */, + 07A64E6C4EB700E3AF3FD496 /* document_test.cc in Sources */, + 2B1E95FAFD350C191B525F3B /* empty_credentials_provider_test.cc in Sources */, + B220E091D8F4E6DE1EA44F57 /* executor_libdispatch_test.mm in Sources */, + BAB43C839445782040657239 /* executor_std_test.cc in Sources */, + 3A7CB01751697ED599F2D9A1 /* executor_test.cc in Sources */, + EF3518F84255BAF3EBD317F6 /* exponential_backoff_test.cc in Sources */, + 4E8085FB9DBE40BAE11F0F4E /* fake_credentials_provider.cc in Sources */, + ED4E2AC80CAF2A8FDDAC3DEE /* field_mask_test.cc in Sources */, + 41EAC526C543064B8F3F7EDA /* field_path_test.cc in Sources */, + 4B57AE178F715ADE738C4F78 /* field_transform_test.mm in Sources */, + E4EEF6AAFCD33303CE9E5408 /* field_value_test.cc in Sources */, + AAF2F02E77A80C9CDE2C0C7A /* filesystem_test.cc in Sources */, + DAC43DD1FDFBAB1FE1AD6BE5 /* firebase_credentials_provider_test.mm in Sources */, + 8683BBC3AC7B01937606A83B /* firestore.pb.cc in Sources */, + F7718C43D3A8FCCDB4BB0071 /* geo_point_test.cc in Sources */, + BA9A65BD6D993B2801A3C768 /* grpc_connection_test.cc in Sources */, + D6DE74259F5C0CCA010D6A0D /* grpc_stream_test.cc in Sources */, + 7BBE0389D855242DDB83334B /* grpc_stream_tester.cc in Sources */, + 804B0C6CCE3933CF3948F249 /* grpc_streaming_reader_test.cc in Sources */, + 8612F3C7E4A7D17221442699 /* grpc_unary_call_test.cc in Sources */, + E0E640226A1439C59BBBA9C1 /* hard_assert_test.cc in Sources */, + 227CFA0B2A01884C277E4F1D /* hashing_test.cc in Sources */, + CD78EEAA1CD36BE691CA3427 /* hashing_test_apple.mm in Sources */, + 1357806B4CD3A62A8F5DE86D /* http.pb.cc in Sources */, + 20EC3A46525B8C3471D7D179 /* index_manager_test.mm in Sources */, + 0E4C94369FFF7EC0C9229752 /* iterator_adaptors_test.cc in Sources */, + 0FBDD5991E8F6CD5F8542474 /* latlng.pb.cc in Sources */, + A64B1CD2776BC118C74503A7 /* leveldb_index_manager_test.mm in Sources */, + B513F723728E923DFF34F60F /* leveldb_key_test.cc in Sources */, + 7A3BE0ED54933C234FDE23D1 /* leveldb_util_test.cc in Sources */, + 0FA4D5601BE9F0CB5EC2882C /* local_serializer_test.cc in Sources */, + 12BB9ED1CA98AA52B92F497B /* log_test.cc in Sources */, + 88FD82A1FC5FEC5D56B481D8 /* maybe_document.pb.cc in Sources */, + 01C0A2CF788A93EF2CEB6100 /* memory_index_manager_test.mm in Sources */, + 153F3E4E9E3A0174E29550B4 /* mutation.pb.cc in Sources */, + 5E6F9184B271F6D5312412FF /* mutation_test.cc in Sources */, + 78E38BEDF502B85E27D50C3B /* nanopb_string_test.cc in Sources */, + FEF55ECFB0CA317B351179AB /* no_document_test.cc in Sources */, + 7400AC9377419A28B782B5EC /* objc_compatibility_apple_test.mm in Sources */, + FD8EA96A604E837092ACA51D /* ordered_code_test.cc in Sources */, + 0963F6D7B0F9AE1E24B82866 /* path_test.cc in Sources */, + 152543FD706D5E8851C8DA92 /* precondition_test.cc in Sources */, + 5FA3DB52A478B01384D3A2ED /* query.pb.cc in Sources */, + F481368DB694B3B4D0C8E4A2 /* query_test.cc in Sources */, + 7DBE7DB90CF83B589A94980F /* reference_set_test.cc in Sources */, + 85B8918FC8C5DC62482E39C3 /* resource_path_test.cc in Sources */, + A8C9FF6D13E6C83D4AB54EA7 /* secure_random_test.cc in Sources */, + 31A396C81A107D1DEFDF4A34 /* serializer_test.cc in Sources */, + 13D8F4196528BAB19DBB18A7 /* snapshot_version_test.cc in Sources */, + 86E6FC2B7657C35B342E1436 /* sorted_map_test.cc in Sources */, + 8413BD9958F6DD52C466D70F /* sorted_set_test.cc in Sources */, + 0D2D25522A94AA8195907870 /* status.pb.cc in Sources */, + C0AD8DB5A84CAAEE36230899 /* status_test.cc in Sources */, + DC48407370E87F2233D7AB7E /* statusor_test.cc in Sources */, + 215643858470A449D3A3E168 /* stream_test.mm in Sources */, + 69ED7BC38B3F981DE91E7933 /* strerror_test.cc in Sources */, + 0087625FD31D76E1365C589E /* string_apple_test.mm in Sources */, + 7A7EC216A0015D7620B4FF3E /* string_format_apple_test.mm in Sources */, + 392F527F144BADDAC69C5485 /* string_format_test.cc in Sources */, + E50187548B537DBCDBF7F9F0 /* string_util_test.cc in Sources */, + 81D1B1D2B66BD8310AC5707F /* string_win_test.cc in Sources */, + 81B23D2D4E061074958AF12F /* target.pb.cc in Sources */, + DA4303684707606318E1914D /* target_id_generator_test.cc in Sources */, + 8388418F43042605FB9BFB92 /* testutil.cc in Sources */, + 26CB3D7C871BC56456C6021E /* timestamp_test.cc in Sources */, + 5BE49546D57C43DDFCDB6FBD /* to_string_apple_test.mm in Sources */, + E500AB82DF2E7F3AFDB1AB3F /* to_string_test.cc in Sources */, + 2F6E23D7888FC82475C63010 /* token_test.cc in Sources */, + 5C7FAF228D0F52CFFE9E41B5 /* transform_operations_test.mm in Sources */, + 627253FDEC6BB5549FE77F4E /* tree_sorted_map_test.cc in Sources */, + 9AC28D928902C6767A11F5FC /* type_traits_apple_test.mm in Sources */, + 596C782EFB68131380F8EEF8 /* user_test.cc in Sources */, + 178FE1E277C63B3E7120BE56 /* watch_change_test.mm in Sources */, + A5AB1815C45FFC762981E481 /* write.pb.cc in Sources */, + 6EB896CD1B64A60E6C82D8CC /* xcgmock_test.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 54AA33B0224C0035006CE580 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 95ED06D2B0078D3CDB821B68 /* FIRArrayTransformTests.mm in Sources */, + EC160876D8A42166440E0B53 /* FIRCursorTests.mm in Sources */, + EA38690795FBAA182A9AA63E /* FIRDatabaseTests.mm in Sources */, + 60985657831B8DDE2C65AC8B /* FIRFieldsTests.mm in Sources */, + BC2D0A8EA272A0058F6C2B9E /* FIRFirestoreSourceTests.mm in Sources */, + F19B749671F2552E964422F7 /* FIRListenerRegistrationTests.mm in Sources */, + 08F44F7DF9A3EF0D35C8FB57 /* FIRNumericTransformTests.mm in Sources */, + 9A29D572C64CA1FA62F591D4 /* FIRQueryTests.mm in Sources */, + FA7837C5CDFB273DE447E447 /* FIRServerTimestampTests.mm in Sources */, + 75D124966E727829A5F99249 /* FIRTypeTests.mm in Sources */, + 12DB753599571E24DCED0C2C /* FIRValidationTests.mm in Sources */, + BC0C98A9201E8F98B9A176A9 /* FIRWriteBatchTests.mm in Sources */, + A4ECA8335000CBDF94586C94 /* FSTDatastoreTests.mm in Sources */, + 2E169CF1E9E499F054BB873A /* FSTEventAccumulator.mm in Sources */, + 086E10B1B37666FB746D56BC /* FSTHelpers.mm in Sources */, + 02C953A7B0FA5EF87DB0361A /* FSTIntegrationTestCase.mm in Sources */, + 3D9619906F09108E34FF0C95 /* FSTSmokeTests.mm in Sources */, + D77941FD93DBE862AEF1F623 /* FSTTransactionTests.mm in Sources */, + 4D42E5C756229C08560DD731 /* XCTestCase+Await.mm in Sources */, + 7B8D7BAC1A075DB773230505 /* app_testing.mm in Sources */, + 409C0F2BFC2E1BECFFAC4D32 /* testutil.cc in Sources */, + 1EF47EF3D03B0847007C2318 /* xcgmock_test.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 54B8E4A6224BDC4100930F18 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2797,6 +3542,16 @@ target = DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */; targetProxy = 544AB1972248072200F851E6 /* PBXContainerItemProxy */; }; + 54AA33AC224BFE0A006CE580 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 54AA338E224BF935006CE580 /* Firestore_Example_tvOS */; + targetProxy = 54AA33AB224BFE0A006CE580 /* PBXContainerItemProxy */; + }; + 54AA33BA224C0035006CE580 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 54AA338E224BF935006CE580 /* Firestore_Example_tvOS */; + targetProxy = 54AA33B9224C0035006CE580 /* PBXContainerItemProxy */; + }; 54B8E4B0224BDC4100930F18 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DAFF0CF421E64AC30062958F /* Firestore_Example_macOS */; @@ -2845,6 +3600,14 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 54AA3397224BF935006CE580 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 54AA3398224BF935006CE580 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; 6003F596195388D20070C39A /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( @@ -3030,36 +3793,367 @@ }; name = Release; }; - 54B8E4B1224BDC4100930F18 /* Debug */ = { + 54AA339F224BF936006CE580 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2F901F31BC62444A476B779F /* Pods-Firestore_IntegrationTests_macOS.debug.xcconfig */; + baseConfigurationReference = A70E82DD627B162BEF92B8ED /* Pods-Firestore_Example_tvOS.debug.xcconfig */; buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - COMBINE_HIDPI_IMAGES = YES; + CODE_SIGN_STYLE = Automatic; DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = c99; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "\"${PODS_ROOT}/../../..\"", - "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", - "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", - "\"${PODS_ROOT}/GoogleTest/googletest/include\"", - "\"${PODS_ROOT}/leveldb-library/include\"", - ); - INFOPLIST_FILE = "Tests/Tests-Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + INFOPLIST_FILE = "$(SRCROOT)/App/tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - OTHER_LDFLAGS = ( - "$(inherited)", + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-Example-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Debug; + }; + 54AA33A0224BF936006CE580 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FC738525340E594EBFAB121E /* Pods-Firestore_Example_tvOS.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + INFOPLIST_FILE = "$(SRCROOT)/App/tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-Example-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Release; + }; + 54AA33AD224BFE0A006CE580 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2E48431B0EDA400BEA91D4AB /* Pods-Firestore_Tests_tvOS.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/ProtobufCpp/src\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"z\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"GTMSessionFetcher\"", + "-framework", + "\"GoogleTest\"", + "-framework", + "\"GoogleUtilities\"", + "-framework", + "\"OCMock\"", + "-framework", + "\"Protobuf\"", + "-framework", + "\"ProtobufCpp\"", + "-framework", + "\"Security\"", + "-framework", + "\"grpc\"", + "-framework", + "\"grpcpp\"", + "-framework", + "\"leveldb\"", + "-framework", + "\"nanopb\"", + "-framework", + "\"openssl_grpc\"", + "-framework", + "\"FirebaseFirestore\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-Tests-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_tvOS.app/Firestore_Example_tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Debug; + }; + 54AA33AE224BFE0A006CE580 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6AE927CDFC7A72BF825BE4CB /* Pods-Firestore_Tests_tvOS.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/ProtobufCpp/src\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"z\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"GTMSessionFetcher\"", + "-framework", + "\"GoogleTest\"", + "-framework", + "\"GoogleUtilities\"", + "-framework", + "\"OCMock\"", + "-framework", + "\"Protobuf\"", + "-framework", + "\"ProtobufCpp\"", + "-framework", + "\"Security\"", + "-framework", + "\"grpc\"", + "-framework", + "\"grpcpp\"", + "-framework", + "\"leveldb\"", + "-framework", + "\"nanopb\"", + "-framework", + "\"openssl_grpc\"", + "-framework", + "\"FirebaseFirestore\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-Tests-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_tvOS.app/Firestore_Example_tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Release; + }; + 54AA33BC224C0035006CE580 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 74AC2ADBF1BAD9A8EF30CF41 /* Pods-Firestore_IntegrationTests_tvOS.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"z\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"GTMSessionFetcher\"", + "-framework", + "\"GoogleTest\"", + "-framework", + "\"GoogleUtilities\"", + "-framework", + "\"OCMock\"", + "-framework", + "\"Protobuf\"", + "-framework", + "\"Security\"", + "-framework", + "\"grpc\"", + "-framework", + "\"grpcpp\"", + "-framework", + "\"leveldb\"", + "-framework", + "\"nanopb\"", + "-framework", + "\"openssl_grpc\"", + "-framework", + "\"FirebaseFirestore\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-IntegrationTests-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_tvOS.app/Firestore_Example_tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Debug; + }; + 54AA33BD224C0035006CE580 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 36D235D9F1240D5195CDB670 /* Pods-Firestore_IntegrationTests_tvOS.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"z\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"GTMSessionFetcher\"", + "-framework", + "\"GoogleTest\"", + "-framework", + "\"GoogleUtilities\"", + "-framework", + "\"OCMock\"", + "-framework", + "\"Protobuf\"", + "-framework", + "\"Security\"", + "-framework", + "\"grpc\"", + "-framework", + "\"grpcpp\"", + "-framework", + "\"leveldb\"", + "-framework", + "\"nanopb\"", + "-framework", + "\"openssl_grpc\"", + "-framework", + "\"FirebaseFirestore\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-IntegrationTests-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_tvOS.app/Firestore_Example_tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Release; + }; + 54B8E4B1224BDC4100930F18 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2F901F31BC62444A476B779F /* Pods-Firestore_IntegrationTests_macOS.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = c99; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", "-l\"c++\"", "-l\"z\"", "-framework", @@ -4016,6 +5110,33 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 54AA33A1224BF936006CE580 /* Build configuration list for PBXNativeTarget "Firestore_Example_tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 54AA339F224BF936006CE580 /* Debug */, + 54AA33A0224BF936006CE580 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 54AA33AF224BFE0A006CE580 /* Build configuration list for PBXNativeTarget "Firestore_Tests_tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 54AA33AD224BFE0A006CE580 /* Debug */, + 54AA33AE224BFE0A006CE580 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 54AA33BB224C0035006CE580 /* Build configuration list for PBXNativeTarget "Firestore_IntegrationTests_tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 54AA33BC224C0035006CE580 /* Debug */, + 54AA33BD224C0035006CE580 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 54B8E4B3224BDC4100930F18 /* Build configuration list for PBXNativeTarget "Firestore_IntegrationTests_macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_tvOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_tvOS.xcscheme new file mode 100644 index 00000000000..528621459ce --- /dev/null +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Example_tvOS.xcscheme @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_tvOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_tvOS.xcscheme new file mode 100644 index 00000000000..3a921c69dde --- /dev/null +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_tvOS.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_tvOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_tvOS.xcscheme new file mode 100644 index 00000000000..e33360ffaa2 --- /dev/null +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_tvOS.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Firestore/Example/GoogleBenchmark.podspec b/Firestore/Example/GoogleBenchmark.podspec index 62f4d5c59e5..156ee2aface 100644 --- a/Firestore/Example/GoogleBenchmark.podspec +++ b/Firestore/Example/GoogleBenchmark.podspec @@ -35,6 +35,7 @@ Google's C++ benchmark framework. s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' + s.tvos.deployment_target = '10.0' s.requires_arc = false diff --git a/Firestore/Example/GoogleTest.podspec b/Firestore/Example/GoogleTest.podspec index 63dce9b2321..784f69e63c6 100644 --- a/Firestore/Example/GoogleTest.podspec +++ b/Firestore/Example/GoogleTest.podspec @@ -35,6 +35,7 @@ Google's C++ test framework. s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' + s.tvos.deployment_target = '10.0' s.requires_arc = false diff --git a/Firestore/Example/LibFuzzer.podspec b/Firestore/Example/LibFuzzer.podspec index 528d73b1410..ae99f46c62b 100644 --- a/Firestore/Example/LibFuzzer.podspec +++ b/Firestore/Example/LibFuzzer.podspec @@ -32,6 +32,7 @@ Pod::Spec.new do |s| # iOS 9. s.ios.deployment_target = '9.0' s.osx.deployment_target = '10.10' + s.tvos.deployment_target = '10.0' # Check out only libFuzzer folder. s.source = { diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index f6a632ea258..bb5ffb0c7cd 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -86,3 +86,30 @@ target 'Firestore_Example_macOS' do pod 'leveldb-library' end end + +target 'Firestore_Example_tvOS' do + platform :tvos, '10.0' + + pod 'FirebaseAuth', :path => '../../' + pod 'FirebaseCore', :path => '../../' + pod 'FirebaseFirestore', :path => '../../' + + target 'Firestore_Tests_tvOS' do + inherit! :search_paths + + pod 'GoogleTest', :podspec => 'GoogleTest.podspec' + pod 'ProtobufCpp', :podspec => 'ProtobufCpp.podspec' + + pod 'OCMock' + pod 'leveldb-library' + end + + target 'Firestore_IntegrationTests_tvOS' do + inherit! :search_paths + + pod 'GoogleTest', :podspec => 'GoogleTest.podspec' + + pod 'OCMock' + pod 'leveldb-library' + end +end diff --git a/Firestore/Example/ProtobufCpp.podspec b/Firestore/Example/ProtobufCpp.podspec index 921c0b3e55d..e2527bdae7c 100644 --- a/Firestore/Example/ProtobufCpp.podspec +++ b/Firestore/Example/ProtobufCpp.podspec @@ -31,6 +31,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' + s.tvos.deployment_target = '10.0' s.source_files = 'src/**/*.{h,cc}' s.exclude_files = # skip test files. (Yes, the test files are intermixed with diff --git a/README.md b/README.md index ad8de0fa481..907e2a92f75 100644 --- a/README.md +++ b/README.md @@ -170,8 +170,8 @@ participate in the Firebase community. ### macOS and tvOS Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, -FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on macOS and tvOS. -FirebaseFirestore is availiable for macOS and FirebaseMessaging for tvOS. +FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on +macOS and tvOS. FirebaseMessaging is available for tvOS. For tvOS, checkout the [Sample](Example/tvOSSample). diff --git a/scripts/build.sh b/scripts/build.sh index ddba97209df..d59dfa6cd86 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -306,7 +306,7 @@ case "$product-$method-$platform" in build ;; - Firestore-xcodebuild-macOS) + Firestore-xcodebuild-macOS | Firestore-xcodebuild-tvOS) # TODO(wilhuff): Combine with above once all targets exist RunXcodebuild \ -workspace 'Firestore/Example/Firestore.xcworkspace' \ diff --git a/scripts/sync_project.rb b/scripts/sync_project.rb index 8d4aab89f28..fc6c71d693c 100755 --- a/scripts/sync_project.rb +++ b/scripts/sync_project.rb @@ -62,7 +62,7 @@ def sync_firestore() 'SwiftTests', ] - ['iOS', 'macOS'].each do |platform| + ['iOS', 'macOS', 'tvOS'].each do |platform| s.target "Firestore_Tests_#{platform}" do |t| t.source_files = [ 'Firestore/Example/Tests/**', @@ -80,7 +80,7 @@ def sync_firestore() end end - ['iOS', 'macOS'].each do |platform| + ['iOS', 'macOS', 'tvOS'].each do |platform| s.target "Firestore_IntegrationTests_#{platform}" do |t| t.source_files = [ 'Firestore/Example/Tests/Integration/**', From 2e8b9181fa09d863def7cc876fbdd47715000af7 Mon Sep 17 00:00:00 2001 From: Gil Date: Thu, 28 Mar 2019 13:19:28 -0700 Subject: [PATCH 114/214] Port FSTUsageValidation to C++ (#2670) * Port FSTUsageValidation to C++ * Add an error log level --- FirebaseFirestore.podspec | 1 + .../Source/API/FIRCollectionReference.mm | 11 +- Firestore/Source/API/FIRDocumentReference.mm | 11 +- Firestore/Source/API/FIRDocumentSnapshot.mm | 5 +- Firestore/Source/API/FIRFieldPath.mm | 17 +- Firestore/Source/API/FIRFirestore.mm | 38 ++--- Firestore/Source/API/FIRFirestoreSettings.mm | 24 +-- Firestore/Source/API/FIRGeoPoint.mm | 18 +-- Firestore/Source/API/FIRQuery.mm | 151 ++++++++---------- Firestore/Source/API/FIRQuerySnapshot.mm | 8 +- Firestore/Source/API/FIRTransaction.mm | 5 +- Firestore/Source/API/FIRWriteBatch.mm | 9 +- Firestore/Source/API/FSTFirestoreComponent.mm | 5 +- Firestore/Source/API/FSTUserDataConverter.mm | 60 ++++--- Firestore/Source/Core/FSTQuery.mm | 10 +- Firestore/Source/Util/FSTUsageValidation.h | 45 ------ Firestore/Source/Util/FSTUsageValidation.mm | 30 ---- .../src/firebase/firestore/api/CMakeLists.txt | 25 +++ .../firestore/api/document_reference.mm | 1 - .../firebase/firestore/api/input_validation.h | 80 ++++++++++ .../firestore/api/input_validation_apple.mm | 49 ++++++ .../firestore/api/input_validation_std.cc | 50 ++++++ .../firebase/firestore/api/query_snapshot.mm | 1 - .../src/firebase/firestore/core/user_data.mm | 12 +- .../core/src/firebase/firestore/util/log.h | 17 ++ .../src/firebase/firestore/util/log_apple.mm | 4 +- .../src/firebase/firestore/util/log_stdio.cc | 3 + 27 files changed, 411 insertions(+), 279 deletions(-) delete mode 100644 Firestore/Source/Util/FSTUsageValidation.h delete mode 100644 Firestore/Source/Util/FSTUsageValidation.mm create mode 100644 Firestore/core/src/firebase/firestore/api/input_validation.h create mode 100644 Firestore/core/src/firebase/firestore/api/input_validation_apple.mm create mode 100644 Firestore/core/src/firebase/firestore/api/input_validation_std.cc diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index f1981baed3f..6204e0aec6f 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -41,6 +41,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, 'Firestore/third_party/Immutable/Tests/**', # Exclude alternate implementations for other platforms + 'Firestore/core/src/firebase/firestore/api/input_validation_std.cc', 'Firestore/core/src/firebase/firestore/remote/connectivity_monitor_noop.cc', 'Firestore/core/src/firebase/firestore/remote/grpc_root_certificate_finder_generated.cc', 'Firestore/core/src/firebase/firestore/util/filesystem_win.cc', diff --git a/Firestore/Source/API/FIRCollectionReference.mm b/Firestore/Source/API/FIRCollectionReference.mm index 3156ef42a63..9f2bdc2ee0f 100644 --- a/Firestore/Source/API/FIRCollectionReference.mm +++ b/Firestore/Source/API/FIRCollectionReference.mm @@ -25,13 +25,14 @@ #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/API/FIRQuery_Init.h" #import "Firestore/Source/Core/FSTQuery.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/model/document_key.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::api::ThrowInvalidArgument; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::ResourcePath; using firebase::firestore::util::CreateAutoId; @@ -58,9 +59,9 @@ @implementation FIRCollectionReference - (instancetype)initWithPath:(const ResourcePath &)path firestore:(FIRFirestore *)firestore { if (path.size() % 2 != 1) { - FSTThrowInvalidArgument(@"Invalid collection reference. Collection references must have an odd " - "number of segments, but %s has %zu", - path.CanonicalString().c_str(), path.size()); + ThrowInvalidArgument("Invalid collection reference. Collection references must have an odd " + "number of segments, but %s has %s", + path.CanonicalString(), path.size()); } self = [super initWithQuery:[FSTQuery queryWithPath:path] firestore:firestore]; return self; @@ -112,7 +113,7 @@ - (NSString *)path { - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath { if (!documentPath) { - FSTThrowInvalidArgument(@"Document path cannot be nil."); + ThrowInvalidArgument("Document path cannot be nil."); } const ResourcePath subPath = ResourcePath::FromString(util::MakeString(documentPath)); ResourcePath path = self.query.path.Append(subPath); diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index c1e838fa6b6..501c94665b6 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -30,10 +30,10 @@ #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTFieldValue.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/api/document_reference.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/core/event_listener.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" @@ -49,6 +49,7 @@ using firebase::firestore::api::DocumentReference; using firebase::firestore::api::DocumentSnapshot; using firebase::firestore::api::Firestore; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::EventListener; using firebase::firestore::core::ListenOptions; using firebase::firestore::core::ParsedSetData; @@ -77,9 +78,9 @@ - (instancetype)initWithReference:(DocumentReference &&)reference { - (instancetype)initWithPath:(ResourcePath)path firestore:(Firestore *)firestore { if (path.size() % 2 != 0) { - FSTThrowInvalidArgument(@"Invalid document reference. Document references must have an even " - "number of segments, but %s has %zu", - path.CanonicalString().c_str(), path.size()); + ThrowInvalidArgument("Invalid document reference. Document references must have an even " + "number of segments, but %s has %s", + path.CanonicalString(), path.size()); } return [self initWithKey:DocumentKey{std::move(path)} firestore:firestore]; } @@ -125,7 +126,7 @@ - (NSString *)path { - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath { if (!collectionPath) { - FSTThrowInvalidArgument(@"Collection path cannot be nil."); + ThrowInvalidArgument("Collection path cannot be nil."); } ResourcePath subPath = ResourcePath::FromString(util::MakeString(collectionPath)); diff --git a/Firestore/Source/API/FIRDocumentSnapshot.mm b/Firestore/Source/API/FIRDocumentSnapshot.mm index 8597e7edd49..ea9eb9d422a 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot.mm +++ b/Firestore/Source/API/FIRDocumentSnapshot.mm @@ -28,10 +28,10 @@ #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTFieldValue.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/api/firestore.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #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_value.h" @@ -41,6 +41,7 @@ namespace util = firebase::firestore::util; using firebase::firestore::api::DocumentSnapshot; using firebase::firestore::api::Firestore; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldValue; @@ -163,7 +164,7 @@ - (nullable id)valueForField:(id)field } else if ([field isKindOfClass:[FIRFieldPath class]]) { fieldPath = field; } else { - FSTThrowInvalidArgument(@"Subscript key must be an NSString or FIRFieldPath."); + ThrowInvalidArgument("Subscript key must be an NSString or FIRFieldPath."); } FSTFieldValue *fieldValue = _snapshot.GetValue(fieldPath.internalValue); diff --git a/Firestore/Source/API/FIRFieldPath.mm b/Firestore/Source/API/FIRFieldPath.mm index 68f3de105b7..9ffbc3ea60f 100644 --- a/Firestore/Source/API/FIRFieldPath.mm +++ b/Firestore/Source/API/FIRFieldPath.mm @@ -22,13 +22,14 @@ #include #import "Firestore/Source/API/FIRFieldPath+Internal.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::model::FieldPath; NS_ASSUME_NONNULL_BEGIN @@ -44,14 +45,14 @@ @implementation FIRFieldPath - (instancetype)initWithFields:(NSArray *)fieldNames { if (fieldNames.count == 0) { - FSTThrowInvalidArgument(@"Invalid field path. Provided names must not be empty."); + ThrowInvalidArgument("Invalid field path. Provided names must not be empty."); } std::vector field_names; field_names.reserve(fieldNames.count); for (int i = 0; i < fieldNames.count; ++i) { if (fieldNames[i].length == 0) { - FSTThrowInvalidArgument(@"Invalid field name at index %d. Field names must not be empty.", i); + ThrowInvalidArgument("Invalid field name at index %s. Field names must not be empty.", i); } field_names.emplace_back(util::MakeString(fieldNames[i])); } @@ -75,15 +76,15 @@ + (instancetype)pathWithDotSeparatedString:(NSString *)path { numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)] > 0) { - FSTThrowInvalidArgument( - @"Invalid field path (%@). Paths must not contain '~', '*', '/', '[', or ']'", path); + ThrowInvalidArgument( + "Invalid field path (%s). Paths must not contain '~', '*', '/', '[', or ']'", path); } @try { return [[FIRFieldPath alloc] initWithFields:[path componentsSeparatedByString:@"."]]; } @catch (NSException *exception) { - FSTThrowInvalidArgument( - @"Invalid field path (%@). Paths must not be empty, begin with '.', end with '.', or " - @"contain '..'", + ThrowInvalidArgument( + "Invalid field path (%s). Paths must not be empty, begin with '.', end with '.', or " + "contain '..'", path); } } diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index e4761b1f7ba..094db978a3e 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -32,9 +32,9 @@ #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FSTFirestoreComponent.h" #import "Firestore/Source/API/FSTUserDataConverter.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/api/firestore.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" @@ -46,6 +46,8 @@ namespace util = firebase::firestore::util; using firebase::firestore::api::DocumentReference; using firebase::firestore::api::Firestore; +using firebase::firestore::api::ThrowIllegalState; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::model::DatabaseId; using firebase::firestore::util::AsyncQueue; @@ -109,9 +111,8 @@ + (void)initialize { + (instancetype)firestore { FIRApp *app = [FIRApp defaultApp]; if (!app) { - FSTThrowInvalidUsage(@"FIRAppNotConfiguredException", - @"Failed to get FirebaseApp instance. Please call FirebaseApp.configure() " - @"before using Firestore"); + ThrowIllegalState("Failed to get FirebaseApp instance. Please call FirebaseApp.configure() " + "before using Firestore"); } return [self firestoreForApp:app database:util::WrapNSString(DatabaseId::kDefault)]; } @@ -123,13 +124,13 @@ + (instancetype)firestoreForApp:(FIRApp *)app { // TODO(b/62410906): make this public + (instancetype)firestoreForApp:(FIRApp *)app database:(NSString *)database { if (!app) { - FSTThrowInvalidArgument(@"FirebaseApp instance may not be nil. Use FirebaseApp.app() if you'd " - "like to use the default FirebaseApp instance."); + ThrowInvalidArgument("FirebaseApp instance may not be nil. Use FirebaseApp.app() if you'd like " + "to use the default FirebaseApp instance."); } if (!database) { - FSTThrowInvalidArgument(@"database identifier may not be nil. Use '%s' if you want the default " - "database", - DatabaseId::kDefault); + ThrowInvalidArgument("Database identifier may not be nil. Use '%s' if you want the default " + "database", + DatabaseId::kDefault); } id provider = @@ -182,11 +183,10 @@ - (FSTFirestoreClient *)client { - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath { if (!collectionPath) { - FSTThrowInvalidArgument(@"Collection path cannot be nil."); + ThrowInvalidArgument("Collection path cannot be nil."); } if ([collectionPath containsString:@"//"]) { - FSTThrowInvalidArgument(@"Invalid path (%@). Paths must not contain // in them.", - collectionPath); + ThrowInvalidArgument("Invalid path (%s). Paths must not contain // in them.", collectionPath); } return _firestore->GetCollection(util::MakeString(collectionPath)); @@ -194,10 +194,10 @@ - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath { - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath { if (!documentPath) { - FSTThrowInvalidArgument(@"Document path cannot be nil."); + ThrowInvalidArgument("Document path cannot be nil."); } if ([documentPath containsString:@"//"]) { - FSTThrowInvalidArgument(@"Invalid path (%@). Paths must not contain // in them.", documentPath); + ThrowInvalidArgument("Invalid path (%s). Paths must not contain // in them.", documentPath); } DocumentReference documentReference = _firestore->GetDocument(util::MakeString(documentPath)); @@ -215,9 +215,9 @@ - (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **)) // We wrap the function they provide in order to use internal implementation classes for // transaction, and to run the user callback block on the proper queue. if (!updateBlock) { - FSTThrowInvalidArgument(@"Transaction block cannot be nil."); + ThrowInvalidArgument("Transaction block cannot be nil."); } else if (!completion) { - FSTThrowInvalidArgument(@"Transaction completion block cannot be nil."); + ThrowInvalidArgument("Transaction completion block cannot be nil."); } _firestore->RunTransaction(updateBlock, queue, completion); @@ -275,11 +275,11 @@ + (FIRFirestore *)recoverFromFirestore:(Firestore *)firestore { - (FIRQuery *)collectionGroupWithID:(NSString *)collectionID { if (!collectionID) { - FSTThrowInvalidArgument(@"Collection ID cannot be nil."); + ThrowInvalidArgument("Collection ID cannot be nil."); } if ([collectionID containsString:@"/"]) { - FSTThrowInvalidArgument( - @"Invalid collection ID (%@). Collection IDs must not contain / in them.", collectionID); + ThrowInvalidArgument("Invalid collection ID (%s). Collection IDs must not contain / in them.", + collectionID); } return _firestore->GetCollectionGroup(collectionID); diff --git a/Firestore/Source/API/FIRFirestoreSettings.mm b/Firestore/Source/API/FIRFirestoreSettings.mm index 3dd7dbda29b..5ddf569a63a 100644 --- a/Firestore/Source/API/FIRFirestoreSettings.mm +++ b/Firestore/Source/API/FIRFirestoreSettings.mm @@ -14,14 +14,15 @@ * limitations under the License. */ -#include "Firestore/core/src/firebase/firestore/util/warnings.h" - #import "FIRFirestoreSettings.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" +#include "Firestore/core/src/firebase/firestore/util/warnings.h" NS_ASSUME_NONNULL_BEGIN +using firebase::firestore::api::ThrowInvalidArgument; + static NSString *const kDefaultHost = @"firestore.googleapis.com"; static const BOOL kDefaultSSLEnabled = YES; static const BOOL kDefaultPersistenceEnabled = YES; @@ -88,20 +89,19 @@ - (id)copyWithZone:(nullable NSZone *)zone { - (void)setHost:(NSString *)host { if (!host) { - FSTThrowInvalidArgument( - @"host setting may not be nil. You should generally just use the default value " - "(which is %@)", - kDefaultHost); + ThrowInvalidArgument("Host setting may not be nil. You should generally just use the default " + "value (which is %s)", + kDefaultHost); } _host = [host mutableCopy]; } - (void)setDispatchQueue:(dispatch_queue_t)dispatchQueue { if (!dispatchQueue) { - FSTThrowInvalidArgument( - @"dispatch queue setting may not be nil. Create a new dispatch queue with " - "dispatch_queue_create(\"com.example.MyQueue\", NULL) or just use the default " - "(which is the main queue, returned from dispatch_get_main_queue())"); + ThrowInvalidArgument( + "Dispatch queue setting may not be nil. Create a new dispatch queue with " + "dispatch_queue_create(\"com.example.MyQueue\", NULL) or just use the default (which is " + "the main queue, returned from dispatch_get_main_queue())"); } _dispatchQueue = dispatchQueue; } @@ -109,7 +109,7 @@ - (void)setDispatchQueue:(dispatch_queue_t)dispatchQueue { - (void)setCacheSizeBytes:(int64_t)cacheSizeBytes { if (cacheSizeBytes != kFIRFirestoreCacheSizeUnlimited && cacheSizeBytes < kMinimumCacheSizeBytes) { - FSTThrowInvalidArgument(@"Cache size must be set to at least %i bytes", kMinimumCacheSizeBytes); + ThrowInvalidArgument("Cache size must be set to at least %s bytes", kMinimumCacheSizeBytes); } _cacheSizeBytes = cacheSizeBytes; } diff --git a/Firestore/Source/API/FIRGeoPoint.mm b/Firestore/Source/API/FIRGeoPoint.mm index cfd70fb5507..8e35d539806 100644 --- a/Firestore/Source/API/FIRGeoPoint.mm +++ b/Firestore/Source/API/FIRGeoPoint.mm @@ -16,10 +16,10 @@ #import "Firestore/Source/API/FIRGeoPoint+Internal.h" -#import "Firestore/core/src/firebase/firestore/util/comparison.h" - -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" +#include "Firestore/core/src/firebase/firestore/util/comparison.h" +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::util::DoubleBitwiseEquals; using firebase::firestore::util::DoubleBitwiseHash; using firebase::firestore::util::WrapCompare; @@ -31,14 +31,14 @@ @implementation FIRGeoPoint - (instancetype)initWithLatitude:(double)latitude longitude:(double)longitude { if (self = [super init]) { if (latitude < -90 || latitude > 90 || !isfinite(latitude)) { - FSTThrowInvalidArgument(@"GeoPoint requires a latitude value in the range of [-90, 90], " - "but was %f", - latitude); + ThrowInvalidArgument("GeoPoint requires a latitude value in the range of [-90, 90], " + "but was %s", + latitude); } if (longitude < -180 || longitude > 180 || !isfinite(longitude)) { - FSTThrowInvalidArgument(@"GeoPoint requires a longitude value in the range of [-180, 180], " - "but was %f", - longitude); + ThrowInvalidArgument("GeoPoint requires a longitude value in the range of [-180, 180], " + "but was %s", + longitude); } _latitude = latitude; diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 097ac5ef419..affb6d68e5a 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -38,8 +38,8 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTFieldValue.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.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/field_value.h" @@ -50,6 +50,7 @@ #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::AsyncEventListener; using firebase::firestore::core::EventListener; using firebase::firestore::core::ViewSnapshot; @@ -278,7 +279,7 @@ - (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isGreaterThanOrEqualTo:(i - (FIRQuery *)queryFilteredUsingComparisonPredicate:(NSPredicate *)predicate { NSComparisonPredicate *comparison = (NSComparisonPredicate *)predicate; if (comparison.comparisonPredicateModifier != NSDirectPredicateModifier) { - FSTThrowInvalidArgument(@"Invalid query. Predicate cannot have an aggregate modifier."); + ThrowInvalidArgument("Invalid query. Predicate cannot have an aggregate modifier."); } NSString *path; id value = nil; @@ -317,24 +318,24 @@ - (FIRQuery *)queryFilteredUsingComparisonPredicate:(NSPredicate *)predicate { default:; // Fallback below to throw assertion. } } else { - FSTThrowInvalidArgument( - @"Invalid query. Predicate comparisons must include a key path and a constant."); + ThrowInvalidArgument( + "Invalid query. Predicate comparisons must include a key path and a constant."); } // Fallback cases of unsupported comparison operator. switch (comparison.predicateOperatorType) { case NSCustomSelectorPredicateOperatorType: - FSTThrowInvalidArgument(@"Invalid query. Custom predicate filters are not supported."); + ThrowInvalidArgument("Invalid query. Custom predicate filters are not supported."); break; default: - FSTThrowInvalidArgument(@"Invalid query. Operator type %lu is not supported.", - (unsigned long)comparison.predicateOperatorType); + ThrowInvalidArgument("Invalid query. Operator type %s is not supported.", + comparison.predicateOperatorType); } } - (FIRQuery *)queryFilteredUsingCompoundPredicate:(NSPredicate *)predicate { NSCompoundPredicate *compound = (NSCompoundPredicate *)predicate; if (compound.compoundPredicateType != NSAndPredicateType || compound.subpredicates.count == 0) { - FSTThrowInvalidArgument(@"Invalid query. Only compound queries using AND are supported."); + ThrowInvalidArgument("Invalid query. Only compound queries using AND are supported."); } FIRQuery *query = self; for (NSPredicate *pred in compound.subpredicates) { @@ -352,13 +353,11 @@ - (FIRQuery *)queryFilteredUsingPredicate:(NSPredicate *)predicate { predicateWithBlock:^BOOL(id obj, NSDictionary *bindings) { return true; }] class]]) { - FSTThrowInvalidArgument(@"Invalid query. Block-based predicates are not " - "supported. Please use predicateWithFormat to " - "create predicates instead."); + ThrowInvalidArgument("Invalid query. Block-based predicates are not supported. Please use " + "predicateWithFormat to create predicates instead."); } else { - FSTThrowInvalidArgument(@"Invalid query. Expect comparison or compound of " - "comparison predicate. Please use " - "predicateWithFormat to create predicates."); + ThrowInvalidArgument("Invalid query. Expect comparison or compound of comparison predicate. " + "Please use predicateWithFormat to create predicates."); } } @@ -379,14 +378,12 @@ - (FIRQuery *)queryOrderedByField:(NSString *)field descending:(BOOL)descending - (FIRQuery *)queryOrderedByFieldPath:(FIRFieldPath *)fieldPath descending:(BOOL)descending { [self validateNewOrderByPath:fieldPath.internalValue]; if (self.query.startAt) { - FSTThrowInvalidUsage( - @"InvalidQueryException", - @"Invalid query. You must not specify a starting point before specifying the order by."); + ThrowInvalidArgument( + "Invalid query. You must not specify a starting point before specifying the order by."); } if (self.query.endAt) { - FSTThrowInvalidUsage( - @"InvalidQueryException", - @"Invalid query. You must not specify an ending point before specifying the order by."); + ThrowInvalidArgument( + "Invalid query. You must not specify an ending point before specifying the order by."); } FSTSortOrder *sortOrder = [FSTSortOrder sortOrderWithFieldPath:fieldPath.internalValue ascending:!descending]; @@ -396,8 +393,8 @@ - (FIRQuery *)queryOrderedByFieldPath:(FIRFieldPath *)fieldPath descending:(BOOL - (FIRQuery *)queryLimitedTo:(NSInteger)limit { if (limit <= 0) { - FSTThrowInvalidArgument(@"Invalid Query. Query limit (%ld) is invalid. Limit must be positive.", - (long)limit); + ThrowInvalidArgument("Invalid Query. Query limit (%s) is invalid. Limit must be positive.", + limit); } return [FIRQuery referenceWithQuery:[self.query queryBySettingLimit:limit] firestore:_firestore]; } @@ -467,30 +464,27 @@ - (FIRQuery *)queryWithFilterOperator:(FSTRelationFilterOperator)filterOperator FSTFieldValue *fieldValue; if (fieldPath.IsKeyFieldPath()) { if (filterOperator == FSTRelationFilterOperatorArrayContains) { - FSTThrowInvalidArgument( - @"Invalid query. You can't perform arrayContains queries on document ID since document " - "IDs are not arrays."); + ThrowInvalidArgument("Invalid query. You can't perform arrayContains queries on document ID " + "since document IDs are not arrays."); } if ([value isKindOfClass:[NSString class]]) { NSString *documentKey = (NSString *)value; if (documentKey.length == 0) { - FSTThrowInvalidArgument(@"Invalid query. When querying by document ID you must provide " - "a valid document ID, but it was an empty string."); + ThrowInvalidArgument("Invalid query. When querying by document ID you must provide a valid " + "document ID, but it was an empty string."); } if (![self.query isCollectionGroupQuery] && [documentKey containsString:@"/"]) { - FSTThrowInvalidArgument( - @"Invalid query. When querying a collection by document ID you must provide " - "a plain document ID, but '%@' contains a '/' character.", - documentKey); + ThrowInvalidArgument("Invalid query. When querying a collection by document ID you must " + "provide a plain document ID, but '%s' contains a '/' character.", + documentKey); } ResourcePath path = self.query.path.Append(ResourcePath::FromString([documentKey UTF8String])); if (!DocumentKey::IsDocumentKey(path)) { - FSTThrowInvalidArgument( - @"Invalid query. When querying a collection group by document ID, " - "the value provided must result in a valid document path, but '%s' is not because it " - "has an odd number of segments.", - path.CanonicalString().c_str()); + ThrowInvalidArgument("Invalid query. When querying a collection group by document ID, the " + "value provided must result in a valid document path, but '%s' is not " + "because it has an odd number of segments.", + path.CanonicalString()); } fieldValue = [FSTReferenceValue referenceValue:[FSTDocumentKey keyWithDocumentKey:DocumentKey{path}] @@ -500,9 +494,9 @@ - (FIRQuery *)queryWithFilterOperator:(FSTRelationFilterOperator)filterOperator fieldValue = [FSTReferenceValue referenceValue:[FSTDocumentKey keyWithDocumentKey:ref.key] databaseID:self.firestore.databaseID]; } else { - FSTThrowInvalidArgument(@"Invalid query. When querying by document ID you must provide a " - "valid string or DocumentReference, but it was of type: %@", - NSStringFromClass([value class])); + ThrowInvalidArgument("Invalid query. When querying by document ID you must provide a valid " + "string or DocumentReference, but it was of type: %s", + NSStringFromClass([value class])); } } else { fieldValue = [self.firestore.dataConverter parsedQueryValue:value]; @@ -524,12 +518,11 @@ - (void)validateNewRelationFilter:(FSTRelationFilter *)filter { if ([filter isInequality]) { const FieldPath *existingField = [self.query inequalityFilterField]; if (existingField && *existingField != filter.field) { - FSTThrowInvalidUsage( - @"InvalidQueryException", - @"Invalid Query. All where filters with an inequality " - "(lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) must be on the same " - "field. But you have inequality filters on '%s' and '%s'", - existingField->CanonicalString().c_str(), filter.field.CanonicalString().c_str()); + ThrowInvalidArgument( + "Invalid Query. All where filters with an inequality " + "(lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) must be on the same " + "field. But you have inequality filters on '%s' and '%s'", + existingField->CanonicalString(), filter.field.CanonicalString()); } const FieldPath *firstOrderByField = [self.query firstSortOrderField]; @@ -538,8 +531,7 @@ - (void)validateNewRelationFilter:(FSTRelationFilter *)filter { } } else if (filter.filterOperator == FSTRelationFilterOperatorArrayContains) { if ([self.query hasArrayContainsFilter]) { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid Query. Queries only support a single arrayContains filter."); + ThrowInvalidArgument("Invalid Query. Queries only support a single arrayContains filter."); } } } @@ -557,14 +549,12 @@ - (void)validateNewOrderByPath:(const FieldPath &)fieldPath { - (void)validateOrderByField:(const FieldPath &)orderByField matchesInequalityField:(const FieldPath &)inequalityField { if (orderByField != inequalityField) { - FSTThrowInvalidUsage( - @"InvalidQueryException", - @"Invalid query. You have a where filter with an " - "inequality (lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) on field '%s' " - "and so you must also use '%s' as your first queryOrderedBy field, but your first " - "queryOrderedBy is currently on field '%s' instead.", - inequalityField.CanonicalString().c_str(), inequalityField.CanonicalString().c_str(), - orderByField.CanonicalString().c_str()); + ThrowInvalidArgument("Invalid query. You have a where filter with an inequality " + "(lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) on field " + "'%s' and so you must also use '%s' as your first queryOrderedBy field, " + "but your first queryOrderedBy is currently on field '%s' instead.", + inequalityField.CanonicalString(), inequalityField.CanonicalString(), + orderByField.CanonicalString()); } } @@ -580,9 +570,8 @@ - (void)validateOrderByField:(const FieldPath &)orderByField */ - (FSTBound *)boundFromSnapshot:(FIRDocumentSnapshot *)snapshot isBefore:(BOOL)isBefore { if (![snapshot exists]) { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid query. You are trying to start or end a query using a document " - @"that doesn't exist."); + ThrowInvalidArgument("Invalid query. You are trying to start or end a query using a document " + "that doesn't exist."); } FSTDocument *document = snapshot.internalDocument; NSMutableArray *components = [NSMutableArray array]; @@ -601,20 +590,18 @@ - (FSTBound *)boundFromSnapshot:(FIRDocumentSnapshot *)snapshot isBefore:(BOOL)i FSTFieldValue *value = [document fieldForPath:sortOrder.field]; if (value.type == FieldValue::Type::ServerTimestamp) { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid query. You are trying to start or end a query using a " - "document for which the field '%s' is an uncommitted server " - "timestamp. (Since the value of this field is unknown, you cannot " - "start/end a query with it.)", - sortOrder.field.CanonicalString().c_str()); + ThrowInvalidArgument( + "Invalid query. You are trying to start or end a query using a document for which the " + "field '%s' is an uncommitted server timestamp. (Since the value of this field is " + "unknown, you cannot start/end a query with it.)", + sortOrder.field.CanonicalString()); } else if (value != nil) { [components addObject:value]; } else { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid query. You are trying to start or end a query using a " - "document for which the field '%s' (used as the order by) " - "does not exist.", - sortOrder.field.CanonicalString().c_str()); + ThrowInvalidArgument( + "Invalid query. You are trying to start or end a query using a document for which the " + "field '%s' (used as the order by) does not exist.", + sortOrder.field.CanonicalString()); } } } @@ -626,9 +613,8 @@ - (FSTBound *)boundFromFieldValues:(NSArray *)fieldValues isBefore:(BOOL)isB // Use explicit sort order because it has to match the query the user made NSArray *explicitSortOrders = self.query.explicitSortOrders; if (fieldValues.count > explicitSortOrders.count) { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid query. You are trying to start or end a query using more values " - @"than were specified in the order by."); + ThrowInvalidArgument("Invalid query. You are trying to start or end a query using more values " + "than were specified in the order by."); } NSMutableArray *components = [NSMutableArray array]; @@ -636,25 +622,20 @@ - (FSTBound *)boundFromFieldValues:(NSArray *)fieldValues isBefore:(BOOL)isB FSTSortOrder *sortOrder = explicitSortOrders[idx]; if (sortOrder.field == FieldPath::KeyFieldPath()) { if (![rawValue isKindOfClass:[NSString class]]) { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid query. Expected a string for the document ID."); + ThrowInvalidArgument("Invalid query. Expected a string for the document ID."); } NSString *documentID = (NSString *)rawValue; if (![self.query isCollectionGroupQuery] && [documentID containsString:@"/"]) { - FSTThrowInvalidUsage( - @"InvalidQueryException", - @"Invalid query. When querying a collection and ordering by document ID, " - "you must pass a plain document ID, but '%@' contains a slash.", - documentID); + ThrowInvalidArgument("Invalid query. When querying a collection and ordering by document " + "ID, you must pass a plain document ID, but '%s' contains a slash.", + documentID); } ResourcePath path = self.query.path.Append(ResourcePath::FromString([documentID UTF8String])); if (!DocumentKey::IsDocumentKey(path)) { - FSTThrowInvalidUsage( - @"InvalidQueryException", - @"Invalid query. When querying a collection group and ordering by document ID, " - "you must pass a value that results in a valid document path, but '%s' " - "is not because it contains an odd number of segments.", - path.CanonicalString().c_str()); + ThrowInvalidArgument("Invalid query. When querying a collection group and ordering by " + "document ID, you must pass a value that results in a valid document " + "path, but '%s' is not because it contains an odd number of segments.", + path.CanonicalString()); } DocumentKey key{path}; [components diff --git a/Firestore/Source/API/FIRQuerySnapshot.mm b/Firestore/Source/API/FIRQuerySnapshot.mm index 52a4810575c..05a9ad0c29f 100644 --- a/Firestore/Source/API/FIRQuerySnapshot.mm +++ b/Firestore/Source/API/FIRQuerySnapshot.mm @@ -26,14 +26,15 @@ #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h" using firebase::firestore::api::Firestore; using firebase::firestore::api::QuerySnapshot; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::util::DelayedConstructor; @@ -123,9 +124,8 @@ - (NSInteger)count { - (NSArray *)documentChangesWithIncludeMetadataChanges: (BOOL)includeMetadataChanges { if (includeMetadataChanges && _snapshot->view_snapshot().excludes_metadata_changes()) { - FSTThrowInvalidArgument( - @"To include metadata changes with your document changes, you must call " - @"addSnapshotListener(includeMetadataChanges: true)."); + ThrowInvalidArgument("To include metadata changes with your document changes, you must call " + "addSnapshotListener(includeMetadataChanges: true)."); } if (!_documentChanges || _documentChangesIncludeMetadataChanges != includeMetadataChanges) { diff --git a/Firestore/Source/API/FIRTransaction.mm b/Firestore/Source/API/FIRTransaction.mm index b0d0f9fee9f..d00eef48d5e 100644 --- a/Firestore/Source/API/FIRTransaction.mm +++ b/Firestore/Source/API/FIRTransaction.mm @@ -26,13 +26,14 @@ #import "Firestore/Source/API/FIRTransaction+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/core/transaction.h" #include "Firestore/core/src/firebase/firestore/util/error_apple.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/status.h" +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::ParsedSetData; using firebase::firestore::core::ParsedUpdateData; using firebase::firestore::core::Transaction; @@ -171,7 +172,7 @@ - (FIRDocumentSnapshot *_Nullable)getDocument:(FIRDocumentReference *)document - (void)validateReference:(FIRDocumentReference *)reference { if (reference.firestore != self.firestore) { - FSTThrowInvalidArgument(@"Provided document reference is from a different Firestore instance."); + ThrowInvalidArgument("Provided document reference is from a different Firestore instance."); } } diff --git a/Firestore/Source/API/FIRWriteBatch.mm b/Firestore/Source/API/FIRWriteBatch.mm index 484b8b0f4e8..aec381d1bd6 100644 --- a/Firestore/Source/API/FIRWriteBatch.mm +++ b/Firestore/Source/API/FIRWriteBatch.mm @@ -26,10 +26,12 @@ #import "Firestore/Source/API/FSTUserDataConverter.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Model/FSTMutation.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/model/precondition.h" +using firebase::firestore::api::ThrowIllegalState; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::ParsedSetData; using firebase::firestore::core::ParsedUpdateData; using firebase::firestore::model::Precondition; @@ -136,14 +138,13 @@ - (void)commitWithCompletion:(nullable void (^)(NSError *_Nullable error))comple - (void)verifyNotCommitted { if (self.committed) { - FSTThrowInvalidUsage(@"FIRIllegalStateException", - @"A write batch can no longer be used after commit has been called."); + ThrowIllegalState("A write batch can no longer be used after commit has been called."); } } - (void)validateReference:(FIRDocumentReference *)reference { if (reference.firestore != self.firestore) { - FSTThrowInvalidArgument(@"Provided document reference is from a different Firestore instance."); + ThrowInvalidArgument("Provided document reference is from a different Firestore instance."); } } diff --git a/Firestore/Source/API/FSTFirestoreComponent.mm b/Firestore/Source/API/FSTFirestoreComponent.mm index 2d676d96c4d..20df5ddd13f 100644 --- a/Firestore/Source/API/FSTFirestoreComponent.mm +++ b/Firestore/Source/API/FSTFirestoreComponent.mm @@ -29,9 +29,9 @@ #include #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/include/firebase/firestore/firestore_version.h" #include "Firestore/core/src/firebase/firestore/api/firestore.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" @@ -41,6 +41,7 @@ namespace util = firebase::firestore::util; using firebase::firestore::api::Firestore; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::auth::FirebaseCredentialsProvider; using util::AsyncQueue; @@ -73,7 +74,7 @@ - (instancetype)initWithApp:(FIRApp *)app { - (FIRFirestore *)firestoreForDatabase:(NSString *)database { if (!database) { - FSTThrowInvalidArgument(@"database identifier may not be nil."); + ThrowInvalidArgument("Database identifier may not be nil."); } NSString *key = [NSString stringWithFormat:@"%@|%@", self.app.name, database]; diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm index 0f0abd80114..9563a751fcb 100644 --- a/Firestore/Source/API/FSTUserDataConverter.mm +++ b/Firestore/Source/API/FSTUserDataConverter.mm @@ -31,8 +31,8 @@ #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Model/FSTMutation.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/core/user_data.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" @@ -47,6 +47,7 @@ #include "absl/strings/match.h" namespace util = firebase::firestore::util; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::ParsedSetData; using firebase::firestore::core::ParsedUpdateData; using firebase::firestore::core::ParseAccumulator; @@ -110,7 +111,7 @@ - (ParsedSetData)parsedSetData:(id)input { // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust // Obj-C to verify the type for us. if (![input isKindOfClass:[NSDictionary class]]) { - FSTThrowInvalidArgument(@"Data to be written must be an NSDictionary."); + ThrowInvalidArgument("Data to be written must be an NSDictionary."); } ParseAccumulator accumulator{UserDataSource::Set}; @@ -123,7 +124,7 @@ - (ParsedSetData)parsedMergeData:(id)input fieldMask:(nullable NSArray *)fie // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust // Obj-C to verify the type for us. if (![input isKindOfClass:[NSDictionary class]]) { - FSTThrowInvalidArgument(@"Data to be written must be an NSDictionary."); + ThrowInvalidArgument("Data to be written must be an NSDictionary."); } ParseAccumulator accumulator{UserDataSource::MergeSet}; @@ -141,15 +142,14 @@ - (ParsedSetData)parsedMergeData:(id)input fieldMask:(nullable NSArray *)fie } else if ([fieldPath isKindOfClass:[FIRFieldPath class]]) { path = ((FIRFieldPath *)fieldPath).internalValue; } else { - FSTThrowInvalidArgument( - @"All elements in mergeFields: must be NSStrings or FIRFieldPaths."); + ThrowInvalidArgument("All elements in mergeFields: must be NSStrings or FIRFieldPaths."); } // Verify that all elements specified in the field mask are part of the parsed context. if (!accumulator.Contains(path)) { - FSTThrowInvalidArgument( - @"Field '%s' is specified in your field mask but missing from your input data.", - path.CanonicalString().c_str()); + ThrowInvalidArgument( + "Field '%s' is specified in your field mask but missing from your input data.", + path.CanonicalString()); } validatedFieldPaths.insert(path); @@ -166,7 +166,7 @@ - (ParsedUpdateData)parsedUpdateData:(id)input { // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust // Obj-C to verify the type for us. if (![input isKindOfClass:[NSDictionary class]]) { - FSTThrowInvalidArgument(@"Data to be written must be an NSDictionary."); + ThrowInvalidArgument("Data to be written must be an NSDictionary."); } NSDictionary *dict = input; @@ -183,8 +183,7 @@ - (ParsedUpdateData)parsedUpdateData:(id)input { } else if ([key isKindOfClass:[FIRFieldPath class]]) { path = ((FIRFieldPath *)key).internalValue; } else { - FSTThrowInvalidArgument( - @"Dictionary keys in updateData: must be NSStrings or FIRFieldPaths."); + ThrowInvalidArgument("Dictionary keys in updateData: must be NSStrings or FIRFieldPaths."); } value = self.preConverter(value); @@ -247,7 +246,7 @@ - (nullable FSTFieldValue *)parseData:(id)input context:(ParseContext &&)context if ([input isKindOfClass:[NSArray class]]) { // TODO(b/34871131): Include the path containing the array in the error message. if (context.array_element()) { - FSTThrowInvalidArgument(@"Nested arrays are not supported"); + ThrowInvalidArgument("Nested arrays are not supported"); } return [self parseArray:(NSArray *)input context:std::move(context)]; } else { @@ -298,11 +297,11 @@ - (FSTFieldValue *)parseArray:(NSArray *)array context:(ParseContext &&)context - (void)parseSentinelFieldValue:(FIRFieldValue *)fieldValue context:(ParseContext &&)context { // Sentinels are only supported with writes, and not within arrays. if (!context.write()) { - FSTThrowInvalidArgument(@"%@ can only be used with updateData() and setData()%s", - fieldValue.methodName, context.FieldDescription().c_str()); + ThrowInvalidArgument("%s can only be used with updateData() and setData()%s", + fieldValue.methodName, context.FieldDescription()); } if (!context.path()) { - FSTThrowInvalidArgument(@"%@ is not currently supported inside arrays", fieldValue.methodName); + ThrowInvalidArgument("%s is not currently supported inside arrays", fieldValue.methodName); } if ([fieldValue isKindOfClass:[FSTDeleteFieldValue class]]) { @@ -314,15 +313,14 @@ - (void)parseSentinelFieldValue:(FIRFieldValue *)fieldValue context:(ParseContex } else if (context.data_source() == UserDataSource::Update) { HARD_ASSERT(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%s", - context.FieldDescription().c_str()); + ThrowInvalidArgument("FieldValue.delete() can only appear at the top level of your " + "update data%s", + 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 " - @"merge:true%s", - context.FieldDescription().c_str()); + ThrowInvalidArgument( + "FieldValue.delete() can only be used with updateData() and setData() with merge:true%s", + context.FieldDescription()); } } else if ([fieldValue isKindOfClass:[FSTServerTimestampFieldValue class]]) { @@ -400,9 +398,8 @@ - (nullable FSTFieldValue *)parseScalarValue:(nullable id)input context:(ParseCo unsigned long long extended = [input unsignedLongLongValue]; if (extended > LLONG_MAX) { - FSTThrowInvalidArgument(@"NSNumber (%llu) is too large%s", - [input unsignedLongLongValue], - context.FieldDescription().c_str()); + ThrowInvalidArgument("NSNumber (%s) is too large%s", [input unsignedLongLongValue], + context.FieldDescription()); } else { return [FSTIntegerValue integerValue:(int64_t)extended]; @@ -459,18 +456,17 @@ - (nullable FSTFieldValue *)parseScalarValue:(nullable id)input context:(ParseCo FSTDocumentKeyReference *reference = input; if (*reference.databaseID != *self.databaseID) { const DatabaseId *other = reference.databaseID; - FSTThrowInvalidArgument( - @"Document Reference is for database %s/%s but should be for database %s/%s%s", - other->project_id().c_str(), other->database_id().c_str(), - self.databaseID->project_id().c_str(), self.databaseID->database_id().c_str(), - context.FieldDescription().c_str()); + ThrowInvalidArgument( + "Document Reference is for database %s/%s but should be for database %s/%s%s", + other->project_id(), other->database_id(), self.databaseID->project_id(), + self.databaseID->database_id(), context.FieldDescription()); } return [FSTReferenceValue referenceValue:[FSTDocumentKey keyWithDocumentKey:reference.key] databaseID:self.databaseID]; } else { - FSTThrowInvalidArgument(@"Unsupported type: %@%s", NSStringFromClass([input class]), - context.FieldDescription().c_str()); + ThrowInvalidArgument("Unsupported type: %s%s", NSStringFromClass([input class]), + context.FieldDescription()); } } diff --git a/Firestore/Source/Core/FSTQuery.mm b/Firestore/Source/Core/FSTQuery.mm index 9576bee86e5..389b0224c00 100644 --- a/Firestore/Source/Core/FSTQuery.mm +++ b/Firestore/Source/Core/FSTQuery.mm @@ -24,8 +24,8 @@ #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Util/FSTClasses.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.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/field_value.h" @@ -35,6 +35,7 @@ #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldPath; using firebase::firestore::model::FieldValue; @@ -77,15 +78,12 @@ + (instancetype)filterWithField:(const FieldPath &)field value:(FSTFieldValue *)value { if ([value isEqual:[FSTNullValue nullValue]]) { if (op != FSTRelationFilterOperatorEqual) { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid Query. You can only perform equality comparisons on nil / " - "NSNull."); + ThrowInvalidArgument("Invalid Query. Nil and NSNull only support equality comparisons."); } return [[FSTNullFilter alloc] initWithField:field]; } else if ([value isEqual:[FSTDoubleValue nanValue]]) { if (op != FSTRelationFilterOperatorEqual) { - FSTThrowInvalidUsage(@"InvalidQueryException", - @"Invalid Query. You can only perform equality comparisons on NaN."); + ThrowInvalidArgument("Invalid Query. NaN only supports equality comparisons."); } return [[FSTNanFilter alloc] initWithField:field]; } else { diff --git a/Firestore/Source/Util/FSTUsageValidation.h b/Firestore/Source/Util/FSTUsageValidation.h deleted file mode 100644 index afc54128c82..00000000000 --- a/Firestore/Source/Util/FSTUsageValidation.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - -NS_ASSUME_NONNULL_BEGIN - -/** Helper for creating a general exception for invalid usage of an API. */ -NSException *FSTInvalidUsage(NSString *exceptionName, NSString *format, ...); - -/** - * Macro to throw exceptions in response to API usage errors. Avoids the lint warning you usually - * get when using @throw and (unlike a function) doesn't trigger warnings about not all codepaths - * returning a value. - * - * Exceptions should only be used for programmer errors made by consumers of the SDK, e.g. - * invalid method arguments. - * - * For recoverable runtime errors, use NSError**. - * For internal programming errors, use HARD_FAIL(). - */ -#define FSTThrowInvalidUsage(exceptionName, format, ...) \ - do { \ - @throw FSTInvalidUsage(exceptionName, format, ##__VA_ARGS__); \ - } while (0) - -#define FSTThrowInvalidArgument(format, ...) \ - do { \ - @throw FSTInvalidUsage(@"FIRInvalidArgumentException", format, ##__VA_ARGS__); \ - } while (0) - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Util/FSTUsageValidation.mm b/Firestore/Source/Util/FSTUsageValidation.mm deleted file mode 100644 index 93abf878ade..00000000000 --- a/Firestore/Source/Util/FSTUsageValidation.mm +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 "Firestore/Source/Util/FSTUsageValidation.h" - -NS_ASSUME_NONNULL_BEGIN - -NSException *FSTInvalidUsage(NSString *exceptionName, NSString *format, ...) { - va_list arg_list; - va_start(arg_list, format); - NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; - va_end(arg_list); - - return [[NSException alloc] initWithName:exceptionName reason:formattedString userInfo:nil]; -} - -NS_ASSUME_NONNULL_END diff --git a/Firestore/core/src/firebase/firestore/api/CMakeLists.txt b/Firestore/core/src/firebase/firestore/api/CMakeLists.txt index cc241aa0c7e..c50aa484a1f 100644 --- a/Firestore/core/src/firebase/firestore/api/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/api/CMakeLists.txt @@ -12,6 +12,30 @@ # See the License for the specific language governing permissions and # limitations under the License. +cc_library( + firebase_firestore_api_input_validation_std + SOURCES + input_validation.h + input_validation_std.cc + DEPENDS + firebase_firestore_util +) + +cc_library( + firebase_firestore_api_input_validation_apple + SOURCES + input_validation.h + input_validation_apple.mm + DEPENDS + firebase_firestore_util +) + +cc_select( + firebase_firestore_api_input_validation + APPLE firebase_firestore_api_input_validation_apple + DEFAULT firebase_firestore_api_input_validation_std +) + cc_library( firebase_firestore_api SOURCES @@ -19,5 +43,6 @@ cc_library( snapshot_metadata.h DEPENDS absl_meta + firebase_firestore_api_input_validation firebase_firestore_util ) diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index 3418bc1647e..f61f54bd7c4 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -26,7 +26,6 @@ #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTMutation.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" diff --git a/Firestore/core/src/firebase/firestore/api/input_validation.h b/Firestore/core/src/firebase/firestore/api/input_validation.h new file mode 100644 index 00000000000..50cf7111067 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/input_validation.h @@ -0,0 +1,80 @@ +/* + * Copyright 2019 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_API_INPUT_VALIDATION_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_INPUT_VALIDATION_H_ + +// Routines in this file are used to throw an exception (or crash, depending on +// platform) in response to API usage errors. Exceptions should only be used +// for programmer errors made by consumers of the SDK, e.g. invalid method +// arguments. +// +// These routines avoid conditional compilation in the caller and avoids lint +// warnings around actually throwing exceptions in source. The implementation +// chooses the best way to surface a logic error to the developer. +// +// For recoverable runtime errors, use util::Status, or in pure Objective-C +// code use an NSError** out-parameter. +// +// For internal programming errors, including internal argument checking, use +// HARD_ASSERT or HARD_FAIL(). + +#include + +#include "Firestore/core/src/firebase/firestore/util/string_format.h" + +namespace firebase { +namespace firestore { +namespace api { + +namespace impl { + +[[noreturn]] void ThrowIllegalState(const std::string& message); +[[noreturn]] void ThrowInvalidArgument(const std::string& message); + +} // namespace impl + +/** + * Throws an exception indicating that the user passed an invalid argument. + * + * Invalid argument is interpreted pretty broadly and can mean that the user + * made an incompatible chained method call while building up a larger + * structure, like a query. + */ +template +[[noreturn]] void ThrowInvalidArgument(const char* format, const FA&... args) { + impl::ThrowInvalidArgument(util::StringFormat(format, args...)); +} + +/** + * Throws an exception that indicates the user has attempted to use an API + * that's in an illegal state, usually by violating a precondition of the API + * call. + * + * Good uses of these are things like using a write batch after committing or + * trying to use Firestore without initializing FIRApp. Builder-style APIs that + * haven't done anything yet should likely just stick to ThrowInvalidArgument. + */ +template +[[noreturn]] void ThrowIllegalState(const char* format, const FA&... args) { + impl::ThrowIllegalState(util::StringFormat(format, args...)); +} + +} // namespace api +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_INPUT_VALIDATION_H_ diff --git a/Firestore/core/src/firebase/firestore/api/input_validation_apple.mm b/Firestore/core/src/firebase/firestore/api/input_validation_apple.mm new file mode 100644 index 00000000000..ba1db1c8ed7 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/input_validation_apple.mm @@ -0,0 +1,49 @@ +/* + * Copyright 2019 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/api/input_validation.h" + +#import + +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace firebase { +namespace firestore { +namespace api { +namespace impl { + +static NSException* MakeException(NSString* name, const std::string& message) { + return [[NSException alloc] initWithName:name + reason:util::WrapNSString(message) + userInfo:nil]; +} + +[[noreturn]] void ThrowIllegalState(const std::string& message) { + @throw MakeException(@"FIRIllegalStateException", message); // NOLINT +} + +[[noreturn]] void ThrowInvalidArgument(const std::string& message) { + @throw MakeException(@"FIRInvalidArgumentException", message); // NOLINT +} + +} // namespace impl +} // namespace api +} // namespace firestore +} // namespace firebase + +NS_ASSUME_NONNULL_END diff --git a/Firestore/core/src/firebase/firestore/api/input_validation_std.cc b/Firestore/core/src/firebase/firestore/api/input_validation_std.cc new file mode 100644 index 00000000000..cd01de20f4f --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/input_validation_std.cc @@ -0,0 +1,50 @@ +/* + * Copyright 2019 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/api/input_validation.h" + +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/log.h" + +namespace firebase { +namespace firestore { +namespace api { +namespace impl { + +namespace { + +template +[[noreturn]] void Throw(const char* kind, const T& error) { + LOG_ERROR(ERROR, "%s: %s", kind, error.what()); + std::abort(); +} + +} // namespace + +[[noreturn]] void ThrowIllegalState(const std::string& message) { + Throw("Illegal state", std::logic_error(message)); +} + +[[noreturn]] void ThrowInvalidArgument(const std::string& message) { + Throw("Invalid argument", std::invalid_argument(message)); +} + +} // namespace impl +} // namespace api +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/api/query_snapshot.mm b/Firestore/core/src/firebase/firestore/api/query_snapshot.mm index 7992cc15fba..c0269809a59 100644 --- a/Firestore/core/src/firebase/firestore/api/query_snapshot.mm +++ b/Firestore/core/src/firebase/firestore/api/query_snapshot.mm @@ -24,7 +24,6 @@ #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" diff --git a/Firestore/core/src/firebase/firestore/core/user_data.mm b/Firestore/core/src/firebase/firestore/core/user_data.mm index 237e49f234c..7150d12b329 100644 --- a/Firestore/core/src/firebase/firestore/core/user_data.mm +++ b/Firestore/core/src/firebase/firestore/core/user_data.mm @@ -19,14 +19,15 @@ #include #import "Firestore/Source/Model/FSTMutation.h" -#import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "absl/strings/match.h" namespace firebase { namespace firestore { namespace core { +using api::ThrowInvalidArgument; using model::DocumentKey; using model::FieldMask; using model::FieldPath; @@ -155,8 +156,8 @@ case UserDataSource::Argument: return false; default: - FSTThrowInvalidArgument(@"Unexpected case for UserDataSource: %d", - accumulator_->data_source()); + ThrowInvalidArgument("Unexpected case for UserDataSource: %s", + accumulator_->data_source()); } } @@ -175,9 +176,8 @@ absl::string_view designator{RESERVED_FIELD_DESIGNATOR}; if (write() && absl::StartsWith(segment, designator) && absl::EndsWith(segment, designator)) { - FSTThrowInvalidArgument(@"Document fields cannot begin and end with %s%s", - RESERVED_FIELD_DESIGNATOR, - FieldDescription().c_str()); + ThrowInvalidArgument("Document fields cannot begin and end with %s%s", + RESERVED_FIELD_DESIGNATOR, FieldDescription()); } } diff --git a/Firestore/core/src/firebase/firestore/util/log.h b/Firestore/core/src/firebase/firestore/util/log.h index 248d434ac4e..c02af00080e 100644 --- a/Firestore/core/src/firebase/firestore/util/log.h +++ b/Firestore/core/src/firebase/firestore/util/log.h @@ -31,6 +31,8 @@ enum LogLevel { kLogLevelDebug, // Warning Log Level kLogLevelWarning, + // Error Log Level + kLogLevelError, }; // Log a message if kLogLevelDebug is enabled. Arguments are not evaluated if @@ -63,6 +65,21 @@ enum LogLevel { } \ } while (0) +// Log a message if kLogLevelError is enabled (it is by default). Arguments are +// not evaluated if logging is disabled. +// +// @param format A format string suitable for use with `util::StringFormat` +// @param ... C++ variadic arguments that match the format string. Not C +// varargs. +#define LOG_ERROR(...) \ + do { \ + namespace _util = firebase::firestore::util; \ + if (_util::LogIsLoggable(_util::kLogLevelError)) { \ + std::string _message = _util::StringFormat(__VA_ARGS__); \ + _util::LogMessage(_util::kLogLevelError, _message); \ + } \ + } while (0) + // Tests to see if the given log level is loggable. bool LogIsLoggable(LogLevel level); diff --git a/Firestore/core/src/firebase/firestore/util/log_apple.mm b/Firestore/core/src/firebase/firestore/util/log_apple.mm index e173b476904..f1745e204ce 100644 --- a/Firestore/core/src/firebase/firestore/util/log_apple.mm +++ b/Firestore/core/src/firebase/firestore/util/log_apple.mm @@ -39,6 +39,8 @@ FIRLoggerLevel ToFIRLoggerLevel(LogLevel level) { return FIRLoggerLevelDebug; case kLogLevelWarning: return FIRLoggerLevelWarning; + case kLogLevelError: + return FIRLoggerLevelError; default: // Unsupported log level. FIRSetLoggerLevel will deal with it. return static_cast(-1); @@ -64,7 +66,7 @@ void LogSetLevel(LogLevel level) { } bool LogIsLoggable(LogLevel level) { - return FIRIsLoggableLevel(ToFIRLoggerLevel(level), NO); + return FIRIsLoggableLevel(ToFIRLoggerLevel(level), false); } void LogMessage(LogLevel level, const std::string& message) { diff --git a/Firestore/core/src/firebase/firestore/util/log_stdio.cc b/Firestore/core/src/firebase/firestore/util/log_stdio.cc index 6fff8850230..91af4b5d6dc 100644 --- a/Firestore/core/src/firebase/firestore/util/log_stdio.cc +++ b/Firestore/core/src/firebase/firestore/util/log_stdio.cc @@ -49,6 +49,9 @@ void LogMessage(LogLevel log_level, const std::string& message) { case kLogLevelWarning: level_word = "WARNING"; break; + case kLogLevelError: + level_word = "ERROR"; + break; default: UNREACHABLE(); break; From 04561fe74d022daf518b77d7b035076ef3de25d3 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Thu, 28 Mar 2019 19:06:04 -0400 Subject: [PATCH 115/214] FIRMessagingRemoteNotificationsProxy - test only public API with no assumptions on implementation (#2672) --- ...FIRMessagingRemoteNotificationsProxyTest.m | 279 +++++++++++------- Firebase/Messaging/FIRMessaging.m | 2 +- .../FIRMessagingRemoteNotificationsProxy.h | 7 +- .../FIRMessagingRemoteNotificationsProxy.m | 4 - 4 files changed, 179 insertions(+), 113 deletions(-) diff --git a/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m b/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m index 7a91dd68bd3..a46efffc979 100644 --- a/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m +++ b/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m @@ -24,38 +24,32 @@ #import "FIRMessaging.h" #import "FIRMessagingRemoteNotificationsProxy.h" -#pragma mark - Expose Internal Methods for Testing -// Expose some internal properties and methods here, in order to test -@interface FIRMessagingRemoteNotificationsProxy () - -@property(readonly, nonatomic) BOOL didSwizzleMethods; -@property(readonly, nonatomic) BOOL didSwizzleAppDelegateMethods; - -@property(readonly, nonatomic) BOOL hasSwizzledUserNotificationDelegate; -@property(readonly, nonatomic) BOOL isObservingUserNotificationDelegateChanges; - -@property(strong, readonly, nonatomic) id userNotificationCenter; -@property(strong, readonly, nonatomic) id currentUserNotificationCenterDelegate; - -+ (instancetype)sharedProxy; - -- (BOOL)swizzleAppDelegateMethods:(id)appDelegate; -- (void)listenForDelegateChangesInUserNotificationCenter:(id)notificationCenter; -- (void)swizzleUserNotificationCenterDelegate:(id)delegate; -- (void)unswizzleUserNotificationCenterDelegate:(id)delegate; - -void FCM_swizzle_appDidReceiveRemoteNotification(id self, - SEL _cmd, - UIApplication *app, - NSDictionary *userInfo); -void FCM_swizzle_appDidReceiveRemoteNotificationWithHandler( - id self, SEL _cmd, UIApplication *app, NSDictionary *userInfo, - void (^handler)(UIBackgroundFetchResult)); -void FCM_swizzle_willPresentNotificationWithHandler( - id self, SEL _cmd, id center, id notification, void (^handler)(NSUInteger)); -void FCM_swizzle_didReceiveNotificationResponseWithHandler( - id self, SEL _cmd, id center, id response, void (^handler)()); +#pragma mark - Invalid App Delegate or UNNotificationCenter +@interface RandomObject : NSObject +@property(nonatomic, weak) id delegate; +@end +@implementation RandomObject +- (void)application:(UIApplication *)application +didReceiveRemoteNotification:(NSDictionary *)userInfo +fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + willPresentNotification:(UNNotification *)notification + withCompletionHandler:(void (^)(UNNotificationPresentationOptions options)) +completionHandler { +} + +#if TARGET_OS_IOS +- (void)userNotificationCenter:(UNUserNotificationCenter *)center +didReceiveNotificationResponse:(UNNotificationResponse *)response + withCompletionHandler:(void(^)(void))completionHandler { +} +#endif // TARGET_OS_IOS + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 @end #pragma mark - Incomplete App Delegate @@ -104,7 +98,9 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center self.willPresentWasCalled = YES; } #if TARGET_OS_IOS -- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { +- (void)userNotificationCenter:(UNUserNotificationCenter *)center +didReceiveNotificationResponse:(UNNotificationResponse *)response + withCompletionHandler:(void (^)(void))completionHandler { self.didReceiveResponseWasCalled = YES; } #endif // TARGET_OS_IOS @@ -116,9 +112,10 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti @interface FIRMessagingRemoteNotificationsProxyTest : XCTestCase @property(nonatomic, strong) FIRMessagingRemoteNotificationsProxy *proxy; -@property(nonatomic, strong) id mockProxy; @property(nonatomic, strong) id mockProxyClass; -@property(nonatomic, strong) id mockMessagingClass; +@property(nonatomic, strong) id mockSharedApplication; +@property(nonatomic, strong) id mockMessaging; +@property(nonatomic, strong) id mockUserNotificationCenter; @end @@ -126,26 +123,31 @@ @implementation FIRMessagingRemoteNotificationsProxyTest - (void)setUp { [super setUp]; + _mockSharedApplication = OCMPartialMock([UIApplication sharedApplication]); + + _mockMessaging = OCMClassMock([FIRMessaging class]); + OCMStub([_mockMessaging messaging]).andReturn(_mockMessaging); + _proxy = [[FIRMessagingRemoteNotificationsProxy alloc] init]; - _mockProxy = OCMPartialMock(_proxy); _mockProxyClass = OCMClassMock([FIRMessagingRemoteNotificationsProxy class]); - // Update +sharedProxy to always return our partial mock of FIRMessagingRemoteNotificationsProxy - OCMStub([_mockProxyClass sharedProxy]).andReturn(_mockProxy); - // Many of our swizzled methods call [FIRMessaging messaging], but we don't need it, - // so just stub it to return nil - _mockMessagingClass = OCMClassMock([FIRMessaging class]); - OCMStub([_mockMessagingClass messaging]).andReturn(nil); + // Update +sharedProxy to always return our test instance + OCMStub([_mockProxyClass sharedProxy]).andReturn(self.proxy); + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + _mockUserNotificationCenter = OCMClassMock([UNUserNotificationCenter class]); + OCMStub([_mockUserNotificationCenter currentNotificationCenter]).andReturn(_mockUserNotificationCenter); +#endif } - (void)tearDown { - [_mockMessagingClass stopMocking]; - _mockMessagingClass = nil; - [_mockProxyClass stopMocking]; _mockProxyClass = nil; - [_mockProxy stopMocking]; - _mockProxy = nil; + [_mockMessaging stopMocking]; + _mockMessaging = nil; + + [_mockSharedApplication stopMocking]; + _mockSharedApplication = nil; _proxy = nil; [super tearDown]; @@ -154,30 +156,36 @@ - (void)tearDown { #pragma mark - Method Swizzling Tests - (void)testSwizzlingNonAppDelegate { - id randomObject = @"Random Object that is not an App Delegate"; - [self.proxy swizzleAppDelegateMethods:randomObject]; - XCTAssertFalse(self.proxy.didSwizzleAppDelegateMethods); -} + RandomObject *invalidAppDelegate = [[RandomObject alloc] init]; + [OCMStub([self.mockSharedApplication delegate]) andReturn:invalidAppDelegate]; + [self.proxy swizzleMethodsIfPossible]; -- (void)testSwizzlingAppDelegate { - IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; - [self.proxy swizzleAppDelegateMethods:incompleteAppDelegate]; - XCTAssertTrue(self.proxy.didSwizzleAppDelegateMethods); + OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]); + + [invalidAppDelegate application:self.mockSharedApplication + didReceiveRemoteNotification:@{} + fetchCompletionHandler:^(UIBackgroundFetchResult result) {}]; } - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod { - IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; - [self.mockProxy swizzleAppDelegateMethods:incompleteAppDelegate]; + IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; + [OCMStub([self.mockSharedApplication delegate]) andReturn:incompleteAppDelegate]; + [self.proxy swizzleMethodsIfPossible]; + + NSDictionary *notification = @{@"test" : @""}; + OCMExpect([self.mockMessaging appDidReceiveMessage:notification]); - [incompleteAppDelegate application:OCMClassMock([UIApplication class]) - didReceiveRemoteNotification:@{}]; - // Verify our swizzled method was called - OCMVerify(FCM_swizzle_appDidReceiveRemoteNotification); + [incompleteAppDelegate application:self.mockSharedApplication + didReceiveRemoteNotification:notification]; + + [self.mockMessaging verify]; } - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod { IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; - [self.mockProxy swizzleAppDelegateMethods:incompleteAppDelegate]; + [OCMStub([self.mockSharedApplication delegate]) andReturn:incompleteAppDelegate]; + [self.proxy swizzleMethodsIfPossible]; + SEL remoteNotificationWithFetchHandler = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); XCTAssertFalse([incompleteAppDelegate respondsToSelector:remoteNotificationWithFetchHandler]); @@ -188,43 +196,87 @@ - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod { - (void)testSwizzledAppDelegateRemoteNotificationMethods { #if TARGET_OS_IOS FakeAppDelegate *appDelegate = [[FakeAppDelegate alloc] init]; - [self.mockProxy swizzleAppDelegateMethods:appDelegate]; - [appDelegate application:OCMClassMock([UIApplication class]) didReceiveRemoteNotification:@{}]; + [OCMStub([self.mockSharedApplication delegate]) andReturn:appDelegate]; + [self.proxy swizzleMethodsIfPossible]; + + NSDictionary *notification = @{@"test" : @""}; + + //Test application:didReceiveRemoteNotification: + // Verify our swizzled method was called - OCMVerify(FCM_swizzle_appDidReceiveRemoteNotification); + OCMExpect([self.mockMessaging appDidReceiveMessage:notification]); + + // Call the method + [appDelegate application:self.mockSharedApplication + didReceiveRemoteNotification:notification]; + // Verify our original method was called XCTAssertTrue(appDelegate.remoteNotificationMethodWasCalled); + [self.mockMessaging verify]; + + //Test application:didReceiveRemoteNotification:fetchCompletionHandler: + + // Verify our swizzled method was called + OCMExpect([self.mockMessaging appDidReceiveMessage:notification]); - // Now call the remote notification with handler method [appDelegate application:OCMClassMock([UIApplication class]) - didReceiveRemoteNotification:@{} + didReceiveRemoteNotification:notification fetchCompletionHandler:^(UIBackgroundFetchResult result) {}]; - // Verify our swizzled method was called - OCMVerify(FCM_swizzle_appDidReceiveRemoteNotificationWithHandler); + // Verify our original method was called XCTAssertTrue(appDelegate.remoteNotificationWithFetchHandlerWasCalled); -#endif + [self.mockMessaging verify]; +#endif } #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - (void)testListeningForDelegateChangesOnInvalidUserNotificationCenter { - id randomObject = @"Random Object that is not a User Notification Center"; - [self.proxy listenForDelegateChangesInUserNotificationCenter:randomObject]; - XCTAssertFalse(self.proxy.isObservingUserNotificationDelegateChanges); + RandomObject *invalidNotificationCenter = [[RandomObject alloc] init]; + OCMStub([self.mockUserNotificationCenter currentNotificationCenter]).andReturn(invalidNotificationCenter); + [self.proxy swizzleMethodsIfPossible]; + + OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]); + + [(id)invalidNotificationCenter.delegate + userNotificationCenter:self.mockUserNotificationCenter + willPresentNotification:OCMClassMock([UNNotification class]) + withCompletionHandler:^(UNNotificationPresentationOptions options) { + }]; } - (void)testSwizzlingInvalidUserNotificationCenterDelegate { - id randomObject = @"Random Object that is not a User Notification Center Delegate"; - [self.proxy swizzleUserNotificationCenterDelegate:randomObject]; - XCTAssertFalse(self.proxy.hasSwizzledUserNotificationDelegate); + RandomObject *invalidDelegate = [[RandomObject alloc] init]; + OCMStub([self.mockUserNotificationCenter delegate]).andReturn(invalidDelegate); + [self.proxy swizzleMethodsIfPossible]; + + OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]); + + [invalidDelegate + userNotificationCenter:self.mockUserNotificationCenter + willPresentNotification:OCMClassMock([UNNotification class]) + withCompletionHandler:^(UNNotificationPresentationOptions options) { + }]; } - (void)testSwizzlingUserNotificationsCenterDelegate { FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init]; - [self.proxy swizzleUserNotificationCenterDelegate:delegate]; - XCTAssertTrue(self.proxy.hasSwizzledUserNotificationDelegate); + OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate); + [self.proxy swizzleMethodsIfPossible]; + + NSDictionary *message = @{@"message": @""}; + id notification = [self userNotificationWithMessage:message]; + + OCMExpect([self.mockMessaging appDidReceiveMessage:message]); + + [delegate + userNotificationCenter:self.mockUserNotificationCenter + willPresentNotification:notification + withCompletionHandler:^(UNNotificationPresentationOptions options) { + }]; + + [self.mockMessaging verify]; } // Use a fake delegate that doesn't actually implement the needed delegate method. @@ -237,7 +289,8 @@ - (void)testIncompleteUserNotificationCenterDelegateMethod { } IncompleteUserNotificationCenterDelegate *delegate = [[IncompleteUserNotificationCenterDelegate alloc] init]; - [self.mockProxy swizzleUserNotificationCenterDelegate:delegate]; + OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate); + [self.proxy swizzleMethodsIfPossible]; // Because the incomplete delete does not implement either of the optional delegate methods, we // should swizzle nothing. If we had swizzled them, then respondsToSelector: would return YES // even though the delegate does not implement the methods. @@ -250,55 +303,67 @@ - (void)testIncompleteUserNotificationCenterDelegateMethod { // Use an object that does actually implement the optional methods. Both should be called. - (void)testSwizzledUserNotificationsCenterDelegate { - // Early exit if running on pre iOS 10 - if (![UNNotification class]) { - return; - } FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init]; - [self.mockProxy swizzleUserNotificationCenterDelegate:delegate]; + OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate); + [self.proxy swizzleMethodsIfPossible]; + + NSDictionary *message = @{@"message": @""}; + + // Verify userNotificationCenter:willPresentNotification:withCompletionHandler: + OCMExpect([self.mockMessaging appDidReceiveMessage:message]); + // Invoking delegate method should also invoke our swizzled method // The swizzled method uses the +sharedProxy, which should be - // returning our mocked proxy. + // returning our proxy. // Use non-nil, proper classes, otherwise our SDK bails out. - [delegate userNotificationCenter:OCMClassMock([UNUserNotificationCenter class]) - willPresentNotification:[self generateMockNotification] + [delegate userNotificationCenter:self.mockUserNotificationCenter + willPresentNotification:[self userNotificationWithMessage:message] withCompletionHandler:^(NSUInteger options) {}]; - // Verify our swizzled method was called - OCMVerify(FCM_swizzle_willPresentNotificationWithHandler); + // Verify our original method was called XCTAssertTrue(delegate.willPresentWasCalled); + + // Verify our swizzled method was called + [self.mockMessaging verify]; + #if TARGET_OS_IOS - [delegate userNotificationCenter:OCMClassMock([UNUserNotificationCenter class]) - didReceiveNotificationResponse:[self generateMockNotificationResponse] + // Verify userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: + + OCMExpect([self.mockMessaging appDidReceiveMessage:message]); + + [delegate userNotificationCenter:self.mockUserNotificationCenter + didReceiveNotificationResponse:[self userNotificationResponseWithMessage:message] withCompletionHandler:^{}]; - // Verify our swizzled method was called - OCMVerify(FCM_swizzle_appDidReceiveRemoteNotificationWithHandler); + // Verify our original method was called XCTAssertTrue(delegate.didReceiveResponseWasCalled); -#endif -} -- (id)generateMockNotification { - // Stub out: notification.request.content.userInfo - id mockNotification = OCMClassMock([UNNotification class]); - id mockRequest = OCMClassMock([UNNotificationRequest class]); - id mockContent = OCMClassMock([UNNotificationContent class]); - OCMStub([mockContent userInfo]).andReturn(@{}); - OCMStub([mockRequest content]).andReturn(mockContent); - OCMStub([mockNotification request]).andReturn(mockRequest); - return mockNotification; + // Verify our swizzled method was called + [self.mockMessaging verify]; +#endif // TARGET_OS_IOS } -- (id)generateMockNotificationResponse { +- (id)userNotificationResponseWithMessage:(NSDictionary *)message { // Stub out: response.[mock notification above] #if TARGET_OS_IOS id mockNotificationResponse = OCMClassMock([UNNotificationResponse class]); - id mockNotification = [self generateMockNotification]; + id mockNotification = [self userNotificationWithMessage:message]; OCMStub([mockNotificationResponse notification]).andReturn(mockNotification); return mockNotificationResponse; -#else +#else // TARGET_OS_IOS return nil; -#endif +#endif // TARGET_OS_IOS +} + +- (UNNotification *)userNotificationWithMessage:(NSDictionary *)message { + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.userInfo = message; + id notificationRequest = OCMClassMock([UNNotificationRequest class]); + OCMStub([notificationRequest content]).andReturn(content); + id notification = OCMClassMock([UNNotification class]); + OCMStub([notification request]).andReturn(notificationRequest); + + return notification; } #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 6a9870281f6..6a16840eb61 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -259,7 +259,7 @@ - (void)configureMessaging:(FIRApp *)app { @"proper integration.", kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey, docsURLString); - [FIRMessagingRemoteNotificationsProxy swizzleMethods]; + [[FIRMessagingRemoteNotificationsProxy sharedProxy] swizzleMethodsIfPossible]; } } diff --git a/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.h b/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.h index 59c3c15d94d..f0010b3b783 100644 --- a/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.h +++ b/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.h @@ -31,10 +31,15 @@ */ + (BOOL)canSwizzleMethods; +/** + * A shared instance of `FIRMessagingRemoteNotificationsProxy` + */ ++ (instancetype)sharedProxy; + /** * Swizzles Application Delegate's remote-notification callbacks and User Notification Center * delegate callback, and invokes the original selectors once done. */ -+ (void)swizzleMethods; +- (void)swizzleMethodsIfPossible; @end diff --git a/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m b/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m index 7cea178edc8..a7f6efbe743 100644 --- a/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m +++ b/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m @@ -63,10 +63,6 @@ + (BOOL)canSwizzleMethods { } } -+ (void)swizzleMethods { - [[FIRMessagingRemoteNotificationsProxy sharedProxy] swizzleMethodsIfPossible]; -} - + (instancetype)sharedProxy { static FIRMessagingRemoteNotificationsProxy *proxy; static dispatch_once_t onceToken; From e024df5073a0a1788637e734c07724aad78c79fe Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 28 Mar 2019 16:13:20 -0700 Subject: [PATCH 116/214] fix typo in iOS target (#2673) * also a few minor cleanup --- .../Tests/FIRMessagingDataMessageManagerTest.m | 15 --------------- Firebase/Messaging/FIRMessagingCheckinService.h | 2 +- .../Messaging/FIRMessagingRmq2PersistentStore.m | 2 +- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/Example/Messaging/Tests/FIRMessagingDataMessageManagerTest.m b/Example/Messaging/Tests/FIRMessagingDataMessageManagerTest.m index 8aaad512ebe..6f6b9f70c09 100644 --- a/Example/Messaging/Tests/FIRMessagingDataMessageManagerTest.m +++ b/Example/Messaging/Tests/FIRMessagingDataMessageManagerTest.m @@ -108,7 +108,6 @@ - (void)testSendValidMessage_withNoConnection { error:[OCMArg anyObjectRef]]; // should be logged into the service - [self addFakeFIRMessagingRegistrationToken]; [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; // try to send messages with no connection should be queued into RMQ NSMutableDictionary *message = [self upstreamMessageWithID:messageID ttl:-1 delay:0]; @@ -158,7 +157,6 @@ - (void)testSendInvalidMessage_withNoTo { }]]); // should be logged into the service - [self addFakeFIRMessagingRegistrationToken]; [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; [self.dataMessageManager sendDataMessageStanza:message]; @@ -187,7 +185,6 @@ - (void)testSendInvalidMessage_withSizeExceeded { return NO; }]]); - [self addFakeFIRMessagingRegistrationToken]; // should be logged into the service [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; [self.dataMessageManager sendDataMessageStanza:message]; @@ -215,7 +212,6 @@ - (void)testSendValidMessage_withRmqSaveError { // should be logged into the service [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; - [self addFakeFIRMessagingRegistrationToken]; [self.dataMessageManager sendDataMessageStanza:message]; OCMVerifyAll(self.mockReceiver); @@ -240,7 +236,6 @@ - (void)testSendValidMessage_withTTL0 { OCMExpect([self.mockClient sendMessage:[OCMArg checkWithBlock:isValidStanza]]); [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; - [self addFakeFIRMessagingRegistrationToken]; [self.dataMessageManager sendDataMessageStanza:message]; OCMVerifyAll(self.mockClient); @@ -276,7 +271,6 @@ - (void)XXX_testSendValidMessage_withTTL0AndNoFIRMessagingConnection { OCMExpect([self.mockClient retryConnectionImmediately:[OCMArg isEqual:@YES]]); [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; - [self addFakeFIRMessagingRegistrationToken]; [self.dataMessageManager sendDataMessageStanza:message]; OCMVerifyAll(self.mockClient); @@ -302,7 +296,6 @@ - (void)xxx_testSendValidMessage_withTTL0AndNoNetwork { }]]); [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; - [self addFakeFIRMessagingRegistrationToken]; [self.dataMessageManager sendDataMessageStanza:message]; OCMVerifyAll(self.mockReceiver); @@ -329,7 +322,6 @@ - (void)XXX_testDelayedMessagesBeingResentOnReconnect { error:[OCMArg isNil]]); [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; - [self addFakeFIRMessagingRegistrationToken]; [self.dataMessageManager sendDataMessageStanza:message]; __block FIRMessagingDataMessageHandler dataMessageHandler; @@ -496,7 +488,6 @@ - (void)testResendSavedMessages { // have a real RMQ store [self.dataMessageManager setRmq2Manager:newRmqManager]; - [self addFakeFIRMessagingRegistrationToken]; [self.dataMessageManager setDeviceAuthID:@"auth-id" secretToken:@"secret-token"]; // send a couple of message with no connection should be saved to RMQ @@ -576,12 +567,6 @@ - (void)testResendingExpiredMessagesFails { dataMessageHandler:nil]; } -#pragma mark - Private - -- (void)addFakeFIRMessagingRegistrationToken { - // [[FIRMessagingDefaultsManager sharedInstance] saveAppIDToken:kFIRMessagingAppIDToken]; -} - #pragma mark - Create Packet - (GtalkDataMessageStanza *)validDataMessagePacket { diff --git a/Firebase/Messaging/FIRMessagingCheckinService.h b/Firebase/Messaging/FIRMessagingCheckinService.h index 155143a5d55..d7c34512753 100644 --- a/Firebase/Messaging/FIRMessagingCheckinService.h +++ b/Firebase/Messaging/FIRMessagingCheckinService.h @@ -18,7 +18,7 @@ /** * Register the device with Checkin Service and get back the `authID`, `secret token` etc. for the - * client. Checkin results are cached in the `FIRMessagingDefaultsManager` and periodically refreshed to + * client. Checkin results are cached and periodically refreshed to * prevent them from being stale. Each client needs to register with checkin before registering * with FIRMessaging. */ diff --git a/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m b/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m index a14db5892a6..bef6aac5370 100644 --- a/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m +++ b/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m @@ -126,7 +126,7 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName { self = [super init]; if (self) { _databaseName = [databaseName copy]; -#if TARGET_OS_iOS +#if TARGET_OS_IOS BOOL didMoveToApplicationSupport = [self moveToApplicationSupportSubDirectory:kFIRMessagingSubDirectoryName]; From 17f9064cd04103536f417469cac2c706bceff6e8 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 29 Mar 2019 11:04:13 -0700 Subject: [PATCH 117/214] Release 5.20.0 (#2677) --- Example/Podfile | 2 +- Firebase/Core/FIROptions.m | 2 +- FirebaseAuth.podspec | 2 +- FirebaseCore.podspec | 4 ++-- FirebaseDynamicLinks.podspec | 2 +- FirebaseFirestore.podspec | 2 +- FirebaseInstanceID.podspec | 2 +- FirebaseMessaging.podspec | 2 +- Firestore/Example/Podfile | 2 +- Releases/Manifests/5.20.0.json | 8 ++++++++ SymbolCollisionTest/Podfile | 2 +- 11 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 Releases/Manifests/5.20.0.json diff --git a/Example/Podfile b/Example/Podfile index f8016c949ab..066f0a916e8 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -19,7 +19,7 @@ target 'Core_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.19.0' + pod 'Firebase/CoreOnly', '5.20.0' target 'Core_Tests_iOS' do inherit! :search_paths diff --git a/Firebase/Core/FIROptions.m b/Firebase/Core/FIROptions.m index 75c7706aee6..72901f51a37 100644 --- a/Firebase/Core/FIROptions.m +++ b/Firebase/Core/FIROptions.m @@ -42,7 +42,7 @@ // Library version ID. NSString *const kFIRLibraryVersionID = @"5" // Major version (one or more digits) @"04" // Minor version (exactly 2 digits) - @"00" // Build number (exactly 2 digits) + @"01" // Build number (exactly 2 digits) @"000"; // Fixed "000" // Plist file name. NSString *const kServiceInfoFileName = @"GoogleService-Info"; diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index 9dd85a9c168..9c2383918e1 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAuth' - s.version = '5.4.1' + s.version = '5.4.2' s.summary = 'The official iOS client for Firebase Authentication (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 5d72a48d899..2a74d92b063 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCore' - s.version = '5.4.0' + s.version = '5.4.1' s.summary = 'Firebase Core for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC @@ -33,7 +33,7 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration s.pod_target_xcconfig = { 'GCC_C_LANGUAGE_STANDARD' => 'c99', 'GCC_PREPROCESSOR_DEFINITIONS' => - 'FIRCore_VERSION=' + s.version.to_s + ' Firebase_VERSION=5.19.0', + 'FIRCore_VERSION=' + s.version.to_s + ' Firebase_VERSION=5.20.0', 'OTHER_CFLAGS' => '-fno-autolink' } end diff --git a/FirebaseDynamicLinks.podspec b/FirebaseDynamicLinks.podspec index 067a07e2125..3402a4f1d78 100644 --- a/FirebaseDynamicLinks.podspec +++ b/FirebaseDynamicLinks.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseDynamicLinks' - s.version = '3.4.2' + s.version = '3.4.3' s.summary = 'Firebase DynamicLinks for iOS' s.description = <<-DESC diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 6204e0aec6f..a602a5c81a1 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFirestore' - s.version = '1.1.0' + s.version = '1.2.0' s.summary = 'Google Cloud Firestore for iOS' s.description = <<-DESC diff --git a/FirebaseInstanceID.podspec b/FirebaseInstanceID.podspec index e9b917603d8..d2d8eeebba7 100644 --- a/FirebaseInstanceID.podspec +++ b/FirebaseInstanceID.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInstanceID' - s.version = '3.8.0' + s.version = '3.8.1' s.summary = 'Firebase InstanceID for iOS' s.description = <<-DESC diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index b49ff324106..535a575c049 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMessaging' - s.version = '3.4.0' + s.version = '3.5.0' s.summary = 'Firebase Messaging for iOS' s.description = <<-DESC diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index bb5ffb0c7cd..a816a42cc8c 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -14,7 +14,7 @@ target 'Firestore_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.19.0' + pod 'Firebase/CoreOnly', '5.20.0' pod 'FirebaseAuth', :path => '../../' pod 'FirebaseAuthInterop', :path => '../../' diff --git a/Releases/Manifests/5.20.0.json b/Releases/Manifests/5.20.0.json new file mode 100644 index 00000000000..3afa84abc24 --- /dev/null +++ b/Releases/Manifests/5.20.0.json @@ -0,0 +1,8 @@ +{ + "FirebaseAuth":"5.4.2", + "FirebaseCore":"5.4.1", + "FirebaseDynamicLinks":"3.4.3", + "FirebaseFirestore":"1.2.0", + "FirebaseInstanceID":"3.8.1", + "FirebaseMessaging":"3.5.0" +} diff --git a/SymbolCollisionTest/Podfile b/SymbolCollisionTest/Podfile index d2ae9ceaa29..247db236377 100644 --- a/SymbolCollisionTest/Podfile +++ b/SymbolCollisionTest/Podfile @@ -6,7 +6,7 @@ target 'SymbolCollisionTest' do # use_frameworks! # Firebase Pods - pod 'Firebase', '5.19.0' + pod 'Firebase', '5.20.0' pod 'FirebaseAnalytics' pod 'FirebaseAuth' pod 'FirebaseCore' From 98549593ac7e0898fbe1f1b673343b362bc0b841 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Fri, 29 Mar 2019 12:23:43 -0700 Subject: [PATCH 118/214] fix a few place that copybara not properly handle the change (#2678) --- Example/Messaging/Tests/FIRInstanceIDWithFCMTest.m | 2 +- Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Example/Messaging/Tests/FIRInstanceIDWithFCMTest.m b/Example/Messaging/Tests/FIRInstanceIDWithFCMTest.m index afaabbcc38c..8f042a66463 100644 --- a/Example/Messaging/Tests/FIRInstanceIDWithFCMTest.m +++ b/Example/Messaging/Tests/FIRInstanceIDWithFCMTest.m @@ -17,7 +17,7 @@ #import #import -#import +#import #import #import "FIRMessaging_Private.h" #import "FIRMessaging.h" diff --git a/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m index 8bf554db4e5..4bcb76ece53 100644 --- a/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m +++ b/Example/Messaging/Tests/FIRMessagingExtensionHelperTest.m @@ -55,6 +55,8 @@ - (void)tearDown { } #if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +#ifdef COCOAPODS +// This test requires internet access. - (void)testModifyNotificationWithValidPayloadData { XCTestExpectation *validPayloadExpectation = [self expectationWithDescription:@"Test payload is valid."]; @@ -70,6 +72,7 @@ - (void)testModifyNotificationWithValidPayloadData { completionHandler:[OCMArg any]]); [self waitForExpectationsWithTimeout:1.0 handler:nil]; } +#endif - (void)testModifyNotificationWithInvalidPayloadData { XCTestExpectation *validPayloadExpectation = From 76a69834e240fa6598ad830c8ddb0b83374a5e79 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Fri, 29 Mar 2019 13:36:08 -0700 Subject: [PATCH 119/214] Remove pendingToken from public API. It's added to public header by mistake. (#2676) Developers are not supposed to use it, and they currently have no way to use it. --- .../OAuth/FIROAuthCredential_Internal.h | 5 +++++ .../AuthProviders/OAuth/FIROAuthProvider.m | 10 ---------- Firebase/Auth/Source/Public/FIROAuthCredential.h | 5 ----- Firebase/Auth/Source/Public/FIROAuthProvider.h | 16 ---------------- 4 files changed, 5 insertions(+), 31 deletions(-) diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential_Internal.h b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential_Internal.h index bcc3248ebba..623d5fd6041 100644 --- a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential_Internal.h +++ b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential_Internal.h @@ -35,6 +35,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, readonly, nullable) NSString *sessionID; +/** @property pendingToken + @brief The pending token used when completing the headful-lite flow. + */ +@property(nonatomic, readonly, nullable) NSString *pendingToken; + /** @fn initWithProviderId:IDToken:accessToken:pendingToken @brief Designated initializer. @param providerID The provider ID associated with the credential being created. diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m index 5557c16f53d..81e4fc919ca 100644 --- a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m +++ b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m @@ -64,16 +64,6 @@ @implementation FIROAuthProvider { NSString *_callbackScheme; } -+ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID - IDToken:(NSString *)IDToken - accessToken:(nullable NSString *)accessToken - pendingToken:(nullable NSString *)pendingToken { - return [[FIROAuthCredential alloc] initWithProviderID:providerID - IDToken:IDToken - accessToken:accessToken - pendingToken:pendingToken]; -} - + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID IDToken:(NSString *)IDToken accessToken:(nullable NSString *)accessToken { diff --git a/Firebase/Auth/Source/Public/FIROAuthCredential.h b/Firebase/Auth/Source/Public/FIROAuthCredential.h index 43b1d8111f2..db642c5dcea 100644 --- a/Firebase/Auth/Source/Public/FIROAuthCredential.h +++ b/Firebase/Auth/Source/Public/FIROAuthCredential.h @@ -36,11 +36,6 @@ NS_SWIFT_NAME(OAuthCredential) */ @property(nonatomic, readonly, nullable) NSString *accessToken; -/** @property pendingToken - @brief The pending token used when completing the headful-lite flow. - */ -@property(nonatomic, readonly, nullable) NSString *pendingToken; - /** @fn init @brief This class is not supposed to be instantiated directly. */ diff --git a/Firebase/Auth/Source/Public/FIROAuthProvider.h b/Firebase/Auth/Source/Public/FIROAuthProvider.h index 1ddf1f834df..e921e27286d 100644 --- a/Firebase/Auth/Source/Public/FIROAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIROAuthProvider.h @@ -72,22 +72,6 @@ NS_SWIFT_NAME(OAuthProvider) */ + (FIROAuthProvider *)providerWithProviderID:(NSString *)providerID auth:(FIRAuth *)auth; -/** @fn credentialWithProviderID:IDToken:accessToken: - @brief Creates an `FIRAuthCredential` for that OAuth 2 provider identified by providerID, ID - token and access token. - - @param providerID The provider ID associated with the Auth credential being created. - @param IDToken The IDToken associated with the Auth credential being created. - @param accessToken The accessstoken associated with the Auth credential be created, if - available. - @param pendingToken The pending token used when completing the headful-lite flow. - @return A FIRAuthCredential for the specified provider ID, ID token and access token. - */ -+ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID - IDToken:(NSString *)IDToken - accessToken:(nullable NSString *)accessToken - pendingToken:(nullable NSString *)pendingToken; - /** @fn credentialWithProviderID:IDToken:accessToken: @brief Creates an `FIRAuthCredential` for that OAuth 2 provider identified by providerID, ID token and access token. From 145c111d1aee07e144f0c3f1a4100b10b466bb29 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 29 Mar 2019 16:26:27 -0700 Subject: [PATCH 120/214] Minimum osx to 10.11 (#2680) --- Example/Firebase.xcodeproj/project.pbxproj | 4 ++-- Example/Podfile | 8 ++++---- FirebaseAnalyticsInterop.podspec | 2 +- FirebaseAuth.podspec | 2 +- FirebaseAuthInterop.podspec | 2 +- FirebaseCore.podspec | 2 +- FirebaseDatabase.podspec | 2 +- FirebaseFirestore.podspec | 2 +- FirebaseStorage.podspec | 2 +- .../Example/Firestore.xcodeproj/project.pbxproj | 14 +++++++------- Firestore/Example/GoogleBenchmark.podspec | 2 +- Firestore/Example/GoogleTest.podspec | 2 +- Firestore/Example/LibFuzzer.podspec | 2 +- Firestore/Example/Podfile | 2 +- Firestore/Example/ProtobufCpp.podspec | 2 +- .../FrameworkMaker.xcodeproj/project.pbxproj | 4 ++-- GoogleUtilities.podspec | 2 +- GoogleUtilities/Example/Podfile | 2 +- 18 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 1e07faddddb..d0b9a4c0ba5 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -5977,7 +5977,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -6015,7 +6015,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; diff --git a/Example/Podfile b/Example/Podfile index 066f0a916e8..366f80fa1b4 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -149,7 +149,7 @@ target 'Auth_Sample' do end target 'Core_Example_macOS' do - platform :osx, '10.10' + platform :osx, '10.11' pod 'FirebaseCore', :path => '../' @@ -160,7 +160,7 @@ target 'Core_Example_macOS' do end target 'Auth_Example_macOS' do - platform :osx, '10.10' + platform :osx, '10.11' pod 'FirebaseAuth', :path => '../' @@ -171,7 +171,7 @@ target 'Auth_Example_macOS' do end target 'Database_Example_macOS' do - platform :osx, '10.10' + platform :osx, '10.11' pod 'FirebaseDatabase', :path => '../' @@ -185,7 +185,7 @@ target 'Database_Example_macOS' do end target 'Storage_Example_macOS' do - platform :osx, '10.10' + platform :osx, '10.11' pod 'FirebaseStorage', :path => '../' diff --git a/FirebaseAnalyticsInterop.podspec b/FirebaseAnalyticsInterop.podspec index bea3f9de282..8f3907c9118 100644 --- a/FirebaseAnalyticsInterop.podspec +++ b/FirebaseAnalyticsInterop.podspec @@ -22,7 +22,7 @@ Pod::Spec.new do |s| } s.social_media_url = 'https://twitter.com/Firebase' s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.source_files = 'Interop/Analytics/**/*.h' s.public_header_files = 'Interop/Analytics/Public/*.h' diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index 9c2383918e1..e6b52d6fe2b 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -18,7 +18,7 @@ supports email and password accounts, as well as several 3rd party authenticatio } s.social_media_url = 'https://twitter.com/Firebase' s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' diff --git a/FirebaseAuthInterop.podspec b/FirebaseAuthInterop.podspec index 4ee7710652c..df08e2a64da 100644 --- a/FirebaseAuthInterop.podspec +++ b/FirebaseAuthInterop.podspec @@ -22,7 +22,7 @@ Pod::Spec.new do |s| } s.social_media_url = 'https://twitter.com/Firebase' s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.source_files = 'Interop/Auth/**/*.h' s.public_header_files = 'Interop/Auth/Public/*.h' diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 2a74d92b063..47a714ba1e7 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -17,7 +17,7 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration } s.social_media_url = 'https://twitter.com/Firebase' s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' diff --git a/FirebaseDatabase.podspec b/FirebaseDatabase.podspec index 062db75bd7a..e9ddac19386 100644 --- a/FirebaseDatabase.podspec +++ b/FirebaseDatabase.podspec @@ -17,7 +17,7 @@ Simplify your iOS development, grow your user base, and monetize more effectivel } s.social_media_url = 'https://twitter.com/Firebase' s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index a602a5c81a1..e6b958f884e 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -17,7 +17,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, } s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' diff --git a/FirebaseStorage.podspec b/FirebaseStorage.podspec index d9a9441142c..01c9152ea21 100644 --- a/FirebaseStorage.podspec +++ b/FirebaseStorage.podspec @@ -17,7 +17,7 @@ Firebase Storage provides robust, secure file uploads and downloads from Firebas } s.social_media_url = 'https://twitter.com/Firebase' s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index ca232d6e0c4..b3ecfa8f1ed 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -2554,7 +2554,7 @@ "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-macOS/openssl_grpc.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-macOS/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities-Environment-Logger-macOS/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/Protobuf-macOS10.10/Protobuf.framework", + "${BUILT_PRODUCTS_DIR}/Protobuf-macOS10.11/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/gRPC-C++-macOS/grpcpp.framework", "${BUILT_PRODUCTS_DIR}/gRPC-Core-macOS/grpc.framework", "${BUILT_PRODUCTS_DIR}/leveldb-library-macOS/leveldb.framework", @@ -3671,7 +3671,7 @@ ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -3746,7 +3746,7 @@ ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -4149,7 +4149,7 @@ ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -4218,7 +4218,7 @@ ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -4894,7 +4894,7 @@ GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = "$(SRCROOT)/App/macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -4966,7 +4966,7 @@ GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = "$(SRCROOT)/App/macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( diff --git a/Firestore/Example/GoogleBenchmark.podspec b/Firestore/Example/GoogleBenchmark.podspec index 156ee2aface..1046b988d2d 100644 --- a/Firestore/Example/GoogleBenchmark.podspec +++ b/Firestore/Example/GoogleBenchmark.podspec @@ -34,7 +34,7 @@ Google's C++ benchmark framework. } s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.requires_arc = false diff --git a/Firestore/Example/GoogleTest.podspec b/Firestore/Example/GoogleTest.podspec index 784f69e63c6..8feab54a195 100644 --- a/Firestore/Example/GoogleTest.podspec +++ b/Firestore/Example/GoogleTest.podspec @@ -34,7 +34,7 @@ Google's C++ test framework. } s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.requires_arc = false diff --git a/Firestore/Example/LibFuzzer.podspec b/Firestore/Example/LibFuzzer.podspec index ae99f46c62b..569b6116714 100644 --- a/Firestore/Example/LibFuzzer.podspec +++ b/Firestore/Example/LibFuzzer.podspec @@ -31,7 +31,7 @@ Pod::Spec.new do |s| # LibFuzzer uses thread_local which does not appear to be supported before # iOS 9. s.ios.deployment_target = '9.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' # Check out only libFuzzer folder. diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index a816a42cc8c..2259266b02e 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -61,7 +61,7 @@ target 'Firestore_Example_iOS' do end target 'Firestore_Example_macOS' do - platform :osx, '10.10' + platform :osx, '10.11' pod 'FirebaseAuth', :path => '../../' pod 'FirebaseCore', :path => '../../' diff --git a/Firestore/Example/ProtobufCpp.podspec b/Firestore/Example/ProtobufCpp.podspec index e2527bdae7c..14721069a1f 100644 --- a/Firestore/Example/ProtobufCpp.podspec +++ b/Firestore/Example/ProtobufCpp.podspec @@ -30,7 +30,7 @@ Pod::Spec.new do |s| } s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.source_files = 'src/**/*.{h,cc}' diff --git a/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj b/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj index 4a1e9d48833..f88531043f1 100644 --- a/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj +++ b/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj @@ -288,7 +288,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -327,7 +327,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/GoogleUtilities.podspec b/GoogleUtilities.podspec index b3dac0c0bdf..813ebe7590d 100644 --- a/GoogleUtilities.podspec +++ b/GoogleUtilities.podspec @@ -19,7 +19,7 @@ other Google CocoaPods. They're not intended for direct public usage. # Technically GoogleUtilites requires iOS 7, but it supports a dependency pod with a minimum # iOS 6, that will do runtime checking to avoid calling into GoogleUtilities. s.ios.deployment_target = '6.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '10.0' s.cocoapods_version = '>= 1.4.0' diff --git a/GoogleUtilities/Example/Podfile b/GoogleUtilities/Example/Podfile index c880a36f3fa..aa230a65d94 100644 --- a/GoogleUtilities/Example/Podfile +++ b/GoogleUtilities/Example/Podfile @@ -22,7 +22,7 @@ target 'Example_iOS' do end target 'Example_macOS' do - platform :osx, '10.10' + platform :osx, '10.11' pod 'GoogleUtilities', :path => '../../' From 22cdb796be04dfde717b93b60cd56f9e807d99a9 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Fri, 29 Mar 2019 20:00:33 -0700 Subject: [PATCH 121/214] Fix a couple expected error messages in tests to match recent code changes. (#2685) --- .../Integration/API/FIRValidationTests.mm | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index 15cfc004238..a4233228204 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -40,14 +40,14 @@ @implementation FIRValidationTests - (void)testNilHostFails { FIRFirestoreSettings *settings = self.db.settings; FSTAssertThrows(settings.host = nil, - @"host setting may not be nil. You should generally just use the default value " + @"Host setting may not be nil. You should generally just use the default value " "(which is firestore.googleapis.com)"); } - (void)testNilDispatchQueueFails { FIRFirestoreSettings *settings = self.db.settings; FSTAssertThrows(settings.dispatchQueue = nil, - @"dispatch queue setting may not be nil. Create a new dispatch queue with " + @"Dispatch queue setting may not be nil. Create a new dispatch queue with " "dispatch_queue_create(\"com.example.MyQueue\", NULL) or just use the default " "(which is the main queue, returned from dispatch_get_main_queue())"); } @@ -394,18 +394,18 @@ - (void)testQueryWithNonPositiveLimitFails { - (void)testNonEqualityQueriesOnNullOrNaNFail { FSTAssertThrows([[self collectionRef] queryWhereField:@"a" isGreaterThan:nil], - @"Invalid Query. You can only perform equality comparisons on nil / NSNull."); + @"Invalid Query. Nil and NSNull only support equality comparisons."); FSTAssertThrows([[self collectionRef] queryWhereField:@"a" isGreaterThan:[NSNull null]], - @"Invalid Query. You can only perform equality comparisons on nil / NSNull."); + @"Invalid Query. Nil and NSNull only support equality comparisons."); FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContains:nil], - @"Invalid Query. You can only perform equality comparisons on nil / NSNull."); + @"Invalid Query. Nil and NSNull only support equality comparisons."); FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContains:[NSNull null]], - @"Invalid Query. You can only perform equality comparisons on nil / NSNull."); + @"Invalid Query. Nil and NSNull only support equality comparisons."); FSTAssertThrows([[self collectionRef] queryWhereField:@"a" isGreaterThan:@(NAN)], - @"Invalid Query. You can only perform equality comparisons on NaN."); + @"Invalid Query. NaN only supports equality comparisons."); FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContains:@(NAN)], - @"Invalid Query. You can only perform equality comparisons on NaN."); + @"Invalid Query. NaN only supports equality comparisons."); } - (void)testQueryCannotBeCreatedFromDocumentsMissingSortValues { @@ -704,7 +704,7 @@ - (void)expectFieldPath:(NSString *)fieldPath toFailWithReason:(NSString *)reaso - (void)verifyExceptionForInvalidLatitude:(double)latitude { NSString *reason = [NSString - stringWithFormat:@"GeoPoint requires a latitude value in the range of [-90, 90], but was %f", + stringWithFormat:@"GeoPoint requires a latitude value in the range of [-90, 90], but was %g", latitude]; FSTAssertThrows([[FIRGeoPoint alloc] initWithLatitude:latitude longitude:0], reason); } @@ -712,7 +712,7 @@ - (void)verifyExceptionForInvalidLatitude:(double)latitude { - (void)verifyExceptionForInvalidLongitude:(double)longitude { NSString *reason = [NSString stringWithFormat: - @"GeoPoint requires a longitude value in the range of [-180, 180], but was %f", + @"GeoPoint requires a longitude value in the range of [-180, 180], but was %g", longitude]; FSTAssertThrows([[FIRGeoPoint alloc] initWithLatitude:0 longitude:longitude], reason); } From 8ef6cf80bee255f9ce81460422986eb29cb7eb52 Mon Sep 17 00:00:00 2001 From: Gil Date: Fri, 29 Mar 2019 20:40:48 -0700 Subject: [PATCH 122/214] Fix use-after-free in DocumentReference::AddSnapshotListener (#2686) --- Firestore/CHANGELOG.md | 2 ++ .../API/FIRListenerRegistrationTests.mm | 26 +++++++++++++++++++ .../firestore/api/document_reference.mm | 17 ++++++------ 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 7a00e406a95..e6a6b657e71 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased - [feature] Added community support for tvOS. +- [fixed] Fixed a use-after-free bug that could be observed when using snapshot + listeners on temporary document references (#2682). # 1.2.0 - [feature] Added community support for macOS (#434). diff --git a/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm b/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm index 036ab32b7a1..9cb33c2438e 100644 --- a/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm @@ -128,4 +128,30 @@ - (void)testCanBeRemovedIndependently { [two remove]; } +- (void)testCanOutliveDocumentReference { + FIRCollectionReference *collectionRef = [self collectionRef]; + + XCTestExpectation *seen = [self expectationWithDescription:@"seen document"]; + + __block id registration; + NSString *documentID; + @autoreleasepool { + FIRDocumentReference *docRef = [collectionRef documentWithAutoID]; + documentID = docRef.documentID; + registration = [docRef addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) { + if (snapshot.exists) { + [seen fulfill]; + } + }]; + docRef = nil; + } + + XCTAssertNotNil(registration); + + FIRDocumentReference *docRef2 = [collectionRef documentWithPath:documentID]; + [self writeDocumentRef:docRef2 data:@{@"foo" : @"bar"}]; + + [registration remove]; +} + @end diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index f61f54bd7c4..fdd8993b67a 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -201,7 +201,9 @@ void Resolve(ListenerRegistration&& registration) { public: Converter(DocumentReference* parent, DocumentSnapshot::Listener&& user_listener) - : parent_(parent), user_listener_(std::move(user_listener)) { + : firestore_(parent->firestore_), + key_(parent->key_), + user_listener_(std::move(user_listener)) { } void OnEvent(StatusOr maybe_snapshot) override { @@ -209,26 +211,25 @@ void OnEvent(StatusOr maybe_snapshot) override { user_listener_->OnEvent(maybe_snapshot.status()); return; } - Firestore* firestore = parent_->firestore_; - DocumentKey key = parent_->key_; ViewSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie(); HARD_ASSERT(snapshot.documents().size() <= 1, "Too many documents returned on a document query"); - FSTDocument* document = snapshot.documents().GetDocument(key); + FSTDocument* document = snapshot.documents().GetDocument(key_); bool has_pending_writes = - document ? snapshot.mutated_keys().contains(key) + document ? snapshot.mutated_keys().contains(key_) // We don't raise `has_pending_writes` for deleted documents. : false; - DocumentSnapshot result{firestore, std::move(key), document, - snapshot.from_cache(), has_pending_writes}; + DocumentSnapshot result{firestore_, key_, document, snapshot.from_cache(), + has_pending_writes}; user_listener_->OnEvent(std::move(result)); } private: - DocumentReference* parent_; + Firestore* firestore_; + DocumentKey key_; DocumentSnapshot::Listener user_listener_; }; auto view_listener = From c416b10ad478c5a740d88f651add0314b8acb880 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Sun, 31 Mar 2019 19:16:33 -0400 Subject: [PATCH 123/214] Move reporting Firebase user agent from checkin to register (#2679) --- .../Tests/FIRInstanceIDCheckinServiceTest.m | 32 -------------- .../Tests/FIRInstanceIDTokenOperationsTest.m | 42 +++++++++++++++++++ .../InstanceID/FIRInstanceIDCheckinService.h | 1 - .../InstanceID/FIRInstanceIDCheckinService.m | 6 --- .../FIRInstanceIDTokenFetchOperation.h | 3 ++ .../FIRInstanceIDTokenFetchOperation.m | 5 +++ 6 files changed, 50 insertions(+), 39 deletions(-) diff --git a/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m b/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m index 115ed856ffb..608ac5de461 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDCheckinServiceTest.m @@ -16,7 +16,6 @@ #import -#import #import #import "Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h" #import "Firebase/InstanceID/FIRInstanceIDCheckinPreferences.h" @@ -103,37 +102,6 @@ - (void)testFailedCheckinService { }]; } -- (void)testCheckinServiceAddsFirebaseUserAgentToHTTPHeader { - NSString *expectedFirebaseUserAgent = [FIRApp firebaseUserAgent]; - - FIRInstanceIDURLRequestTestBlock successHandler = [self successfulCheckinCompletionHandler]; - - [FIRInstanceIDCheckinService - setCheckinTestBlock:^(NSURLRequest *request, - FIRInstanceIDURLRequestTestResponseBlock response) { - NSString *requestFirebaseUserAgentValue = - request.allHTTPHeaderFields[kFIRInstanceIDFirebaseUserAgentKey]; - XCTAssertEqualObjects(requestFirebaseUserAgentValue, expectedFirebaseUserAgent); - successHandler(request, response); - }]; - - XCTestExpectation *checkinCompletionExpectation = - [self expectationWithDescription:@"Checkin Completion"]; - - [self.checkinService - checkinWithExistingCheckin:nil - completion:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) { - [checkinCompletionExpectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:5 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Checkin Timeout Error: %@", error); - } - }]; -} - - (void)testCheckinServiceFailsWithErrorAfterStopFetching { [self.checkinService stopFetching]; diff --git a/Example/InstanceID/Tests/FIRInstanceIDTokenOperationsTest.m b/Example/InstanceID/Tests/FIRInstanceIDTokenOperationsTest.m index 135f8cb2085..dd7cfd7cb99 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDTokenOperationsTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDTokenOperationsTest.m @@ -32,6 +32,8 @@ #import "Firebase/InstanceID/NSError+FIRInstanceID.h" #import "Firebase/InstanceID/Public/FIRInstanceID.h" +#import + static NSString *kDeviceID = @"fakeDeviceID"; static NSString *kSecretToken = @"fakeSecretToken"; static NSString *kDigestString = @"test-digest"; @@ -317,6 +319,46 @@ - (void)testHTTPAuthHeaderGenerationFromCheckin { XCTAssertEqualObjects(generatedHeader, expectedHeader); } +- (void)testTokenFetchOperationFirebaseUserAgentHeader { + XCTestExpectation *completionExpectation = + [self expectationWithDescription:@"completionExpectation"]; + + FIRInstanceIDCheckinPreferences *checkinPreferences = + [self setCheckinPreferencesWithLastCheckinTime:0]; + + FIRInstanceIDTokenFetchOperation *operation = + [[FIRInstanceIDTokenFetchOperation alloc] initWithAuthorizedEntity:kAuthorizedEntity + scope:kScope + options:nil + checkinPreferences:checkinPreferences + keyPair:self.keyPair]; + operation.testBlock = + ^(NSURLRequest *request, FIRInstanceIDURLRequestTestResponseBlock response) { + NSString *userAgentValue = request.allHTTPHeaderFields[kFIRInstanceIDFirebaseUserAgentKey]; + XCTAssertEqualObjects(userAgentValue, [FIRApp firebaseUserAgent]); + + // Return a response with Error=RST + NSData *responseBody = [self dataForFetchRequest:request returnValidToken:NO]; + NSHTTPURLResponse *responseObject = [[NSHTTPURLResponse alloc] initWithURL:request.URL + statusCode:200 + HTTPVersion:@"HTTP/1.1" + headerFields:nil]; + response(responseBody, responseObject, nil); + }; + + [operation addCompletionHandler:^(FIRInstanceIDTokenOperationResult result, + NSString *_Nullable token, NSError *_Nullable error) { + [completionExpectation fulfill]; + }]; + + [operation start]; + + [self waitForExpectationsWithTimeout:0.25 + handler:^(NSError *_Nullable error) { + XCTAssertNil(error.localizedDescription); + }]; +} + #pragma mark - Internal Helpers - (NSData *)dataForFetchRequest:(NSURLRequest *)request returnValidToken:(BOOL)returnValidToken { NSString *response; diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinService.h b/Firebase/InstanceID/FIRInstanceIDCheckinService.h index 9d05eb4514a..cc97e4700a3 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinService.h +++ b/Firebase/InstanceID/FIRInstanceIDCheckinService.h @@ -28,7 +28,6 @@ FOUNDATION_EXPORT NSString *const kFIRInstanceIDLastCheckinTimeKey; FOUNDATION_EXPORT NSString *const kFIRInstanceIDVersionInfoStringKey; FOUNDATION_EXPORT NSString *const kFIRInstanceIDGServicesDictionaryKey; FOUNDATION_EXPORT NSString *const kFIRInstanceIDDeviceDataVersionKey; -FOUNDATION_EXPORT NSString *const kFIRInstanceIDFirebaseUserAgentKey; @class FIRInstanceIDCheckinPreferences; diff --git a/Firebase/InstanceID/FIRInstanceIDCheckinService.m b/Firebase/InstanceID/FIRInstanceIDCheckinService.m index 7392a9dd5d4..be5b01a1a46 100644 --- a/Firebase/InstanceID/FIRInstanceIDCheckinService.m +++ b/Firebase/InstanceID/FIRInstanceIDCheckinService.m @@ -16,7 +16,6 @@ #import "FIRInstanceIDCheckinService.h" -#import #import "FIRInstanceIDCheckinPreferences+Internal.h" #import "FIRInstanceIDCheckinPreferences_Private.h" #import "FIRInstanceIDDefines.h" @@ -35,7 +34,6 @@ NSString *const kFIRInstanceIDVersionInfoStringKey = @"GMSInstanceIDVersionInfo"; NSString *const kFIRInstanceIDGServicesDictionaryKey = @"GMSInstanceIDGServicesData"; NSString *const kFIRInstanceIDDeviceDataVersionKey = @"GMSInstanceIDDeviceDataVersion"; -NSString *const kFIRInstanceIDFirebaseUserAgentKey = @"X-firebase-client"; static NSUInteger const kCheckinType = 2; // DeviceType IOS in l/w/a/_checkin.proto static NSUInteger const kCheckinVersion = 2; @@ -85,11 +83,7 @@ - (void)checkinWithExistingCheckin:(FIRInstanceIDCheckinPreferences *)existingCh NSURL *url = [NSURL URLWithString:kDeviceCheckinURL]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [request setValue:@"application/json" forHTTPHeaderField:@"content-type"]; - [request setValue:[FIRApp firebaseUserAgent] - forHTTPHeaderField:kFIRInstanceIDFirebaseUserAgentKey]; - NSDictionary *checkinParameters = [self checkinParametersWithExistingCheckin:existingCheckin]; NSData *checkinData = [NSJSONSerialization dataWithJSONObject:checkinParameters options:0 diff --git a/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h b/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h index 83ac71411c5..87be60fc061 100644 --- a/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h +++ b/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.h @@ -17,6 +17,9 @@ #import "FIRInstanceIDTokenOperation.h" NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT NSString *const kFIRInstanceIDFirebaseUserAgentKey; + @interface FIRInstanceIDTokenFetchOperation : FIRInstanceIDTokenOperation - (instancetype)initWithAuthorizedEntity:(NSString *)authorizedEntity diff --git a/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m b/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m index c2df1f7ed0b..b545c24c3ea 100644 --- a/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m +++ b/Firebase/InstanceID/FIRInstanceIDTokenFetchOperation.m @@ -25,11 +25,14 @@ #import "FIRInstanceIDUtilities.h" #import "NSError+FIRInstanceID.h" +#import + // We can have a static int since this error should theoretically only // happen once (for the first time). If it repeats there is something // else that is wrong. static int phoneRegistrationErrorRetryCount = 0; static const int kMaxPhoneRegistrationErrorRetryCount = 10; +NSString *const kFIRInstanceIDFirebaseUserAgentKey = @"X-firebase-client"; @implementation FIRInstanceIDTokenFetchOperation @@ -55,6 +58,8 @@ - (void)performTokenOperation { NSMutableURLRequest *request = [[self class] requestWithAuthHeader:authHeader]; NSString *checkinVersionInfo = self.checkinPreferences.versionInfo; [request setValue:checkinVersionInfo forHTTPHeaderField:@"info"]; + [request setValue:[FIRApp firebaseUserAgent] + forHTTPHeaderField:kFIRInstanceIDFirebaseUserAgentKey]; // Build form-encoded body NSString *deviceAuthID = self.checkinPreferences.deviceID; From 1dd5dfa9910d82d664900fc0f5978469341e16f4 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 1 Apr 2019 06:35:28 -0700 Subject: [PATCH 124/214] Merge 5.20.1 back to master (#2691) --- Example/Podfile | 2 +- FirebaseFirestore.podspec | 2 +- Firestore/CHANGELOG.md | 2 ++ Firestore/Example/Podfile | 2 +- SymbolCollisionTest/Podfile | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Example/Podfile b/Example/Podfile index 366f80fa1b4..2af8a35c371 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -19,7 +19,7 @@ target 'Core_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.20.0' + pod 'Firebase/CoreOnly', '5.20.1' target 'Core_Tests_iOS' do inherit! :search_paths diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index e6b958f884e..97477b46e4d 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFirestore' - s.version = '1.2.0' + s.version = '1.2.1' s.summary = 'Google Cloud Firestore for iOS' s.description = <<-DESC diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index e6a6b657e71..953a791eb32 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased - [feature] Added community support for tvOS. + +# 1.2.1 - [fixed] Fixed a use-after-free bug that could be observed when using snapshot listeners on temporary document references (#2682). diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index 2259266b02e..be25897e37c 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -14,7 +14,7 @@ target 'Firestore_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.20.0' + pod 'Firebase/CoreOnly', '5.20.1' pod 'FirebaseAuth', :path => '../../' pod 'FirebaseAuthInterop', :path => '../../' diff --git a/SymbolCollisionTest/Podfile b/SymbolCollisionTest/Podfile index 247db236377..88cf453a369 100644 --- a/SymbolCollisionTest/Podfile +++ b/SymbolCollisionTest/Podfile @@ -6,7 +6,7 @@ target 'SymbolCollisionTest' do # use_frameworks! # Firebase Pods - pod 'Firebase', '5.20.0' + pod 'Firebase', '5.20.1' pod 'FirebaseAnalytics' pod 'FirebaseAuth' pod 'FirebaseCore' From 1e29a3476a66ee876000a4db408c7191590fefcd Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Mon, 1 Apr 2019 12:47:00 -0400 Subject: [PATCH 125/214] Fix CMake build (#2694) --- .../core/src/firebase/firestore/api/input_validation_std.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firestore/core/src/firebase/firestore/api/input_validation_std.cc b/Firestore/core/src/firebase/firestore/api/input_validation_std.cc index cd01de20f4f..212271e800e 100644 --- a/Firestore/core/src/firebase/firestore/api/input_validation_std.cc +++ b/Firestore/core/src/firebase/firestore/api/input_validation_std.cc @@ -30,7 +30,7 @@ namespace { template [[noreturn]] void Throw(const char* kind, const T& error) { - LOG_ERROR(ERROR, "%s: %s", kind, error.what()); + LOG_ERROR("%s: %s", kind, error.what()); std::abort(); } From 3aff8d076063e051129009bf9049c0291c6210e5 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Mon, 1 Apr 2019 14:32:44 -0700 Subject: [PATCH 126/214] Add support of sso (#2684) --- Example/Auth/Sample/SettingsViewController.m | 44 +++-- .../Tests/FIRAuthUserDefaultsStorageTests.m | 4 - Firebase/Auth/Source/FIRAuth.m | 174 ++++++++++++++---- Firebase/Auth/Source/FIRAuthKeychain.h | 27 +++ Firebase/Auth/Source/FIRAuthKeychain.m | 118 +++++++++--- .../Auth/Source/FIRAuthStoredUserManager.h | 99 ++++++++++ .../Auth/Source/FIRAuthStoredUserManager.m | 125 +++++++++++++ .../Auth/Source/FIRAuthUserDefaultsStorage.h | 11 -- .../Auth/Source/FIRAuthUserDefaultsStorage.m | 4 - .../Source/Public/FIRActionCodeSettings.h | 2 +- Firebase/Auth/Source/Public/FIRAuth.h | 24 ++- Firebase/Auth/Source/Public/FIRAuthSettings.h | 2 +- .../Auth/Source/Public/FIRAuthTokenResult.h | 12 +- Firebase/Auth/Source/Public/FIRUserMetadata.h | 4 +- .../RPCs/FIRGetOOBConfirmationCodeRequest.h | 2 +- 15 files changed, 549 insertions(+), 103 deletions(-) create mode 100644 Firebase/Auth/Source/FIRAuthStoredUserManager.h create mode 100644 Firebase/Auth/Source/FIRAuthStoredUserManager.m diff --git a/Example/Auth/Sample/SettingsViewController.m b/Example/Auth/Sample/SettingsViewController.m index 07d4828fe11..b1e35f327e1 100644 --- a/Example/Auth/Sample/SettingsViewController.m +++ b/Example/Auth/Sample/SettingsViewController.m @@ -58,6 +58,11 @@ @"GoogleService-Info_multi" }; +/** @var kSharedKeychainAccessGroup + @brief The shared keychain access group for testing. + */ +static NSString *const kSharedKeychainAccessGroup = @"com.google.firebase.auth.keychainGroup1"; + /** @var gAPIEndpoints @brief List of API Hosts by request class name. */ @@ -155,6 +160,9 @@ - (void)setUpFirebaseAppOptions { } - (void)loadTableView { + NSString *appIdentifierPrefix = NSBundle.mainBundle.infoDictionary[@"AppIdentifierPrefix"]; + NSString *fullKeychainAccessGroup = [appIdentifierPrefix stringByAppendingString:kSharedKeychainAccessGroup]; + __weak typeof(self) weakSelf = self; _tableViewManager.contents = [StaticContentTableViewContent contentWithSections:@[ [StaticContentTableViewSection sectionWithTitle:@"Versions" cells:@[ @@ -191,6 +199,23 @@ - (void)loadTableView { [weakSelf toggleProjectForAppAtIndex:1]; }], ]], + [StaticContentTableViewSection sectionWithTitle:@"Keychain Access Groups" cells:@[ + [StaticContentTableViewCell cellWithTitle:@"Current Access Group" + value:[AppManager auth].userAccessGroup ? [AppManager auth].userAccessGroup : @"[none]" + ], + [StaticContentTableViewCell cellWithTitle:@"Default Group" + value:@"[none]" + action:^{ + [[AppManager auth] useUserAccessGroup:nil error:nil]; + [self loadTableView]; + }], + [StaticContentTableViewCell cellWithTitle:@"Shared Group" + value:fullKeychainAccessGroup + action:^{ + [[AppManager auth] useUserAccessGroup:fullKeychainAccessGroup error:nil]; + [self loadTableView]; + }], + ]], [StaticContentTableViewSection sectionWithTitle:@"Phone Auth" cells:@[ [StaticContentTableViewCell cellWithTitle:@"APNs Token" value:[self APNSTokenString] @@ -281,19 +306,14 @@ - (NSString *)projectIDForAppAtIndex:(int)index { return @"[none]"; } -/** @fn googleAppIDForAppAtIndex: - @brief Returns the Google App ID for the Firebase app at the given index. - @param index The index for the app in the app manager. - @return The Google App ID of the project. +/** @fn projectIDForAppAtIndex: + @brief Returns the Firebase project ID for the Firebase app at the given index. + @param index The index for the app in the app manager. + @return The ID of the project. */ -- (NSString *)googleAppIDForAppAtIndex:(int)index { - NSString *APIKey = [[AppManager sharedInstance] appAtIndex:index].options.APIKey; - for (FIROptions *options in gFirebaseAppOptions) { - if ([options.APIKey isEqualToString:APIKey]) { - return options.googleAppID; - } - } - return @"[none]"; +- (NSString *)keychainAccessGroupAtIndex:(int)index { + NSArray *array = @[@"123", @"456"]; + return array[index]; } /** @fn toggleProjectForAppAtIndex: diff --git a/Example/Auth/Tests/FIRAuthUserDefaultsStorageTests.m b/Example/Auth/Tests/FIRAuthUserDefaultsStorageTests.m index 07493d5d8ef..e7e081b3e22 100644 --- a/Example/Auth/Tests/FIRAuthUserDefaultsStorageTests.m +++ b/Example/Auth/Tests/FIRAuthUserDefaultsStorageTests.m @@ -18,8 +18,6 @@ #import "FIRAuthUserDefaultsStorage.h" -#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE - NS_ASSUME_NONNULL_BEGIN /** @var kKey @@ -151,5 +149,3 @@ - (void)testStandardUserDefaults { @end NS_ASSUME_NONNULL_END - -#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m index efb3a487dc6..6a8151afb24 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/FIRAuth.m @@ -42,6 +42,7 @@ #import "FIRAuthKeychain.h" #import "FIRAuthOperationType.h" #import "FIRAuthSettings.h" +#import "FIRAuthStoredUserManager.h" #import "FIRUser_Internal.h" #import "FirebaseAuth.h" #import "FIRAuthBackend.h" @@ -245,6 +246,11 @@ @interface FIRAuth () */ @property(nonatomic, copy, nullable) NSString *additionalFrameworkMarker; +/** @property storedUserManager + @brief The stored user manager. + */ +@property(nonatomic, strong, nullable) FIRAuthStoredUserManager *storedUserManager; + /** @fn initWithApp: @brief Creates a @c FIRAuth instance associated with the provided @c FIRApp instance. @param app The application to associate the auth instance with. @@ -390,11 +396,29 @@ - (nullable instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)a if (keychainServiceName) { strongSelf->_keychain = [[FIRAuthKeychain alloc] initWithService:keychainServiceName]; } - FIRUser *user; + + strongSelf.storedUserManager = + [[FIRAuthStoredUserManager alloc] initWithServiceName:keychainServiceName]; + NSError *error; - if ([strongSelf getUser:&user error:&error]) { - [strongSelf updateCurrentUser:user byForce:NO savingToDisk:NO error:&error]; - self->_lastNotifiedUserToken = user.rawAccessToken; + NSString *storedUserAccessGroup = [strongSelf.storedUserManager getStoredUserAccessGroupWithError:&error]; + if (!error) { + if (!storedUserAccessGroup) { + FIRUser *user; + if ([strongSelf getUser:&user error:&error]) { + [strongSelf updateCurrentUser:user byForce:NO savingToDisk:NO error:&error]; + self->_lastNotifiedUserToken = user.rawAccessToken; + } else { + FIRLogError(kFIRLoggerAuth, @"I-AUT000001", + @"Error loading saved user when starting up: %@", error); + } + } else { + [strongSelf useUserAccessGroup:storedUserAccessGroup error:&error]; + if (error) { + FIRLogError(kFIRLoggerAuth, @"I-AUT000001", + @"Error loading saved user when starting up: %@", error); + } + } } else { FIRLogError(kFIRLoggerAuth, @"I-AUT000001", @"Error loading saved user when starting up: %@", error); @@ -1855,26 +1879,40 @@ - (BOOL)updateCurrentUser:(nullable FIRUser *)user /** @fn saveUser:error: @brief Persists user. @param user The user to save. - @param error Return value for any error which occurs. + @param outError Return value for any error which occurs. @return @YES on success, @NO otherwise. */ - (BOOL)saveUser:(FIRUser *)user - error:(NSError *_Nullable *_Nullable)error { + error:(NSError *_Nullable *_Nullable)outError { BOOL success; - NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName]; - if (!user) { - success = [_keychain removeDataForKey:userKey error:error]; + if (!self.userAccessGroup) { + NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName]; + if (!user) { + success = [_keychain removeDataForKey:userKey error:outError]; + } else { + // Encode the user object. + NSMutableData *archiveData = [NSMutableData data]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData]; + [archiver encodeObject:user forKey:userKey]; + [archiver finishEncoding]; + + // Save the user object's encoded value. + success = [_keychain setData:archiveData forKey:userKey error:outError]; + } } else { - // Encode the user object. - NSMutableData *archiveData = [NSMutableData data]; - NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData]; - [archiver encodeObject:user forKey:userKey]; - [archiver finishEncoding]; - - // Save the user object's encoded value. - success = [_keychain setData:archiveData forKey:userKey error:error]; + if (!user) { + success = [self.storedUserManager removeStoredUserForAccessGroup:self.userAccessGroup + projectIdentifier:self.app.options.APIKey + error:outError]; + } else { + success = [self.storedUserManager setStoredUser:user + forAccessGroup:self.userAccessGroup + projectIdentifier:self.app.options.APIKey + error:outError]; + } } + return success; } @@ -1887,26 +1925,44 @@ - (BOOL)saveUser:(FIRUser *)user */ - (BOOL)getUser:(FIRUser *_Nullable *)outUser error:(NSError *_Nullable *_Nullable)error { - NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName]; + if (!self.userAccessGroup) { + NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName]; - NSError *keychainError; - NSData *encodedUserData = [_keychain dataForKey:userKey error:&keychainError]; - if (keychainError) { - if (error) { - *error = keychainError; + NSError *keychainError; + NSData *encodedUserData = [_keychain dataForKey:userKey error:&keychainError]; + if (keychainError) { + if (error) { + *error = keychainError; + } + return NO; } - return NO; - } - if (!encodedUserData) { - *outUser = nil; + if (!encodedUserData) { + *outUser = nil; + return YES; + } + NSKeyedUnarchiver *unarchiver = + [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData]; + FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey]; + user.auth = self; + *outUser = user; + return YES; + } else { + FIRUser *user = [self.storedUserManager getStoredUserForAccessGroup:self.userAccessGroup + projectIdentifier:self.app.options.APIKey + error:error]; + user.auth = self; + *outUser = user; + if (user) { + return YES; + } else { + if (error && *error) { + return NO; + } else { + return YES; + } + } } - NSKeyedUnarchiver *unarchiver = - [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData]; - FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey]; - user.auth = self; - *outUser = user; - return YES; } #pragma mark - Interoperability @@ -2011,6 +2067,58 @@ - (nullable NSString *)getUserID { return _currentUser.uid; } +#pragma mark - Keychain sharing + +- (BOOL)useUserAccessGroup:(NSString *_Nullable)accessGroup + error:(NSError *_Nullable *_Nullable)outError { + BOOL success; + success = [self.storedUserManager setStoredUserAccessGroup:accessGroup error:outError]; + if (!success) { + return NO; + } + + FIRUser *user = [self getStoredUserForAccessGroup:accessGroup error:outError]; + if (!user && outError && *outError) { + return NO; + } + success = [self updateCurrentUser:user byForce:NO savingToDisk:NO error:outError]; + if (!success) { + return NO; + } + + if(_userAccessGroup == nil && accessGroup != nil) { + NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName]; + [_keychain removeDataForKey:userKey error:outError]; + } + _userAccessGroup = accessGroup; + self->_lastNotifiedUserToken = user.rawAccessToken; + + return YES; +} + +- (FIRUser *)getStoredUserForAccessGroup:(NSString *_Nullable)accessGroup + error:(NSError *_Nullable *_Nullable)outError { + FIRUser *user; + if (!accessGroup) { + NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName]; + NSData *encodedUserData = [_keychain dataForKey:userKey error:outError]; + if (!encodedUserData) { + return nil; + } + + NSKeyedUnarchiver *unarchiver = + [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData]; + user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey]; + user.auth = self; + } else { + user = [self.storedUserManager getStoredUserForAccessGroup:self.userAccessGroup + projectIdentifier:self.app.options.APIKey + error:outError]; + } + + return user; +} + @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthKeychain.h b/Firebase/Auth/Source/FIRAuthKeychain.h index c52e26aafa5..f57d2730650 100644 --- a/Firebase/Auth/Source/FIRAuthKeychain.h +++ b/Firebase/Auth/Source/FIRAuthKeychain.h @@ -65,6 +65,33 @@ NS_ASSUME_NONNULL_BEGIN @brief The utility class to manipulate data in iOS Keychain. */ @interface FIRAuthKeychain : NSObject + +/** @fn getItemWithQuery:error: + @brief Get the item from keychain by given query. + @param query The query to query the keychain. + @param outError The address to store any error that occurs during the process, if not nil. + @return The item of the given query. nil if not exsit. + */ +- (NSData *)getItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError; + +/** @fn setItem:withQuery:error: + @brief Set the item into keychain with given query. + @param item The item to be added into keychain. + @param query The query to query the keychain. + @param outError The address to store any error that occurs during the process, if not nil. + @return Whether the operation succeed. + */ +- (BOOL)setItem:(NSData *)item withQuery:(NSDictionary *)query + error:(NSError *_Nullable *_Nullable)outError; + +/** @fn getItemWithQuery:error: + @brief Remove the item with given queryfrom keychain. + @param query The query to query the keychain. + @param outError The address to store any error that occurs during the process, if not nil. + @return Whether the operation succeed. + */ +- (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError; + @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthKeychain.m b/Firebase/Auth/Source/FIRAuthKeychain.m index d196244441e..9c264742818 100644 --- a/Firebase/Auth/Source/FIRAuthKeychain.m +++ b/Firebase/Auth/Source/FIRAuthKeychain.m @@ -21,17 +21,6 @@ #import "FIRAuthErrorUtils.h" #import "FIRAuthUserDefaultsStorage.h" -#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE -#import - -/** @var kOSVersionMatcherForUsingUserDefaults - @brief The regular expression to match all OS versions that @c FIRAuthUserDefaultsStorage is - used instead if available. - */ -static NSString *const kOSVersionMatcherForUsingUserDefaults = @"^10\\.[01](\\..*)?$"; - -#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE - /** @var kAccountPrefix @brief The prefix string for keychain item account attribute before the key. @remarks A number "1" is encoded in the prefix in case we need to upgrade the scheme in future. @@ -56,19 +45,6 @@ @implementation FIRAuthKeychain { - (id)initWithService:(NSString *)service { -#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE - - NSString *OSVersion = [UIDevice currentDevice].systemVersion; - NSRegularExpression *regex = - [NSRegularExpression regularExpressionWithPattern:kOSVersionMatcherForUsingUserDefaults - options:0 - error:NULL]; - if ([regex numberOfMatchesInString:OSVersion options:0 range:NSMakeRange(0, OSVersion.length)]) { - return (id)[[FIRAuthUserDefaultsStorage alloc] initWithService:service]; - } - -#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE - self = [super init]; if (self) { _service = [service copy]; @@ -141,7 +117,7 @@ - (BOOL)removeDataForKey:(NSString *)key error:(NSError **_Nullable)error { return YES; } -#pragma mark - Private +#pragma mark - Private methods for non-sharing keychain operations - (NSData *)itemWithQuery:(NSDictionary *)query error:(NSError **_Nullable)error { NSMutableDictionary *returningQuery = [query mutableCopy]; @@ -156,7 +132,7 @@ - (NSData *)itemWithQuery:(NSDictionary *)query error:(NSError **_Nullable)error (CFTypeRef *)&result); if (status == noErr && result != NULL) { - NSArray *items = (__bridge_transfer NSArray *)result; + NSArray *items = (__bridge_transfer NSArray *)result; if (items.count != 1) { if (error) { *error = [FIRAuthErrorUtils keychainErrorWithFunction:@"SecItemCopyMatching" @@ -255,6 +231,96 @@ - (NSDictionary *)legacyGenericPasswordQueryWithKey:(NSString *)key { }; } +#pragma mark - Private methods for shared keychain operations + +- (NSData *)getItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError { + NSMutableDictionary *mutableQuery = [query mutableCopy]; + + mutableQuery[(__bridge id)kSecReturnData] = @YES; + mutableQuery[(__bridge id)kSecReturnAttributes] = @YES; + mutableQuery[(__bridge id)kSecMatchLimit] = @2; + + CFArrayRef result = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)mutableQuery, + (CFTypeRef *)&result); + + if (status == noErr && result != NULL) { + NSArray *items = (__bridge_transfer NSArray *)result; + if (items.count != 1) { + if (outError) { + *outError = [FIRAuthErrorUtils keychainErrorWithFunction:@"SecItemCopyMatching" + status:status]; + } + return nil; + } + + if (outError) { + *outError = nil; + } + NSDictionary *item = items[0]; + return item[(__bridge id)kSecValueData]; + } + + if (status == errSecItemNotFound) { + if (outError) { + *outError = nil; + } + } else { + if (outError) { + *outError = [FIRAuthErrorUtils keychainErrorWithFunction:@"SecItemCopyMatching" status:status]; + } + } + return nil; +} + +- (BOOL)setItem:(NSData *)item + withQuery:(NSDictionary *)query + error:(NSError *_Nullable *_Nullable)outError { + NSData *existingItem = [self getItemWithQuery:query error:outError]; + if (outError && *outError) { + return NO; + } + + OSStatus status; + if (!existingItem) { + NSMutableDictionary *queryWithItem = [query mutableCopy]; + [queryWithItem setObject:item forKey:(__bridge id)kSecValueData]; + status = SecItemAdd((__bridge CFDictionaryRef)queryWithItem, NULL); + } else { + NSDictionary *attributes = @{(__bridge id)kSecValueData: item}; + status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes); + } + + if (status == noErr) { + if (outError) { + *outError = nil; + } + return YES; + } + + NSString *function = existingItem ? @"SecItemUpdate" : @"SecItemAdd"; + if (outError) { + *outError = [FIRAuthErrorUtils keychainErrorWithFunction:function status:status]; + } + return NO; +} + +- (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError { + OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); + + if (status == noErr || status == errSecItemNotFound) { + if (outError) { + *outError = nil; + } + return YES; + } + + if (outError) { + *outError = [FIRAuthErrorUtils keychainErrorWithFunction:@"SecItemDelete" status:status]; + } + return NO; +} + @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthStoredUserManager.h b/Firebase/Auth/Source/FIRAuthStoredUserManager.h new file mode 100644 index 00000000000..cbb6d3edce2 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthStoredUserManager.h @@ -0,0 +1,99 @@ +/* + * Copyright 2019 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 + +#import "FIRUser.h" +#import "FIRAuthKeychain.h" +#import "FIRAuthUserDefaultsStorage.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAuthStoredUserManager : NSObject + +/** @property keychain + @brief The mediator object to access to the system Keychain services. + */ +@property (readonly, nonatomic, strong) FIRAuthKeychain *keychain; + +/** @property userDefaults + @brief The mediator object to access to the system User Defaults services. + */ +@property (readonly, nonatomic, strong) FIRAuthUserDefaultsStorage *userDefaults; + +/** @fn init + @brief The default initializer is disabled. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithServiceName: + @brief The designated initializer. + @param serviceName The service name to initialize with. + */ +- (instancetype)initWithServiceName:(NSString *)serviceName NS_DESIGNATED_INITIALIZER; + +/** @fn getStoredUserAccessGroupWithError: + @brief Get the user access group stored locally. + @param outError Return value for any error which occurs. + */ +- (NSString *_Nullable)getStoredUserAccessGroupWithError:(NSError *_Nullable *_Nullable)outError; + +/** @fn setStoredUserAccessGroup:error: + @brief The setter of the user access group stored locally. + @param accessGroup The access group to be set. + @param outError Return value for any error which occurs. + */ +- (BOOL)setStoredUserAccessGroup:(NSString *_Nullable)accessGroup + error:(NSError *_Nullable *_Nullable)outError; + +/** @fn getStoredUserForAccessGroup:projectID:error: + @brief The getter of the user stored locally. + @param accessGroup The access group to retrieve the user from. + @param projectIdentifier An identifier of the project that the user associates with. Currently, + we use API KEY. + @param outError Return value for any error which occurs. + */ +- (FIRUser *)getStoredUserForAccessGroup:(NSString *)accessGroup + projectIdentifier:(NSString *)projectIdentifier + error:(NSError *_Nullable *_Nullable)outError; + +/** @fn setStoredUser:forAccessGroup:projectID:error: + @brief The setter of the user stored locally. + @param user The user to be stored. + @param accessGroup The access group to store the user in. + @param projectIdentifier An identifier of the project that the user associates with. Currently, + we use API KEY. + @param outError Return value for any error which occurs. + */ +- (BOOL)setStoredUser:(FIRUser *)user + forAccessGroup:(NSString *)accessGroup + projectIdentifier:(NSString *)projectIdentifier + error:(NSError *_Nullable *_Nullable)outError; + +/** @fn removeStoredUserForAccessGroup:projectID:error: + @brief Remove the user that stored locally. + @param accessGroup The access group to remove the user from. + @param projectIdentifier An identifier of the project that the user associates with. Currently, + we use API KEY. + @param outError Return value for any error which occurs. + */ +- (BOOL)removeStoredUserForAccessGroup:(NSString *)accessGroup + projectIdentifier:(NSString *)projectIdentifier + error:(NSError *_Nullable *_Nullable)outError; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthStoredUserManager.m b/Firebase/Auth/Source/FIRAuthStoredUserManager.m new file mode 100644 index 00000000000..6a66e70b3c1 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthStoredUserManager.m @@ -0,0 +1,125 @@ +/* + * Copyright 2019 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 "FIRAuthStoredUserManager.h" + +/** @var kUserAccessGroupKey + @brief Key of user access group stored in user defaults. Used for retrieve the user access + group at launch. + */ +static NSString *kStoredUserAccessGroupKey = @"firebase_auth_stored_user_access_group"; + +/** @var kSharedKeychainAccountValue + @brief Default value for kSecAttrAccount of shared keychain items. + */ +static NSString *kSharedKeychainAccountValue = @"firebase_auth_firebase_user"; + +/** @var kStoredUserCoderKey + @brief The key to encode and decode the stored user. + */ +static NSString *kStoredUserCoderKey = @"firebase_auth_stored_user_coder_key"; + +@implementation FIRAuthStoredUserManager + +#pragma mark - Initializers + +- (instancetype)initWithServiceName:(NSString *)serviceName { + self = [super init]; + if (self) { + _keychain = [[FIRAuthKeychain alloc] initWithService:serviceName]; + _userDefaults = [[FIRAuthUserDefaultsStorage alloc] initWithService:serviceName]; + } + return self; +} + +#pragma mark - User Access Group + +- (NSString *_Nullable)getStoredUserAccessGroupWithError:(NSError *_Nullable *_Nullable)outError { + NSData *data = [self.userDefaults dataForKey:kStoredUserAccessGroupKey error:outError]; + if (data) { + NSString *userAccessGroup = [NSString stringWithUTF8String:data.bytes]; + return userAccessGroup; + } else { + return nil; + } +} + +- (BOOL)setStoredUserAccessGroup:(NSString *_Nullable)accessGroup + error:(NSError *_Nullable *_Nullable)outError { + NSData *data = [accessGroup dataUsingEncoding:NSUTF8StringEncoding]; + if (!data) { + return [self.userDefaults removeDataForKey:kStoredUserAccessGroupKey error:outError]; + } else { + return [self.userDefaults setData:data forKey:kStoredUserAccessGroupKey error:outError]; + } +} + +#pragma mark - User for Access Group + +- (FIRUser *)getStoredUserForAccessGroup:(NSString *)accessGroup + projectIdentifier:(NSString *)projectIdentifier + error:(NSError *_Nullable *_Nullable)outError { + + + NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; + query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword; + + query[(__bridge id)kSecAttrAccessGroup] = accessGroup; + query[(__bridge id)kSecAttrService] = projectIdentifier; + query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue; + + NSData *data = [self.keychain getItemWithQuery:query error:outError]; + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; + FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:kStoredUserCoderKey]; + + return user; +} + +- (BOOL)setStoredUser:(FIRUser *)user + forAccessGroup:(NSString *)accessGroup + projectIdentifier:(NSString *)projectIdentifier + error:(NSError *_Nullable *_Nullable)outError { + NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; + query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword; + query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; + + query[(__bridge id)kSecAttrAccessGroup] = accessGroup; + query[(__bridge id)kSecAttrService] = projectIdentifier; + query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue; + + NSMutableData *data = [NSMutableData data]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; + [archiver encodeObject:user forKey:kStoredUserCoderKey]; + [archiver finishEncoding]; + + return [self.keychain setItem:data withQuery:query error:outError]; +} + +- (BOOL)removeStoredUserForAccessGroup:(NSString *)accessGroup + projectIdentifier:(NSString *)projectIdentifier + error:(NSError *_Nullable *_Nullable)outError { + NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; + query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword; + query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; + + query[(__bridge id)kSecAttrAccessGroup] = accessGroup; + query[(__bridge id)kSecAttrService] = projectIdentifier; + query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue; + + return [self.keychain removeItemWithQuery:query error:outError]; +} + +@end diff --git a/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.h b/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.h index 13774ab97ba..7fa2bec59ff 100644 --- a/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.h +++ b/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.h @@ -16,15 +16,6 @@ #import -// This class is only available in the simulator. -#if TARGET_OS_SIMULATOR -#ifndef FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE -#define FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE 1 -#endif -#endif - -#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE - #import "FIRAuthKeychain.h" NS_ASSUME_NONNULL_BEGIN @@ -43,5 +34,3 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END - -#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE diff --git a/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m b/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m index d9f012a96d3..52806d9488f 100644 --- a/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m +++ b/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m @@ -16,8 +16,6 @@ #import "FIRAuthUserDefaultsStorage.h" -#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE - NS_ASSUME_NONNULL_BEGIN static NSString *const kPersistentDomainNamePrefix = @"com.google.Firebase.Auth."; @@ -74,5 +72,3 @@ - (void)clear { @end NS_ASSUME_NONNULL_END - -#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE diff --git a/Firebase/Auth/Source/Public/FIRActionCodeSettings.h b/Firebase/Auth/Source/Public/FIRActionCodeSettings.h index 788e0609907..5382b5bcb92 100644 --- a/Firebase/Auth/Source/Public/FIRActionCodeSettings.h +++ b/Firebase/Auth/Source/Public/FIRActionCodeSettings.h @@ -62,7 +62,7 @@ /** @property dynamicLinkDomain @brief The Firebase Dynamic Link domain used for out of band code flow. */ - @property (copy, nonatomic, nullable) NSString *dynamicLinkDomain; + @property(copy, nonatomic, nullable) NSString *dynamicLinkDomain; /** @fn setIOSBundleID @brief Sets the iOS bundle Id. diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h index 25ee65dac84..ca567a0112d 100644 --- a/Firebase/Auth/Source/Public/FIRAuth.h +++ b/Firebase/Auth/Source/Public/FIRAuth.h @@ -289,12 +289,17 @@ NS_SWIFT_NAME(Auth) @remarks The string used to set this property must be a language code that follows BCP 47. */ -@property (nonatomic, copy, nullable) NSString *languageCode; +@property(nonatomic, copy, nullable) NSString *languageCode; /** @property settings @brief Contains settings related to the auth object. */ -@property (nonatomic, copy, nullable) FIRAuthSettings *settings; +@property(nonatomic, copy, nullable) FIRAuthSettings *settings; + +/** @property userAccessGroup + @brief The current user access group that the Auth instance is using. Default is nil. + */ +@property(readonly, nonatomic, copy, nullable) NSString *userAccessGroup; #if TARGET_OS_IOS /** @property APNSToken @@ -909,6 +914,21 @@ NS_SWIFT_NAME(Auth) #endif // TARGET_OS_IOS +#pragma mark - User sharing + +/** @fn useUserAccessGroup:error: + @brief Switch userAccessGroup and current user to the given accessGroup and the user stored in + it. + */ +- (BOOL)useUserAccessGroup:(NSString *_Nullable)accessGroup + error:(NSError *_Nullable *_Nullable)outError; + +/** @fn getStoredUserForAccessGroup:error: + @brief Get the stored user in the given accessGroup. + */ +- (FIRUser *)getStoredUserForAccessGroup:(NSString *_Nullable)accessGroup + error:(NSError *_Nullable *_Nullable)outError; + @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Public/FIRAuthSettings.h b/Firebase/Auth/Source/Public/FIRAuthSettings.h index 05fc60199c8..55097777f3b 100644 --- a/Firebase/Auth/Source/Public/FIRAuthSettings.h +++ b/Firebase/Auth/Source/Public/FIRAuthSettings.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN /** @property appVerificationDisabledForTesting @brief Flag to determine whether app verification should be disabled for testing or not. */ -@property (nonatomic, assign, getter=isAppVerificationDisabledForTesting) BOOL +@property(nonatomic, assign, getter=isAppVerificationDisabledForTesting) BOOL appVerificationDisabledForTesting; @end diff --git a/Firebase/Auth/Source/Public/FIRAuthTokenResult.h b/Firebase/Auth/Source/Public/FIRAuthTokenResult.h index 9e0028d7b70..515aa60d2cc 100644 --- a/Firebase/Auth/Source/Public/FIRAuthTokenResult.h +++ b/Firebase/Auth/Source/Public/FIRAuthTokenResult.h @@ -28,36 +28,36 @@ NS_SWIFT_NAME(AuthTokenResult) /** @property token @brief Stores the JWT string of the ID token. */ -@property (nonatomic, readonly) NSString *token; +@property(nonatomic, readonly) NSString *token; /** @property expirationDate @brief Stores the ID token's expiration date. */ -@property (nonatomic, readonly) NSDate *expirationDate; +@property(nonatomic, readonly) NSDate *expirationDate; /** @property authDate @brief Stores the ID token's authentication date. @remarks This is the date the user was signed in and NOT the date the token was refreshed. */ -@property (nonatomic, readonly) NSDate *authDate; +@property(nonatomic, readonly) NSDate *authDate; /** @property issuedAtDate @brief Stores the date that the ID token was issued. @remarks This is the date last refreshed and NOT the last authentication date. */ -@property (nonatomic, readonly) NSDate *issuedAtDate; +@property(nonatomic, readonly) NSDate *issuedAtDate; /** @property signInProvider @brief Stores sign-in provider through which the token was obtained. @remarks This does not necessarily map to provider IDs. */ -@property (nonatomic, readonly) NSString *signInProvider; +@property(nonatomic, readonly) NSString *signInProvider; /** @property claims @brief Stores the entire payload of claims found on the ID token. This includes the standard reserved claims as well as custom claims set by the developer via the Admin SDK. */ -@property (nonatomic, readonly) NSDictionary *claims; +@property(nonatomic, readonly) NSDictionary *claims; diff --git a/Firebase/Auth/Source/Public/FIRUserMetadata.h b/Firebase/Auth/Source/Public/FIRUserMetadata.h index 25331718d9e..023c205755b 100644 --- a/Firebase/Auth/Source/Public/FIRUserMetadata.h +++ b/Firebase/Auth/Source/Public/FIRUserMetadata.h @@ -27,12 +27,12 @@ NS_SWIFT_NAME(UserMetadata) /** @property lastSignInDate @brief Stores the last sign in date for the corresponding Firebase user. */ -@property (copy, nonatomic, readonly, nullable) NSDate *lastSignInDate; +@property(copy, nonatomic, readonly, nullable) NSDate *lastSignInDate; /** @property creationDate @brief Stores the creation date for the corresponding Firebase user. */ -@property (copy, nonatomic, readonly, nullable) NSDate *creationDate; +@property(copy, nonatomic, readonly, nullable) NSDate *creationDate; /** @fn init @brief This class should not be initialized manually, an instance of this class can be obtained diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h index 3130c7898fb..2c5faf0bda4 100644 --- a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h +++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h @@ -99,7 +99,7 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) { /** @property dynamicLinkDomain @brief The Firebase Dynamic Link domain used for out of band code flow. */ -@property (copy, nonatomic, nullable) NSString *dynamicLinkDomain; +@property(copy, nonatomic, nullable) NSString *dynamicLinkDomain; /** @fn passwordResetRequestWithEmail:actionCodeSettings:requestConfiguration: From 1a32a1f7d3c84e1fcd620a8e7edea823df3af192 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 1 Apr 2019 19:06:05 -0700 Subject: [PATCH 127/214] Delete references to deprecated Crash and Invites (#2668) --- Carthage.md | 6 - Example/Core/Tests/FIRLoggerTest.m | 2 - Firebase/Core/FIRErrors.m | 1 - Firebase/Core/FIRLogger.m | 2 - Firebase/Core/Private/FIRAppInternal.h | 2 - Firebase/Core/Private/FIRErrors.h | 1 - Firebase/Core/Private/FIRLogger.h | 2 - .../DynamicLinks/FIRDynamicLinkNetworking.h | 2 +- Rome.md | 8 +- SymbolCollisionTest/Podfile | 1 - ZipBuilder/Sources/ZipBuilder/Subspec.swift | 2 - .../Sources/ZipBuilder/ZipBuilder.swift | 21 - ZipBuilder/Template/Crash/batch-upload | 416 ------------------ ZipBuilder/Template/Crash/dump_syms | Bin 211352 -> 0 bytes ZipBuilder/Template/Crash/extract-keys | 12 - ZipBuilder/Template/Crash/upload-sym | 273 ------------ .../Template/Crash/upload-sym-util.bash | 396 ----------------- ZipBuilder/Template/Crash/upload-sym.sh | 18 - ZipBuilder/Template/Firebase.h | 8 - 19 files changed, 3 insertions(+), 1170 deletions(-) delete mode 100755 ZipBuilder/Template/Crash/batch-upload delete mode 100755 ZipBuilder/Template/Crash/dump_syms delete mode 100755 ZipBuilder/Template/Crash/extract-keys delete mode 100755 ZipBuilder/Template/Crash/upload-sym delete mode 100644 ZipBuilder/Template/Crash/upload-sym-util.bash delete mode 100755 ZipBuilder/Template/Crash/upload-sym.sh diff --git a/Carthage.md b/Carthage.md index 4a6c33bda3a..9490de52bde 100644 --- a/Carthage.md +++ b/Carthage.md @@ -33,14 +33,12 @@ binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseABTestingBinary.j binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAdMobBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json" -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseCrashBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDatabaseBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDynamicLinksBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFirestoreBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFunctionsBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseInAppMessagingBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseInAppMessagingDisplayBinary.json" -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseInvitesBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMessagingBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMLModelInterpreterBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMLNLLanguageIDBinary.json" @@ -67,10 +65,6 @@ binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseStorageBinary.jso `Copy Bundle Resources` Build Phase : - For Firestore: - ./Carthage/Build/iOS/FirebaseFirestore.framework/gRPCCertificates.bundle - - For Invites: - - ./Carthage/Build/iOS/FirebaseInvites.framework/GoogleSignIn.bundle - - ./Carthage/Build/iOS/FirebaseInvites.framework/GPPACLPickerResources.bundle - - ./Carthage/Build/iOS/FirebaseInvites.framework/GINInviteResources.bundle - For FirebaseMLVisionFaceModel: - ./Carthage/Build/iOS/FaceDetector.framework/GoogleMVFaceDetectorResources.bundle - For FirebaseMLVisionTextModel: diff --git a/Example/Core/Tests/FIRLoggerTest.m b/Example/Core/Tests/FIRLoggerTest.m index cb6a59a5e99..797a133cdd4 100644 --- a/Example/Core/Tests/FIRLoggerTest.m +++ b/Example/Core/Tests/FIRLoggerTest.m @@ -78,11 +78,9 @@ - (void)testStableVariables { XCTAssertEqualObjects(kFIRLoggerAnalytics, @"[Firebase/Analytics]"); XCTAssertEqualObjects(kFIRLoggerAuth, @"[Firebase/Auth]"); XCTAssertEqualObjects(kFIRLoggerCore, @"[Firebase/Core]"); - XCTAssertEqualObjects(kFIRLoggerCrash, @"[Firebase/Crash]"); XCTAssertEqualObjects(kFIRLoggerDatabase, @"[Firebase/Database]"); XCTAssertEqualObjects(kFIRLoggerDynamicLinks, @"[Firebase/DynamicLinks]"); XCTAssertEqualObjects(kFIRLoggerInstanceID, @"[Firebase/InstanceID]"); - XCTAssertEqualObjects(kFIRLoggerInvites, @"[Firebase/Invites]"); XCTAssertEqualObjects(kFIRLoggerMLKit, @"[Firebase/MLKit]"); XCTAssertEqualObjects(kFIRLoggerMessaging, @"[Firebase/Messaging]"); XCTAssertEqualObjects(kFIRLoggerRemoteConfig, @"[Firebase/RemoteConfig]"); diff --git a/Firebase/Core/FIRErrors.m b/Firebase/Core/FIRErrors.m index 6d6d52d2ce8..914de4bfccb 100644 --- a/Firebase/Core/FIRErrors.m +++ b/Firebase/Core/FIRErrors.m @@ -21,7 +21,6 @@ NSString *const kFirebaseCloudMessagingErrorDomain = @"com.firebase.cloudmessaging"; NSString *const kFirebaseConfigErrorDomain = @"com.firebase.config"; NSString *const kFirebaseCoreErrorDomain = @"com.firebase.core"; -NSString *const kFirebaseCrashReportingErrorDomain = @"com.firebase.crashreporting"; NSString *const kFirebaseDatabaseErrorDomain = @"com.firebase.database"; NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink"; NSString *const kFirebaseInstanceIDErrorDomain = @"com.firebase.instanceid"; diff --git a/Firebase/Core/FIRLogger.m b/Firebase/Core/FIRLogger.m index d1e3b37382b..7fe30196309 100644 --- a/Firebase/Core/FIRLogger.m +++ b/Firebase/Core/FIRLogger.m @@ -25,12 +25,10 @@ FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]"; FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]"; FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]"; -FIRLoggerService kFIRLoggerCrash = @"[Firebase/Crash]"; FIRLoggerService kFIRLoggerDatabase = @"[Firebase/Database]"; FIRLoggerService kFIRLoggerDynamicLinks = @"[Firebase/DynamicLinks]"; FIRLoggerService kFIRLoggerFirestore = @"[Firebase/Firestore]"; FIRLoggerService kFIRLoggerInstanceID = @"[Firebase/InstanceID]"; -FIRLoggerService kFIRLoggerInvites = @"[Firebase/Invites]"; FIRLoggerService kFIRLoggerMLKit = @"[Firebase/MLKit]"; FIRLoggerService kFIRLoggerMessaging = @"[Firebase/Messaging]"; FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]"; diff --git a/Firebase/Core/Private/FIRAppInternal.h b/Firebase/Core/Private/FIRAppInternal.h index d663d3ec8b1..3c346ffc4cb 100644 --- a/Firebase/Core/Private/FIRAppInternal.h +++ b/Firebase/Core/Private/FIRAppInternal.h @@ -40,11 +40,9 @@ typedef NS_ENUM(NSInteger, FIRConfigType) { extern NSString *const kFIRServiceAdMob; extern NSString *const kFIRServiceAuth; extern NSString *const kFIRServiceAuthUI; -extern NSString *const kFIRServiceCrash; extern NSString *const kFIRServiceDatabase; extern NSString *const kFIRServiceDynamicLinks; extern NSString *const kFIRServiceInstanceID; -extern NSString *const kFIRServiceInvites; extern NSString *const kFIRServiceMessaging; extern NSString *const kFIRServiceMeasurement; extern NSString *const kFIRServiceRemoteConfig; diff --git a/Firebase/Core/Private/FIRErrors.h b/Firebase/Core/Private/FIRErrors.h index cf69252aa97..464435a5d34 100644 --- a/Firebase/Core/Private/FIRErrors.h +++ b/Firebase/Core/Private/FIRErrors.h @@ -25,7 +25,6 @@ extern NSString *const kFirebaseAuthErrorDomain; extern NSString *const kFirebaseCloudMessagingErrorDomain; extern NSString *const kFirebaseConfigErrorDomain; extern NSString *const kFirebaseCoreErrorDomain; -extern NSString *const kFirebaseCrashReportingErrorDomain; extern NSString *const kFirebaseDatabaseErrorDomain; extern NSString *const kFirebaseDurableDeepLinkErrorDomain; extern NSString *const kFirebaseInstanceIDErrorDomain; diff --git a/Firebase/Core/Private/FIRLogger.h b/Firebase/Core/Private/FIRLogger.h index a538199be17..f9523ab5e90 100644 --- a/Firebase/Core/Private/FIRLogger.h +++ b/Firebase/Core/Private/FIRLogger.h @@ -30,12 +30,10 @@ extern FIRLoggerService kFIRLoggerAdMob; extern FIRLoggerService kFIRLoggerAnalytics; extern FIRLoggerService kFIRLoggerAuth; extern FIRLoggerService kFIRLoggerCore; -extern FIRLoggerService kFIRLoggerCrash; extern FIRLoggerService kFIRLoggerDatabase; extern FIRLoggerService kFIRLoggerDynamicLinks; extern FIRLoggerService kFIRLoggerFirestore; extern FIRLoggerService kFIRLoggerInstanceID; -extern FIRLoggerService kFIRLoggerInvites; extern FIRLoggerService kFIRLoggerMLKit; extern FIRLoggerService kFIRLoggerMessaging; extern FIRLoggerService kFIRLoggerPerf; diff --git a/Firebase/DynamicLinks/FIRDynamicLinkNetworking.h b/Firebase/DynamicLinks/FIRDynamicLinkNetworking.h index d92d2bca5f8..e6977b9a6aa 100644 --- a/Firebase/DynamicLinks/FIRDynamicLinkNetworking.h +++ b/Firebase/DynamicLinks/FIRDynamicLinkNetworking.h @@ -64,7 +64,7 @@ typedef NS_ENUM(NSInteger, FIRDynamicLinkNetworkingRetrievalProcessType) { */ void FIRMakeHTTPRequest(NSURLRequest *request, FIRNetworkRequestCompletionHandler completion); -/** The base of the FDL API URL, Used in AppInvites to switch prod/staging backend */ +/** The base of the FDL API URL */ FOUNDATION_EXPORT NSString *const kApiaryRestBaseUrl; /** diff --git a/Rome.md b/Rome.md index 47bbba9f536..0b08526b046 100644 --- a/Rome.md +++ b/Rome.md @@ -52,8 +52,8 @@ Then do the following steps: `file Rome/*/* | grep universal | grep dynamic` 1. Drag each dynamic framework to the "Embed Frameworks" section on the Xcode Build Target's "General" page. -1. If you're using FirebaseML, FirebaseInAppMessaging, FirebaseFirestore, or - FirebaseInvites, find +1. If you're using FirebaseML, FirebaseInAppMessaging, or FirebaseFirestore, + find the resources to the project: `ls -ld Pods/*/Resources/*`. More details on this below. 1. Drag all of those resources into the Project Navigator, just @@ -85,10 +85,6 @@ Then do the following steps: - ./Rome/GRPCClient.framework/gRPCCertificates.bundle - For InAppMessagingDisplay: - ./Rome/FirebaseInAppMessagingDisplay.framework/InAppMessagingDisplayResources.bundle - - For Invites: - - ./Pods/FirebaseInvites/Resources/GINInviteResources.bundle - - ./Pods/FirebaseInvites/Resources/GPPACLPickerResources.bundle - - ./Pods/GoogleSignIn/Resources/GoogleSignIn.bundle - For FirebaseMLVisionFaceModel: - ./Pods/GoogleMobileVision/FaceDetector/Resources/GoogleMVFaceDetectorResources - For FirebaseMLVisionTextModel: diff --git a/SymbolCollisionTest/Podfile b/SymbolCollisionTest/Podfile index 88cf453a369..af21941f159 100644 --- a/SymbolCollisionTest/Podfile +++ b/SymbolCollisionTest/Podfile @@ -10,7 +10,6 @@ target 'SymbolCollisionTest' do pod 'FirebaseAnalytics' pod 'FirebaseAuth' pod 'FirebaseCore' - pod 'FirebaseCrash' pod 'FirebaseDatabase' pod 'FirebaseDynamicLinks' pod 'FirebaseFirestore' diff --git a/ZipBuilder/Sources/ZipBuilder/Subspec.swift b/ZipBuilder/Sources/ZipBuilder/Subspec.swift index 8f63c0177fb..d07a9007bc4 100644 --- a/ZipBuilder/Sources/ZipBuilder/Subspec.swift +++ b/ZipBuilder/Sources/ZipBuilder/Subspec.swift @@ -24,14 +24,12 @@ public enum Subspec: String, CaseIterable { case analytics = "Analytics" case auth = "Auth" case core = "Core" - case crash = "Crash" case database = "Database" case dynamicLinks = "DynamicLinks" case firestore = "Firestore" case functions = "Functions" case inAppMessaging = "InAppMessaging" case inAppMessagingDisplay = "InAppMessagingDisplay" - case invites = "Invites" case messaging = "Messaging" case mlModelInterpreter = "MLModelInterpreter" case mlNaturalLanguage = "MLNaturalLanguage" diff --git a/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift b/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift index 091dc64a5e1..82667f03d89 100644 --- a/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift +++ b/ZipBuilder/Sources/ZipBuilder/ZipBuilder.swift @@ -33,9 +33,6 @@ private struct Constants { public static let modulemap = "module.modulemap" public static let notices = "NOTICES" - // Directory containing extra FirebaseCrash scripts. - public static let crashDir = "Crash" - /// All required files for distribution. Note: the readmeTemplate is also needed for /// distribution but is copied separately since it's modified. public static let requiredFilesForDistribution: [String] = [firebaseHeader, modulemap, notices] @@ -360,24 +357,6 @@ struct ZipBuilder { fatalError("Could not write README to Zip directory: \(error)") } - // TODO: Remove this manual copy once FirebaseCrash is removed from the Zip file. - // Copy over the Crash scripts, if Crash should be installed - if subspecsToInstall.contains(.crash) { - do { - let crashDir = paths.templateDir.appendingPathComponent(Constants.ProjectPath.crashDir) - let crashFiles = try FileManager.default.contentsOfDirectory(at: crashDir, - includingPropertiesForKeys: nil, - options: []) - let crashZipDir = zipDir.appendingPathComponent("Crash") - for file in crashFiles { - let destination = crashZipDir.appendingPathComponent(file.lastPathComponent) - try FileManager.default.copyItem(at: file, to: destination) - } - } catch { - fatalError("Could not copy extra Crash tools: \(error)") - } - } - print("Contents of the Zip file were assembled at: \(zipDir)") return zipDir } diff --git a/ZipBuilder/Template/Crash/batch-upload b/ZipBuilder/Template/Crash/batch-upload deleted file mode 100755 index 053a3ee7fe3..00000000000 --- a/ZipBuilder/Template/Crash/batch-upload +++ /dev/null @@ -1,416 +0,0 @@ -#!/bin/bash - -usage () { - echo >&2 "usage: ${0##*/} [-hv] [-p google-service] [-i info] service-account-file {mach-o file|uuid} ..." -} - -help () { - usage - cat >&2 </dev/null)"}" -fi - -var_check FCR_PROD_VERS FCR_BUNDLE_ID - -ERROR=$'environment variable empty or unset\n\nExplicitly add to environment or set GoogleService-Info.plist (-p)\nand Info.plist (-i) flags to extract values from the files.\n\nTry "'"$0"' -h" for details.' - -: "${FIREBASE_API_KEY:?"${ERROR}"}" "${FIREBASE_APP_ID:?"${ERROR}"}" -: "${FCR_PROD_VERS:?"${ERROR}"}" "${FCR_BUNDLE_ID:?"${ERROR}"}" - -# Extract key from legacy cache. - -if [[ ! "${SERVICE_ACCOUNT_FILE}" ]]; then - xcwarning "Running extract-keys on desktop." - EXTRACT_KEYS="$(script_dir)/extract-keys" - (cd "${HOME}/Desktop"; "${EXTRACT_KEYS}") || exit $? - SERVICE_ACCOUNT_FILE="${HOME}/Desktop/${FIREBASE_APP_ID}.json" - xcdebug "Using ${SERVICE_ACCOUNT_FILE} as account file. Please move this and all other extracted keys to a safe place." -fi - -if [[ ! -f "${SERVICE_ACCOUNT_FILE}" ]]; then - echo >&2 "Unable to find service account file." - echo >&2 - usage - exit 2 -fi - -# usage: extract_symbols_and_upload *dwarf-file* *arch* *exe-file* -# -# Do NOT use the dSYM bundle path. While it may work on occasion, it -# is not guaranteed to do so; the full path to the DWARF companion -# file will always work. (Discovered by Kerem Erkan.) -# -# If the executable is empty, use the DWARF companion file as a proxy -# for the executable. -extract_symbols_and_upload () { - local DWARF_COMPANION="$1" ARCH="$2" EXECUTABLE="$3" - - if [[ ! "${EXECUTABLE}" ]]; then - xcdebug "No executable; using ${DWARF_COMPANION} as symbol source." - - EXECUTABLE="${DWARF_COMPANION}" - unset DWARF_COMPANION - fi - - [[ "${EXECUTABLE}" ]] || return 1 - - if [[ -x "${SWIFT_DEMANGLE:=$(xcrun --find swift-demangle 2>/dev/null)}" ]]; - then - SWIFT_DEMANGLE_COMMAND="${SWIFT_DEMANGLE} -simplified" - else - SWIFT_DEMANGLE_COMMAND=/bin/cat - fi - fcr_mktemp SYMBOL_FILE - - "${DUMP_SYMS:="$(script_dir)/dump_syms"}" -a "${ARCH}" ${DWARF_COMPANION:+-g "${DWARF_COMPANION}"} "${EXECUTABLE}" | ${SWIFT_DEMANGLE_COMMAND} >|"${SYMBOL_FILE}" || return $? - - fcr_upload_files "${SYMBOL_FILE}" || return $? -} - -# usage: is_executable *path* -# -# Check to see if the file is an executable or a dSYM bundle -is_executable () { - [[ -f "$1" || ( -d "$1" && "${1%/}" == *.dSYM ) ]] -} - -# usage: is_uuid *string* -# -# Verify that the argument is a UUID. -is_uuid () { - [[ "$1" =~ ^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$ ]] -} - -# usage: set_uuids_archs *mach-o-file* -# -# side effect: appends to UUIDS, ARCHS -# -# Extract the uuid and architecture information from the given Mach-O -# file and append the information to the UUIDS and ARCHS arrays. -set_uuids_archs () { - eval "$(dwarfdump --uuid "$1" | awk '/^UUID:/ { print "UUIDS+=(" $2 "); ARCHS+=" $3 }')" -} - -# usage: mdls_to_bash -# -# Convert the output of mdls to a string consumable by bash. mdls -# outputs string arrays as quoted strings separated by commas, and -# Unicode characters as '\Uxxxx'. -# -# Note: this is sensitive to the current locale. If the locale is not -# UTF-8, then wide-character warnings will result if the strings -# contain non-ASCII characters. This is actually a desired behavior, -# because bash has issues with non-Unicode encodings for file names. -# (The macOS default is to have UTF-8 enabled, so this should not be a -# problem for the majority of use cases.) -mdls_to_bash () { - perl -C -ple 's/,$//; s/\\U(....)/chr hex $1/ge' -} - -for EXE; do - if is_executable "${EXE}"; then - xcdebug "Assuming ${EXE} is an executable or dSYM bundle." - - # Import architecture UUID information - UUIDS=() ARCHS=() - set_uuids_archs "${EXE}" - - for I in "${!UUIDS[@]}"; do - xcdebug "Found ${UUIDS[$I]} for ${ARCHS[$I]} in ${EXE}" - done - - if ((${#UUIDS[*]} == 0)); then - xcwarning "${EXE} exists, but has no architecture information." - continue - fi - - if [[ "${EXE}" = *.dSYM ]]; then - xcdebug "Removing dSYM bundle as executable target." - unset EXE - fi - - elif is_uuid "${EXE}"; then - xcdebug "${EXE} looks like a UUID to me." - UUIDS=("${EXE}"); unset EXE - - else - xcwarning "${EXE}: not an executable, bundle, or UUID." - continue - fi - - BUNDLES=() - - for UUID in "${UUIDS[@]}"; do - xcdebug "Searching for ${UUID} ..." - - QUERY_UUID="com_apple_xcode_dsym_uuids == '${UUID}'" - QUERY_TYPE="kMDItemContentType == 'com.apple.xcode.dsym' || kMDItemContentType == 'com.apple.xcode.archive'" - QUERY="(${QUERY_UUID}) && (${QUERY_TYPE})" - - if ((VERBOSE > 1)); then - xcnote "Passing query \"${QUERY}\" to mdfind." - fi - - MD_FIND_RESULT=() - - eval "$(mdfind "${QUERY}" -0 | xargs -0 perl -le 'print "MD_FIND_RESULT+=(\Q$_\E)" for @ARGV')" - - xcdebug "mdfind returned (${MD_FIND_RESULT[*]})" - - # BUNDLES should contain no duplicates. - for I in "${!MD_FIND_RESULT[@]}"; do - for BUNDLE in "${BUNDLES[@]}"; do - if [[ "${MD_FIND_RESULT[$I]}" == "$BUNDLE" ]]; then - unset "MD_FIND_RESULT[$I]" - fi - done - done - - BUNDLES+=("${MD_FIND_RESULT[@]}") - done - - if [[ ${#BUNDLES[@]} == 0 && ${#ARCHS[@]} == 0 ]]; then - xcwarning "No executable or bundle found for ${UUIDS[*]}." - xcnote "Try passing in the executable itself instead of a UUID." - continue - fi - - xcdebug "BUNDLES = (${BUNDLES[*]})" - - if [[ ${#BUNDLES[@]} == 0 ]]; then - xcdebug "No dSYM bundle found." - - # The dSYM has to be on a normal volume (not temporary). It - # can, however, be shared among multiple executables. - if [[ ! "${SCRATCH_BUNDLE}" ]]; then - SCRATCH_BUNDLE="${HOME}/com.google.BatchUploadScratchFile.dSYM" - FCR_TEMPORARY_FILES+=("${SCRATCH_BUNDLE}") - fi - - xcdebug "Creating one in ${SCRATCH_BUNDLE}" - - BUNDLES=("${SCRATCH_BUNDLE}") - - # Create the dSYM bundle. This may produce an empty dSYM - # bundle if the executable has no debugging information. - xcrun dsymutil -o "${BUNDLES[0]}" "${EXE}"; STATUS=$? - - if ((STATUS)); then - xcwarning "Command dsymutil failed with exit code ${STATUS}." - continue - fi - - # Import the dSYM bundle. There is a momentary delay between - # creating the bundle and having it indexed; explicitly - # importing guarantees the mds database is up-to-date when we - # ask it for information about UUIDs and paths. - mdimport "${SCRATCH_BUNDLE}"; STATUS=$? - - if ((STATUS)); then - xcwarning "Command mdimport failed with exit code ${STATUS}." - continue - fi - fi - - SEEN_ARCH=() SEEN_PATH=() - - for BUNDLE in "${BUNDLES[@]}"; do - typeset -a BNDL_UUIDS BNDL_PATHS # keeps ShellLint happy - - eval "BNDL_UUIDS=$(mdls -raw -name com_apple_xcode_dsym_uuids "${BUNDLE}" | mdls_to_bash)" - eval "BNDL_PATHS=$(mdls -raw -name com_apple_xcode_dsym_paths "${BUNDLE}" | mdls_to_bash)" - - # Neither of these SHOULD occur, but curious things happen out - # in the field. - if ((${#BNDL_UUIDS[@]} != ${#BNDL_PATHS[@]})); then - xcwarning "${BUNDLE}: Malformed dSYM bundle." - continue - elif ((${#BNDL_UUIDS[@]} == 0)); then - xcwarning "${BUNDLE}: No DWARF information." - continue - fi - - # If no executable was specified, then the UUIDS and ARCHS - # arrays are empty. Populate them with information from the - # bundle. - if [[ ! "${EXE}" ]]; then - # The final UUIDS setting will be the intersection of the - # discovered set and the originally specified UUIDS. This - # is to prevent uploading potentially private information. - SOUGHT_UUIDS=("${UUIDS[@]}") - - UUIDS=() ARCHS=() - for BNDL_PATH in "${BNDL_PATHS[@]}"; do - set_uuids_archs "${BUNDLE}/${BNDL_PATH}" - done - - if ((${#SOUGHT_UUIDS[@]})); then - for I in "${!UUIDS[@]}"; do - for UUID in "${SOUGHT_UUIDS[@]}"; do - if [[ "${UUIDS[$I]}" == "${UUID}" ]]; then - continue 2 - fi - done - - # This is not the DWARF you are looking for... - xcdebug "Rejecting ${UUIDS[$I]} (${ARCHS[$I]}) as candidate DWARF file." - unset "UUIDS[$I]" "ARCHS[$I]" - done - fi - - unset SOUGHT_UUIDS - fi - - for I in "${!BNDL_UUIDS[@]}"; do - # See comment on extract_symbols_and_upload for why the - # full path to the companion file is required. - - BNDL_UUID="${BNDL_UUIDS[$I]}" DWARF_COMPANION="${BUNDLE}/${BNDL_PATHS[$I]}" - - for J in "${!ARCHS[@]}"; do - # A dSYM bundle can contain multiple architectures for - # multiple applications. Make sure we get the right - # one. - if [[ "${BNDL_UUID}" == "${UUIDS[$J]}" ]]; then - ARCH="${ARCHS[$J]}" - break - fi - done - - if [[ ! "${ARCH}" ]]; then - # This is not an error: it is legal for a dSYM bundle - # to contain debugging information for multiple - # executables (such as a framework with multiple - # subframeworks). Just ignore it. - xcdebug "No matching information found in ${DWARF_COMPANION} with UUID ${BNDL_UUID}." - continue - fi - - xcdebug "Found ${UUID} for ${ARCH} in ${DWARF_COMPANION}" - - # Have we already uploaded this file? - for J in "${!SEEN_ARCH[@]}"; do - if [[ "${ARCH}" == "${SEEN_ARCH[$J]}" ]] && cmp -s "${DWARF_COMPANION}" "${SEEN_PATH[$J]}"; then - xcdebug "${DWARF_COMPANION}: copy of ${SEEN_PATH[$J]}; no need to upload." - continue 2 - fi - done - - if [[ -f "${DWARF_COMPANION}" ]]; then - extract_symbols_and_upload "${DWARF_COMPANION}" "${ARCH}" "${EXE}" || exit $? - SEEN_ARCH+=("${ARCH}") SEEN_PATH+=("${DWARF_COMPANION}") - fi - done - done -done - -# For debugging odd cases. -if "${KEEP_TEMPORARIES}"; then - FCR_TEMPORARY_FILES=() -fi - -echo "Done." diff --git a/ZipBuilder/Template/Crash/dump_syms b/ZipBuilder/Template/Crash/dump_syms deleted file mode 100755 index 8d0ef781cc8202a868f7120670a3ee5a2dafdbcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211352 zcmeFa3w%`7wLdJU&fpcI+(h7=|NEAVAeIS{UScn8C zksOYvVy{$gD{bnnEp2VhZ8ba;odF~P6$2`wR0yDO#z6#85%_q+F*M*>*y{r%tn ze?Fhd*=Il2UVH7e*Is+=wfA}J#HXjaI~>kThodIL;c%RXUk4)UY6c95nKRR)50R3@Xk!h5nTL%_SuMlbQ6DB!Z;b+aQR zylJy$+%tQ|9K>~n_r;GSyjBpLe|+!M^3(9UYx?wQGpeS~m>rlFxc!c<@HX5e;XUb* z@c7;do{UcZpEhlJWnj+T_uXa3bcHu%nS`efks0`&{@3}9?VEo8eU+&W=?br5gM>F5 z{lGuIcj^xro%}y-TJXNPci(sCw7c({c|T&i!pqqr;aNRp5x#fDDG5xJpD}CN%sIE; zJ442Hh1aoD!b>2vh zb{ZekrrmS0!lf5%UzPrLix z+wYn|R_WRw1?>`ELT>Q=oB8C<+XJ`Dy7|8=yh0n^3cCaO{>}K^QQ5iuo!h(Pl*BJD zRb?mo30_J|OUYrMcEbd3sg{(XnS$?@6(=&wUP!zX-E>%&Ivh2HB5S52jAt0N&Vqm~ z=$7edMaGWqnGTnNgbOnrWq2pea`7t}km(5F+4{XqM@w&q<6MNx@Z%rh^G7*NzO2UY zY=5)!GaWn5kk|`xdHD6f@2X(soU87+`;M#beCVD#@y@@C@FO0-9`p6@=I{NtHx}jo zqjbezUj21G;sj5hdk&B~$MOBdJD&V2KzmuPU=%S-{jr`3{Fo;B;uCb|5a(4lK2#Z) zaqm^WyYHBD`<#ca8cWeJ_x?FQt-NaN{lWXdBzNC`-<3k3F!rjOX3VKXOyyN$($WEw z@P~JT{}JAtskWhg%5pOuhru#~?RWO`|Nr;@9tuoW^^b2XQ}vUoIcnC=GaRbE%lfC9 z;iwA-1|jNJJ8F}Rx=_`3tDz09Tg#(wj>~Yo3hr_i9gCgAfa4X8c{`N^lAssHnMtYp zC1UE1DRn=`b8&Xv7a5M)aIgwh{0u?sc0fawvtG?ZvNafQs=i)ArD9;+!Wcu%QS}Kq zs^LczTFLiJOP>=Q>b z9MLC{GZgQk)ct~iQ|BsmIs%KczeFPNdjMrVLC}hz4Dh>M;-?crC;V0mm}kMSZvwv_ zf>SjM@DEw$QjxUV8jmLluIk6p zwx7SlCJsO5aXEu-H575F@klI7HMCsS9Jc(i3`bE|)i;+%1D#4u`KD5TOqKdUr&5=G z%Tg}8l;>v8O`ge~NjIvYgTpobYt8h!G(G52ou6y^Wc08XxCL8gHhWv0PR%R_%GKdl zAhaAs+f@C1YvhFx*;TzAO6^|}XJn3g6HAl@myLR5&8f{w?Fz(=I`!#^ErE0ScuT2U zgqV%qR*)_G>g@BJp^l8oxtdv>_X-8R<%5vZ41Zo)CDsRh?YK}RD_L=~wI0#3VjqfX zhBr@bE)n_z0Jt;*xEF6#7X28B>fp_)UY3LOTr#7l$r=5QC{vAY=^ii3iJs3~XqamH zU$*P8?nqV;Lm}d*>eUUU#*CMJ`hM#f%)0)%j{r-l-GTI?o!+9jD7pr)62Mki`Is8g zqV;A}@toBw5lbR6s?j=nft@iC^@7#bHENahCZb9W?+PRMoZ)XYye}Blt5johgKC^t zs%Q1r1O2m9-Pa#Ov9v!rZ+`#Wn(D2Nz!1#{+BDfdcf30IbC6`twQ6ze+#)UBtcKR- z`HK%IkBp4Rb58BWQ%r7-j;1X8hpT>5Ty5|0V!fb5Xc%XW)F~L*xS@QvblJK(uD` z0-^6g)GOD3w1UBI_1sn-os2}$I!~f9P%O{J_KcO?D&Uq!-HDQt$_L+)Ej+!7W>qy`bGQAGNMh`hns=&bD~ zFz}h9owXT0#XFkF{80e&QFIn3;7N%)z{cl{68B9}Oi0`e6eAyrjzT*=+Hy8Jpt>xB zT#C82Bti8w*;(M#n?3p#PTiq-E{9S#40@_+a#F0MFje&%y9m>+^5%2s=~AHm4HCo? zIu#FG?pb&WD!OyQ_f}DeBCdO29RBwRc<}!`rEVQ^uG#{L8!I%tFUJazA4>>l;9>+g z`v&?T03#7*{HK`pEoRLou(w8dUk(nF(z>q{(Zhs@4(82l9+T^E#)gby?!e$tgdaSI zu}(*$GnS_sBW$tR3ou1)&?V*)InY=2-d3MEeLzx%3x4(Kd#qfn7JcSV^E$~xpB}a1 zKv6X(Ty^A;ZL5a_&k_*6Wu>8D=e^2ifKcw0S-$6_+~BkXTP+^TwSc%wJsOpwwnZ=`z3 z*=h>rI&x-Uaw1VAtgx<6Bn(guw9oLjs>LnJqCQX$)WH#B@(Q(h^Mlt@q8Z(xBo=y% z2Zz4|o^crd=Y0C!cx!YH)-PvBFWMxNZ$z?BU)zDUNg}qE8r9EvLh*j!>`**Isht3D z2!X--q2qKjq1=@}zf}(zIg@cmkxZ1_cgTeW5=1 z%puEd?M6}n394yUp|tfV(#7m7;`}vMuv3INoSBaIfRx~@E(;bi?qkGtB3-?eg(Y+{ ziWp`eGFl67_>3X!Q^x|Klz=m<5b2?HZfU*ekon<}o%m ziE;}&KxulopCO<~>>mX7hkb_VWn~ejZ7|P9V<{_F^`q+Gx9420nj_azbyJn+-%^X; zpWEM9%lg#8r{*Zea#o@iA5|V5o8Gi3qG@Q?XDn!}4p((&O z)>k!Ul&!U@%_vVqA%pZf!L)`Ka-&XV>(bH3sC|*?@m8fNY!0YHqfzl^*4M)`9Cm|3 zC$oe7eMWVwzxd$-Wzipz1EE6Yk>v;#?X26W)HzA3_y&(LcDRrkTJZ+uk$;|y$D;>` zhI5^&FU?`qPAJbyb493G)z>;iXg5MGhSCws17f0xxs2lM>L*XV=X(8&zQ0(IdA3eiqddp(4o6+%w2x?5X;YOSZQ!*cHcXQzBf z*4W3f9kvdNhG+loL+16iVYOtb`sBkKy&VuN!8XfxA|7At?ZCW{XX~P=HP^y}D@bg8 zDf^SmuCHZxD@|jjES`v!ayjOr^8u};-rtJp#~GSehJg>PAgA){D5o$-=Bc_E^H_lR z%ood0K9;$7%oHIDy-SqA-WmwQ*#s*^W4*UAA@;)PF{W4-LD0%bE=Pj();Wl<#n^sG zrv^*!6k(EwtsvqO5`*Q;NsJu~8$o)ke}&bzQPAQ4{6t}_}GYsN=l3KVF@YljTtUGg4WWnkc}*g7{*Ff>!w= zyE@)lsx(=i(3j5WA`;oKN8xZOKuSDoIp`o&De~j3u^FlX^{#GPusgKBTt}e65TrJe+AyU zFg|Pfuc4`0o<%rTh;a~8*DKVVh0P&H z)!)|iW10@$oeoP9lw|<@Ik*F=7AI$jw#yJiXClhX{>29bNx@jY15|1Cp`d=udX_^T zt35VFtWuYVKQTTHMK#6&EN4VDav~e_u5?4OD~}HB?r=OfNR8~zR-H$nx&$sz&9XLT zYu~1Z_O=Nrc(bc~J>iH^JLyJE->&KH)bou@pFxyq{EGMn@9`BMsq9NT*v2uv9iC&| zoxy^co@P`L8&h=5ryua?pIa}IGB8~Y^bKwksP6|}vHqGdE@va;aq~FQ&e(Y}jU_U# zfmC;rEIU$-BR-lAjjOxQn3zIglL{2jN#k4_$Mtrs`s|gz?uyN+S&-uhTnfds?pR=? zrho3&Usoe-E{uwERd-WwTcN`J96)mLGEm z)NTu40_yQ4`y;+d8av+Js}{AYgIhJ{5uZL0_9Jvr2ZY^{GOOcin8n`0Khf`!lJY+~ z^TQ47OpKXkS9$aY$Ry}|(~ZIan*FLs%~Z{KE^81H!5CuIYa*=a8_6e+An4QEtq<(7 zfVnjt<|jp6VE!Ww=9@@Nz${L|ywZmGs136`IshTbPfJqVOPg7lrhf^YTQhEOd6kv^ zJXjw}^>qvPo+7Vq{bXjT-ZC@P=F}ALZM`nsHkW%x0Kn#^mCOfjEZm zP<{Mhwh8!=^m#OP{izT}nZ@2I^<$Bp7F~dC@$L2aQ6P4C; z#&e~K7WQqU)8>F^3qU4D73tWn8Vg*0b8Zet^F-25hoz~zM)po3BN7LoBoHgm%((@Y zA}||QR!@w^l^{E?nn27-8l4gWDGm~KNvV^E3N`&J%yc_^`U%TVV(J?;;}W%QXYdNh z*)W>Tl!=sF2BzU=Zy1>nW(f{u8b3c{YH0m%j7?7OoyTWtmJc0-*q`PCY^C-s0)atQ zY0A*_*R-vl{+Bba#iw|G;!@4=&e|}#emuIu-x|vV1t1giFcZzo^MlK_9%^)Y@|uyk zvcO+_$ftjzDWkWuQ!mlXK)%yk8%OC%tV^p~tsj9fkTY2i^^Rrxi@&TqM>R4bc!Gb9 z&A==0N7;8-b|92Mlmyi2g=iTX*lJy17q03ayAm~34pxmZKtHUAc|@%B^jtI?U9%%L zki%Eie}Ew$JKs)&QJf1xWD=CgO=E-92%;=~N6P+Jmw+r*_(~2)7}F)!4Z^Dy+UQc7 zL-nLR{t&DY0S3oZ$*3{2?dL4}Sz$k?*v~TisoKvH`#GFXO&{pj=ei(zbEp#g^p0dV z+Ct`H;6Wi1#8Y4|cW9bDLRPEID8)W~0Np0292)Kjz#@RCOw~ZlJWywgbvu?7YJ5zN zU`onH`A5F|mXiFZLXy+O07(v8Jtes<$dhvXv)DkHo-5^eLX0zmkm83R#WUyjgA~VH z*GY=My|<_}DZ)Jo5uO}3_8vh&Lgd}%@*6WTATeF;P$M1LzGAC#qGohc2d`HnC$oLd z7)FR1iH-;;B;KLRX~*owGMafqjy8A$R`_Bzegz5(|1u#B=Z@AQUu9{<|MV%N_r9@gN_-3#*`a8zHEaAnw!}j6Q1~+Swcs9mwi-3fY zRTPHdKG1{A9yyS0jUPg;^EPUD(VSSeSFh3{A0Y?~XS8UMkFzJ31FjMi@*%5QWQII2 zgN5|6a1^d?%}g35R=0gk&S9yJdkZ|1sT8r=@z2@XYm1f|0w1>2(!RA)wqU4 zXam8ixiS0>!alPy4}-YE`sw%4T63Oi-GKm8f6NGb(^BYZ(J*7gL?Jg9u#5<;S2?#| ziImv6R2!SxY1%^1v|Ig>k-tks#;so=GQp?Z$I2Db{gl-<7}HksSgu{;TJzWf!86Tc z^Z1mjqgL=m8((2<`|g88wz_oVNbW80v#oMkl zO>R~7_tdSQ<+Z|MxB~`%pHkYacuy3cs?I9f85>kOI8qx{rytR#Lv{CWg>d$_fsd?z z7Em@TPeRf`QjbMVwL60)K;dsek|}OIFERu$5e<9Bj(S=~_1DY=PU@CW4Ho1S9V-f3 zkD~P+9k#sC2FO`KMa~=`)eq#p9$}c{Gd>n4}-(;<(3H zkQ<7VUss~2W=zXtcSFAIw!)WE*6p@jGNB-u@I<~$_zV@G4XHkj&Oi)AX{=|A4;Fr1 z=CLM-XS;QgK(N_*^UHW#>WR^N&}YD+E?(7>s1+Tvwp`+Hl&>!euV2zR{y{jKoGJb{ z$@~PchD@-U|Gc!}!*D5Oxp<=lPt8k0v-9e&!JO~W-_!IjRQ;qe52z^hA0s1B$wZa4 z;lQ12%N|6LoXfdn!TVOcdzGe7U*S3n2@rFo`f;y**b75<1&mnC^c>RU27TuYl4^;q zwrCNui<+zMN3CyOoT_!TUF%Ai5ee{5-&E@(*jZqv+HY6ujm&*AE4N|0PF~co|6nn;+w!2x(G8+O<X_K zseE^J$`?XDOr&z(U*L-s>y2d|s*R_-WCb0>EY(kPoIS?QC|+OrkjHqK9Qy!3w_)M} z3I$B(45v+hOEt=>Fz4#VP*~I?LQ)1^-5)D*S@c>I{aA9>$igqG%g+wBk znSCIUp#f3-sB zquAxCtXGR_qaXz`e}|dhO=WIEM-}bl!ueeIoj|_#QjIB)#$Yjfy`R8?YYBUSF8i4% z+$d21PwuvgBvhY%ib{NP=80xw4@#D}93^g-c!(1Ivy|YjRVTuKBMxuDod7Kp5dTVR z1xJ1t91bM8lQ?`Rxmc9gbe0kq!5`p^I3#>JRLC#5b#Qs=!15Gs49-qVRImQlOi$xjWo#?JS@;=6+2tD=|LO!$4g?{tLC$!>|N-bSAeCDw8@8@7Wr8Hdk>7-9!8Zq`-0tM1aF&VoH zuaU&@Nb$PBe&|CAmz(EUt3V>5{nT^OtfnmuW%{y2I%#YbxJaVAkz2 zOJS%Z)1y4OF_tM^V8l!&G=!pKdJj5s~Sp| zxAs`zGCSJ360NF-`Y1A4LntVA2EHGC9B}MNp?+9TBcC9d%$SLc6yiUTmCc9nKxMN~ zqyRF`uB;Y%GAqloqb(=sBPwGi>ra%7JA>bg71`;srW$0DHHlmy9&FT{ffMh@|rbb4+dxqznBq|mr?bF)8@rOFQbBAW6fX^Z9fixQbX0N z0F@STxd+h(Jh45^-ANnvQ~`TA6QpBRcZ5{wy zQvmx&fJ|89CjJ0eF9AA)OcN;oL;&0XfXrw;j|@Kju+QD5)Q$tT{<>qTDYn+J&yz=G zNr8pOaH`nZM1F3mH|>xZRN$RE?s(Wudin_f!|o|!gf!)LWjBdI4dT!w3RVm1E{tNp zfEleTkilQ~sZz_8JN8E!5J@9c?JB;T1C~?vVvnl*#Z^e5HO+k01x!)(6hz3XmoiA; z)CrLx4;lFSE4-qtA&^(wk5ado?l%?@fZup3hmXPQw8#k<`TZ-j(1t9{d^oFU6UcyZ zg0{CC*G3MxFj-U%(2S?x!woC_X3h9*2a!{re_wUJt?2^}L;Rs|4`CxKj?Cd+BN;D^ z;{{KHO@43@1YD8i@HQZAUBQSv84+g007lq*FoHtoB7&W{Q#_k3(@IFeq-WB?gY`hD zR5!U7RI1li;h~%J*?;;{?h5IL^}0L7E9O#@TRULdFf3O3I;$OOm*Ia$g+UQJliaH+ zKCYPEz{c2HhE*fjqUvJ95h52xpFGvf`jBMznL|r_=8PJTadWBiJg73xRcZ`ziO&2l zQY!O4ms0yjgp}vs#s*brEfI25U9ec~5sqeIavLE~RO&*=6Ix589aZNmO=^$O9%Y2^ z#!%|+LR!-Mn7iiw#?V?e1%AWlAPwdxW2tD6@rd9H;}`5!PLb$VdWVMgv9m%WcVI;B zK(Na=Czt*Nq=hmHmcd4i^&{+dfjk)H*c82B%g?d5p)Eywpsg-ok|Fk4Rbv&|Z1m!+ z>tL-v7I6l%)veK3_ISg09n7ekf|vN5?U?JAS!HJ|1RoCejB2=jh9_r*GuVB+8Mw|# z0DqXRJg>lg@)*IcBDDRse9jn;skqaZt+R|edVz{a3q--WIK(Smst;>@>q`q zYfmRC;L~f^T*&H=KRy$mf@D%2S0Ds={1YjU`9^u1D`IOWolzcNMj!5UdA#C6MkM9& zQOLanHt#Zt^4Oh{$EN`NZ1VVx#|WykJk|vGbb0&)YDkmEvytI!^0-z4Yd0GN=+JnHkmB#(OlgRb)UL5V>Pu(IXxD-f`0@_0Eih#^c9N4h+Y z@ZB8nVGf>M$K9v5E|o$LI7pT^`>9c}xJ1$DEWq%VQVhu`693s|MEl z+ttXSJPPGJ%~)AL*v>5w${dbTC?Cbx&x2rYm4X>ovu3y-MdqwLyI_9yf-ZvjNk-(Q z3FZ(Z(gpJzM3CV}lcr$!FZQM^Yzh5i1*iqNT!tYFxhzDnNvjv+G9@kLGTkX?a+K0r zP0Hndx#@CQMJ~!^Xq4!dU>GvF1W`Wh!1(mK;R41P#IuwF@E=o+o2EcYCna=*w9cFb z3B3}bgoGw)LPBRHBs61`x_?KWkPtjlLT3pH&E_a|b4hk_PJ|Y6d!O3d2uZynw3dy7 zr0$74LQ->rQjJFh#~8n0ho?(wu?musdM5}chn5iT&E6VBfQde{)`IMkcPXr&J)c9W zdI^@<5Y~fq2%}iM_;n^}G}hldjg^5J4V(n9T+c7q&?rR#r|NQWOpycLt`-3@z|Ep!t<5 zE61sxg&kD7uxyHW@*-;UMgV~)LD42}?WUPteO;i3aq~ShJut+=PbSNwtlXwf-^K-E z@%HKqRO2c3Vrb+!*aFw`g0CB;=U8&D81)s(`lh44sfqfg{LA_j<0)F^&QfJxR7q0U zRSt(PgF1J}I@9-Vhr|DB1-5KgVQmLf3wDYy(S*H4pT;sh@z=cir)WxPJ}hbBKsFpf zV%Z+Umw%^L{H7B68{m6tUd}HDgE{>Ap}_gr)DI#dU&$da;}{5*9jCAi_SC~oZ@2Xs zqOq^5)ZUD40MJs{W);9K+C)W(*kZF*#l|!nhh0i~v7l)Nex1*I$Z0cd{f6}b@O`}{ zKjiJ|D${U+=v&bWhD4iI@Lhc@zXr_`Tk`%)<2>%pF_{Z`a zPz1Y={f(hc5{?IeY{ET$2Er{DsPDE3SEr;1_u9cE;hGRF2sb`OxN8CTe@wU;tOtY} zCJ5MU9r)*I+RwM8+!?vdvdQ+}<+7KsiJIWDnaXG3naToeVy5xgeX_@Ga^2J`MXq0B zv2F9&B18*vO-_;PkAR#a*91e&C!6W#9914JDL_@5W-50?jw%aAyBJfLc*`}^;rjRj zLmjD)AHtN9L`vbe;LlS%C7HJnPspp9o&S6D=Ra*i|Ihie0e++3z@J|W)D!$kukpmd z{>~>!g7-x`yYlBR0XIeP>yiXla!Lx6-lJW34#yLtT~$G0Y|H0?#E30J?6)EA?}!U{ z_yIxWX6v#K5jJND zY~UK&hAxI3<-`=sIz9NN;B713Sc@CA6@`5xf>zK2X_P0-Z?Is$qclZA9nRn-3->rM z)igR69(O=i1mMWBGqA-5fD;sW3*+s10)O6m7tp%w?m?8pA|A;1dFze^v%N+j|4xrN zHm)v%T*>sABWpbIEmeLEa=L1?F?2XZi%ChNuU`WZN*n?HA%M5a&^FJDP6)I?TBv!4 zv=xiw+nHR1WXP~a8WKM(t+(WcLt=wZ_!V;U^x^zWMm(&CVQ>xm^mV~ZK0L~H2Ua-%0gG)}P2WEaA98P&Q+Wu^(JjM+vco#@`-4Hn|c23bI&xo;v z7%4vDo4!Bj_L-wXH-pA~;mBV#!R+ySV<=S4cI$SC0@P z@1Mh7#xCCtxf9K--)kVbKXYsqhzdzVmq*?TFhOS_;XRb@M8*{s7-TU(u%f+qChdBY zZp4l)dRA*L&4;|kNNdi*rZk>LNuH*c=9eIVy`$26c&wG=Fi@x&NLAsn=*x#^OKCnP zm8toKmh0RMnA(fB={wqwc+i`X7ywMohvVbW^EEw?pW_M5&(C*BKE6^L{9aL9xZepo zIQDw|`f-7qhO>!O2MT?IkE_lIs}Ze9^GQn|Kjq8LIDQ&QurrUJHIviF&l7kfc{aJe zY5ZiIef$tWYW&>Kx96>;SSO!8euUP;nOtnDr)Ft=opWjCpaSc|{4;IH2}nx+QyN*+^Tr;y^}L@NIoddGtq!>^!cfCdXBOISyRLN-}h; zXVEcr#G)Dm#qh#v4B*ZEX=Ja%3I-Dc0@Tv39gJyl~asN7s09gL>Y zCznWeE03Os0`_bncG!HzHLyShhS0sySKJ=JCTp{$>TuP55LLtwY_>93i7|q+cXS70 zQ@d#J6~%=J1P#ql#n$6-x+DIuLmhlVy54t)V@G9h3;!(&CeLtx>r!{8N}blz94G8F z`Ft#&%Z{`5^c#Y3pINy8*tO6Zd-`P|>SWh`m?=AZ1%Swy*BOKUrOCni8y4hzp-sU0 zSlHd#tvXR**Ulcqa!`L-E1n@6*HTUC#08X>CxF_6mtd)TL@S0*h-Rwj^N8BM zRSoUw@HrzYjt|&S>CTK$(l%RdGKgCU9J*#LTEKlptpOb=oY zqJLtG1{KKi~G*Qk1-tZ#Gh2oiBy0TzJ5vo&sAPr$G*rF*ub zuk52S7@s&GnC{uyj@?a2sC3U(L03wPqr!3QS{zixIVe;NkLbbgQCMKd|6#ZF9C1Y? z{DU?6k&i)$X2}WA(Qa_zJqPbOc!%vmTJag|!?@u}hi(-029Sle0I;kiVn~1sXE~XyhBw+!|x4ZAO6WHfT`}9Z2KVh2GK=#;e2a}6+tD! z24nWP6%pvB>Qzvx@5E?9*b7(W1DO-dp$lMYzs~wXWKKCE-^A|1sa@;Y-B3^p;3=Jh z7}(zJlT(T5S$Ir?j`Wy*D@YHF^d6J8o<^$zK&VJQf?ATJgC1UVfAbsPWy?UE!1sky zEG2(vy+s4mz1T=e#A*5h7hJi6yHOyP#aQ&NU%yNAE>3EQ-UWw?-o?ATs$@_iTBu=nXXf@lJux+ zREz>3PF)~@_>RtqTj_|a`q(ttnU49sFTM$LXvCfp?%O;tMc}Ry_fojxawMj37xU;O zuJ$X0TP%HDtm?0va4UnmpVnNPzYN&$kKrXUD8f{!_{aDf2Jpv4S``CDVNZQ+{$UYS zo6iREk6GJ92qroGSUpa6wv_%2Og;o+|C;dK1s zMGVl@PB?{wlpqX^daTwOh#H{;!gZ2MWbV3%6&QzsQuhLIl=jXVpe$$)l#h)QC`%_5 z>z4qlnNQ|7i1q|)xDtC1c0A2|OpsdDVS0xjLKE`AL&qHUGK%Xvlh$q9ZcRtm(ZVtU zJISxHr0XD{`#-~8W;P*HNjYW}gD>LiVflE)Hlp}CdW^o@*MTwrnCN7Qyz**`vJ!g< zqZXVGkE=j`4(IEXg6yI2EsPBqwGsdWy+R#72wsZFs~FiGfo=$Rl-{-Z>^aXy$vs3q zY#l!i85-J9k(3?M=bTBqkQB>=0U@buUkM&hD%&&c7c#5$bG)!we?}R0$Pyy?JB1^WpCu_i28$O&K*#+bInk|EW)is?bgQtgr)pYrH*%}fC%G; z#03dDGL%Kf*&JiyNQf8d?Kjh7jPub3J{W+?))XL}KpG^F0;R4QF^NplPX^?~vKEeF z18V4zv6^LQMrq%pHUmKnYQ@uGl>uu=+A7uth;#+iKd@B7E#46fz&1A52lbx#slf}C zl`RY57YD;q4-~j(<|=IAYzk%~TAdzQoyQtFgFn(i@QZ4Yf-PQB6f5y#!7Q&a_fGI< z8zR8eliSEC@Lr~KSVqxKxPzq@X=wSK(W`;4kelk+HOk9S{@ZGlNw1jDL0EgXi=#|& z9B)YIc=9L{bUbmCsk=PNl$kurWb1gBwWD?aTE`nq9WQZ|2{UFlC@idt=jJqhnmDl( zjWz)NlyWE1#R00!l(ulXa`zN4ur-ApU6nhi6j6;^;k|j&9FIkHQtL`k-+xD~%VSlY zl>}+i_!fHIJ1qOZuGe*)bKJ+$=A1Cv!#_?4t7K4wh1j;^SER)^*kL*CaH?R*Gffkm zb}H=jDR}aadCStmT*f8hOVYx_)58C1%IdOD)n(eb0vB~r?8s?n+ZX@GO*>-&n|`@s z+Tj+5kajB&o0@i>L9~$E)M9Y`6xx3Jw6nmIKJ5$y08TqC$cwdwn099Dm(vbs4`U^ z^b3gss^QDcQ7`x9LeYFobh&KG*(RaqXPsdZ`ZFUr2`xlGOhQyWI0=!L=$ycHSe&Tgua6^oP@r_GcgJECYiW+SdrFwvkxI2~P1U=;!s z=cAk?pjXnfDYbM4#P&u9RxaPgDa^dpC5RAKy-u^yy3g4#*{h;TIVJJ@^^0tKaYt|# z)rcuvp1>530}J!>r#gZcr6#5v4>p6vZhz;=X*eqEGC57MYok=)>`z+H#Ewy4W#!RW zKYJQVT_m9yFXY!C3*_+*JUDOdu;8W5N$VZIZOM!F|G(u-_0Zw}t7ocDfx-VtGu4Bv z>VG&>eg6o`{*Py>uD3$?^(}tAS<~Od`F487n%P5t36Uu7Qz)>;!$#%Pe_G9c%#8)A@MFs%%>Ci8?S zjroTRVG$6)RjFWNv4jkXwGx6IsSJlxK`Rjy>RWPiv&-Jc=~TI7X^JXC5B-0MDjOHE zx8Xa#vH?$37oJF}{y00!r!Qhh`Si!xNnLhmvHHeF=}A>zF5JRG^Qzz|J24a4jd`=W zXcAZf#*1MAHiO|c@a+f0cI%0QBs11F$E=d;DRr@0Yq!=j)~KANnYc}^xH;&NkQyNK zh$m#;u=N7cjD|Hh*Z>FztZ@QTJN$&LV?VGV9k3osK*GHZ{^EAHRcAOHujZnRwHHaa zf;UG1Y^J9Ud5-@Ce(RE#{)J z^_OoEmFjx@mH^dd1XUC-+N3mP!c|#c7uvf(#nm+K_d=2Gwc#oYCU|UuDYfvVMU}#< z99{;+>sPa&68Z&ZL8a+@7Tq4&kD~ik_hA;@hN8M~an!vv$ghM9WUU$DP?maYksVPU z&U|q3`fAQ3)g1NgaB4uq{wy9n^FX>abRV#aL4w#dxKbbwM}mFssH)?RYSUkjCmNK; z4QSX9y!9Fm%;~{pFd@PxknM$H)U{b!vRT**O*EqNs?KfK-0#|Lr;XX&5h#>>aZ}9c zhz0-{$aY*Oko{UbdR7AGHUCazA48rP9KwX-xX+kXg^PcIU59qLmo8VC>gqS&kbf}5 zN7Q2B!m8Bq4sWne!yL@M^O zRP#mVwtf}FO?($34jA4fF}zh`xD}5zJj!f+@(EfoUgo)4=DAMh`7872%(Ibsekk+w zl6lUTdH&8kzhRzdn1^1pu?xvo*S`t&2yLi3%}KJ}%4ZVnqCY{2*e5{krE3N8!s5~M z5`z3GK~6-5r2ONVRG7OD3oH1vT=~29ITqRI;+~F z)Lj>@i^Ib+bRwg&7g*a)z@i_xWxp{O&4dH5IVysdzH&Zd*Pw{QdOJ#EQIv9v>)I$r zByKrbALY14L`Ib30@rUK(8C4J;C4c$ksH==}-nb3cy6~g4wRbQ^T((egpCQ z3UPDsd;-r$@yxb=+yU&1a5J7u?6~wYjGt@gVcB<(hw#tzV;u=wJvlC}g%*@$DL+2} z{2)tJ7~m^tbfKAk=3@4WzYR7Zm(L8Gn*o!%s%4JD6>=Mu&;j<4^K}*wT~p~)jfn-S zj;)bc0mvCVS6R6Qq=E}?%#9$PAftQpOpktZj?(nHkC&-Ml#uNEiP+_VJ&SXinzxrt*3r&o;4A} z8s?KJ#F$N>TTSLf7m;VBi~hiRQiBM6cB$yBVFRuaeKrtpYsl8tU-pw@MrF$6udf!# zgPEKS-nJf-$sdVg!*8oV&BAL8UCXZrre2?%S?vrny$@ySm%K)N)Bxk*j~{WShk)5- z-Eqo9DVv!-xHu9x*OK?K#dfLQ&9**jPm+i35u~lPlBRB zt9TcVU}Ik8I;{j3qG9S5?oQ~{8%nUnOV_6|>J7JijiG@|U@i5E&~caY_2;~4SFA?oH-TZJnXbSXe#62!V zboss{`o3!!#N{Y)>n+6kQ4N%yad1FbADkdS$rFyL>w+2aZmaM?qo2=le zR0aLdUcr)N1$nZ9!c+xSvH~U~Td*iq!L_LhRwI_ZGfV6X7YvY_RvZT+b=BU5({ylS8?Nwsn?ZsnF zKAHb~G%`7NCidi~h{ypGym1m#-zPq>xaS15O^g8Jgm_wj$9kwCD@v8!w5wjjqmUF0WXj>@2$bj z!8p4E%a>C7XHX4NZUy|O!>U=D1N{l77eB%EVm-Eg2b;4{+w&vBQ1{z=IrtJaRX{Jt zy`<#?2VhHXFz2`B++f_(mdji9?}cvuHEz&0CQBV;m-Pg0Q$p>reat+%W`0`_2R(AD z8(^|qX8jtlW4+U$1De_$Q_F_cp{dvV1IO;e_{Tbq#E&S=VY|fG|J5kV0v=(j<-oHac;)lxaFygW@$=1@4us3rU3l@EL#~Gp1z0HXfLi<> zp7ZZkp&sIH4aSvgX6c3K*E&nwtUR^%saJuu1I`z?2xDq*iZ7+`iT}RU5`NgyIksh-F={ z%bvkDS_KH&1hx7i{7oIy00N_f>dr$`UoAv)tyw4`R=Q~LG0XKL*&%K<;rWj&9J1nN zt5%qAE>1D@?UT-vg`ylz=giNqu=wQ~UY3#tRLb%+z-ao@;z>_CSh?G+ zukr*E)?&cX44KH`bS18Aib3jNkgX+stt0=$k|vnV+;QvAFUS^8V(oT@R%Bo!SPhWJ z<{<;7xQ77D%p3QC4HoFBn+d>1h^0N0vK&`U^Oc*?6%<_|aeuo*slA7Y+hdHpJe!EJ zwXlN?31ML9bvvK~usc!S8UaL5OKzed)>N=16%5-!kCG#e+6X+B-wHI? z@g&~Hx_absD!oO!l-?#$0bz&+2t)c zGfQ0_UV#*^`4C7oix`PtT`m*N7X>De?V{uf>pw5*+OoqGdCheWT%06J?zVojQ#3-P z$Y$}HBdU?J!JA8J1aG=f0LT}L$BWEsEkiDVQf*K^{l!@b;=2^HnMq$>g$FFSb*uOa z4#UI7dqpbPkP0qK1(&3P^{HTu9jvdj5CX`yRN9VIur(EINd?2HU`~pHu2j&G3U;LO z9!>h;XaId3S?$W65y9O8D-jj99w(p+P=BZO zA-(Z6{axXphii?qyPt!0WqR}-uovz0;E;Lf#6V@yC#2T&K1<-d8Q+T)wYZ}xW$ZjE zjh*{r%}B#V(DBz&Hq^JJ9dUm`$A>~ys()UpNBp{_>F>wORL%KebOQ$V*~_ZU>p*}w zY_dhiR+Kjij)e$VT zu){eJBBu5~kOeOpe5pHymr_00iqoXPv>QyVr#13`nDLdSxmUpMA*~nvV6KLl^Qe~j zf^cm+&5{w{3Nx%WqE?(AP(m-E7A;dQ`&P4JtPX&pF}YRK!`4ec(XW3QT}s5hk_k5du{+%Tn2mISJNTu zggtLtKJ0mzzioS7=63GFe+p}{w4o$7a|D`6VCZ+kdjjylB{8f7pdum5WHu-0#6N!q zOJveur_}uowblNC*GTbZ@a+NDpz%5MYumtb#K%n`wBGoQ7y6;{@rcE{q9!3dq=cCO0DjP zMtW9p+Dp>Vy3tPohPgb^ZrRCiLTfv%H$}TwcPh^g$RUW|BbwXh>;NyDQ|=5sM0{U{ z2!)G;E4mVaR1P-D&e6gI+z}mJFLHciC%%d-T|03N;;7xT>R^(R<)maaE>LTljC1}? zUQ*JZpybbp+3!$JQt}881SMfwJOE1Kq}^Un@&xEw0J>_CJvJpm?0DF$PU4c8!()$h8}Mt880ir(2C9x`9R@BX7Vl?hua)QHwtq8mUrXj=u8QgmeO-4V4is>uf9-}Op5QYHEKLq`ae*h>!9@+ z6;nj7vh~TPc*$mG8?cxv{|5th8%lNE2*9@pBuT(ZS*y|Qr?FC2v>B5!#=Z+;AO{j5 z7fhT7e#U9*J%!-%;pB4n+p2rL>OSgo@AP5PbMKJ!qW~84+Z@LX5qixP{TVe3bSbi@ zRh_XK>~~t#8Bu;?h`BLJRzrLshfPuIkP6noU5(Mf$kL@%9zDDo(lIS}Smw@(o`khy0m^}JpqqP_d%GLQZtcw6Y?8d?f#?stmKD8CAot%Sbg901b)E#} ziN7Og+?ROC89p%LEd2QmAw$v^AQN6!OzTZ7G4hvk;ZugTqy3))H7&hZ(#W`o_FHe9gIL`sK9RvenVkOKZfU6r13W!@4v=#1D=;+ zDE|t-b@)+@%>;~VAdzyB7Q~ZfYi;*QATHhx?7_oWqNNIvqaEeoDnvLzyvdmILEAVhLt~Vw4No-rXcZrS5 zn1WfFBk4e{d$+sYeZr&PRETZez;&S$kon_*n9@|$1KTe>0jn2aDTJfc!3T?>vj*cAdmZS41?E&nS>R;=Wh47JG}bX zp}-jz)|R;eTcb6D zZ&%8birvni_o*gvisVctz*=Lc`6j+2Y=RTlXyj|Ub@^$u`X*b{H@ zD$nD~BW)SJ%rO-{U084?n0;2MnG-<4&B~(9T$j-B+Ck;Wm;fgRlQtMI=0X@!EClhv zRwTH*Iv#aQ8?7`AZNR_z4Nm-@)R2Myr45;o1X2(jaJk#5%AKHJL@NdcrqlysN7E8r{CtdJ8v{?| zV-W0J$b@tpJnk<^+}gi`XF?s_l-ey@%OTdj#sRfbg!bE4E*Ru{dx6Mj|FdODl%n(J!t5cqNL9i~%8OZ=Mp z0KbbRe)Whg#kyLrTf$;ydA*HXu|$sP>B!-u?s`|ucHAN{J5OT9ilQ8UNy@F_c$dhP z5FYlK@es%fr3+sU#k)jqB9W_s$p*xmf>?=M1&bvo#8ayoJ&Y~+HX z$XZrp-A(X@;C@ehgK8{yu%Z#uhboT@LI~UAdH5c}^|i>UOMID?WgPGv&sy;o1rsd$ew2wjvtn2{ zjd4McU}@49zX!Qmxgi4!w)--{{+K$G4oq^Gyoq6wUKIw8mlq%ZU5JJerSLMST267k z!FJ@Nc5l54+EV_sgHZOi{sCYx`aDctNN%!|p9JUHZUqNG&s&ge{Ys=E^DYI~l-3QF z^}v)Zx`$spUdp`>>yqEF*4KQ7xIbKWkpl_nq8vQZ`080kW7&Zk+HuR8Rj`hH_19bx zyEczy%;ns;5KO<{Dn&YaiRtNlC3ZbeXRS6^O4;%KMJcgy6un)HND?Q3?QGBp6AK#O z3E>LXk=i!0rO3042EsT5X3c}XOgp}aSzE*W=JI8HP<-sSo?^Nw_G&sI+bFVh9i-RT z$lfbT$-As?XIOH-U;iw+io9*4>%szY zRmA+YjQP#wVLl|{yO?g)F})MwJ48fR#D6wfQuzf@YAx%rRxm6P2fI9v>#V@r;ClGy zhBo3$T^p506bzT8zN$pgSt>8nsmCuj*!SIg}Bu?iPebG)_%Z6m~~knFuZUBm50uI3$gElabh0+ zyM_NAz`y9viLBTwATLY&~v27unAR;>oQJ zv0=CiyKV?O2x}X{u^$0AH;tRE$#x09ED;+btEEJ>{wRy(5Vv2m81^U#ljOD*%S1uj zhZ)8gy@7ZFsKW-@VMz22_tDs?Nytw=vo4&D9+HBQuFYD z{O$nqm_WIm<1_NaDDfs0cZ><^B~+eTMgHenzI_IPfD2&12Jc$GE=HHhZ&t%jhoK1B zF`F%U`9Fkwg;gFOhf``_ByF*e#cIvq5^E_SVHhwg0D{|?Vl5ExpOrJu@)D7Gxr&FT z@4_658my@-i0M?x5q=HWFe9&s;d#uuUL@KvDYayB+Tth%5pY$`p>hv)}HN~y7R*ekj0j?F0VL_rYnJO26*#+&~x$jN&OqLqLvE-bLU9jD_R{b0R> zr+`}?T?0fqabYQrKzrhwy!soSL0^b#Ym8|nP;3Y6!$03X>bRx*GVp)?zD)d|v@Z+# z?)-h(_&;f1H~bIm>+a=T&zb!inz_XG0L*6Y7HQOOh5;=PMs1ve!}h>?a(iGuwg09d z)9ZZ4JLnDII+9H&6;1GoCQK4dm@1lZk7&aDL=zI|)8g2H>VbYl82-4)u@C6KjbjTy z|3kC_HhH3d%I`iz^bf$|`7zMn2P+{O8rcK%+hFlL04rg$>V69cFkspDxj*x{;~GlA znZ-@i>uy9};L|?Ne&JGgTN>ka>K|j=$ao7bpZDN{Y=gd(9W<96)Z-Al;}AP&>LGSe z>7h(?$DM~n2OVMuO+Ca8nsf*qq)KB6I*1P6Y!h1eHW07l&%MXz#+i=yHTPy8ysd#xpTFSW z{J#)E-(LiRf6271A6O>62O7iFA{^KiYJXU$!Gdvue&_hyvQq2BXry{~oINc&xb(8g z@5mm0xAd}m-vQu}htR-ML4-+y2vZM{OMQp3@V`_L;Z8w>`G=(02B%MGCPo+U&GEV6 zONd@09rmD$v8%;x7hE*+5eRSq1UR6%n@NC=T+$G{2Lu2!z^Dx{qKkd(V)n1-=w=^_ z#8LPEO5@uDE_$o<1$%Vi3f0VZPlM{9clW__9cM{!8_?VmRliwPo5u|Yq3rWg$hYUV zZ;?-YH_pmMIf{~XT-+OD%GArpln7H|LgJ*|8!!vj1{{?tfdv6JKYoAwn3a1g%LQBI z%|!SPve~DI_akNEL&RL__Y`pF<|6JAMNkN4u+2o5 z`b~JEpWH1oaVcJMM>G>9ad;%dHdP0^;a2Jzeq|w8lX9p(72gUx3WS!Xm)FCpQE|nX z#aZ90AA?)8#|$t1o>N&mrfAIK((h%|kGY!9%=$6cfQ#<=URG)SPlw6_Lc>-VWAj&7c{jtQli}hk~T?evrj*l(``dBC9@--|37V+vYv*#L!N;d5gIwrwF&R$OUJNmIeCxioK;aj zwM2v82-d6L-zWMbWGN}wSfVs-JT~VdrRfWNpUC)8uF|xQXE-CDeM*@BLElqUU5K938CkxR(6EePO5>5c@ zgC3o$C@WiWIcX9kW@>TNiBQ2Oa@7y?`#Uwg2z2=faXx-=fk!|mud@2#16Vj!h?(J% z48QXbr;wezx_zC}bTy_By*+dQ^Tz?`{F22=YL8WYbcPI2vt$MgF6P<81Hw5fE)h@q zXe5|)8)g`M@q7|Tx12V-E99IJhTLccg6O&GRo25eT?GkFnX`&h#+`q2;;5VuR

    @ z!}|Mvy~D3Z5xc`e!U1Hugg~>%uyHd9$s_Uj&4H4_rsgq^uFeKR3qk! zu%!V6XNP_w4!)1`Fr5u_9Kl|*6En^)uwAhizuEW|!vf(#TrPeE`27UGPO`oX-jC4E ztVL*#X5d^zJb-gDIE@4ehYjL#xaH5qr$n1`{KcmNKZFEq^_dkeY(i7sLSd3QVLpVq zaLAhQeNaWu6=z&{F)o&r8t!ny?I&Nx6n==wn4O~)cS9)n1po$D5}?o;acyCF+6i9! z-aH=2lt^_GRk^fgx=Ud808+ey%DgKlHWX@Q0TH+JK#PM14X@<|y>Jt%teq!PjVbtS z5n8n)_ysOv3^r@Hx0mWq)=)IQc^p+*Ox$;&mT@@@bDr*es`#WH{33KJ9=J+1aS^8( z{KDGeMUzA80eA;K_H9&=CZa{<@rpdr!@Myb)9?JR$tc4%ALpazJiM)W4{j&cEf~6T ze!#wZ=peQZx9h=!q7tt9H0;8&ri0d@0umQZ8j;pmpMDK$!_6zWEfJM?WF<3XC4E^* z8!P$ZIo3MrQkgtfCg(GG50l?T@@aDvG$l+>_?GvV8t#WTM&_eh+<%Jei-A_5=#N~S z1ogq*;_ zX~fuq%Knr|SAl@g$u0#Ekvah>YQY?Q_ir9lqVC~#TmR7vo*(7x-TdQ7X(ocL5hOvNi?8PFWt3vR#oT~GhGiE%*thco&zGo8FjS+pO-1EAr= zYx>CFsLu8{w|t7y0AFeYegtJ86B{+$YLOc)!W%9MsAQx$EHf?t=YSP`7s?>wKNX`{ zBKQI0*UI?!(&E=K{#hBnJ}v&wh-ZV&K@~=DMS6p*=imSkohNZ$npWmPyUcbZp$zHL z4=s+SkgxZ; zGmt<)V4@<%N0g|jiK2oSB}FtNi8-S)h!-rjqO=sWt+psLfJJUjA~{Zv(pD|DwWSxI z(yLetA{9&oli)RARRk{x7SA{+hzb#u{NA5^&P*nN)%NN0|3BZnUXydqzOKFY+Iz3P z_S$RR`A_Sp_v-jrTSvbq{5rM}W!j0ubSY*GiuDvhvQpbdF5TuF+<``R^6a17!vV^?zV7be3TvkF$u(5I(xZ`In5n^| zsoO!U5WJNr`kM`w=-pky$y(}aFZtA*wUQrnriNJ2Q32QT2oToz;K^nSPusYP3+p$(O)n4X@`>b@e=jbeGg>lVMN5x z9QF7sYlFM(H`4I|RT=h2QUkOs@m|6@w;wI$KzWTdLkpJy9UhU^V0M}aMGXzfHVj^Ue&%3uCkI#r<3AI>M}j`aR0 zB!f4ckMZg}Bd1R7aMiHsM6pF2Ps5lF1DXzxC&*BAXr)-S$IT#YphBRjcD{ppTET*g z4MbJ;ltL(6$J|UQzd+&g;4q|gu{>CdC?pS8MuAYG>HSb*Us1?zy7iZ#6)GQ-zWET0 zC*Zrnt1IMG?+7O`pS?6Z2=7O10^!1%N9yQ?@inrjqvjJouV$G~LTnUDd^%v(Pg5Z= zOip>f4?DW82i=w01t9x{zYoZ!|H;zPH4pzP(>2=aFelz_#WuY*Kw#`WVEi-%iQVk-Q;BzcvaA2?%ct3oqRoMPQ>i1r0R!(VW_@(JY zmeODcPNk#06}45vWssRjVmGHx%PIEyaI&M=^p%=R{Q2laGe^gc#l&DJC&T1U8BR1Y zx>Nvn610+6jkl76VIy?WqZDv01MZNGW~=t*!a!cV?AQ$J%sy4@6i&+X-qWMEzLxi= za`&!ZDqzHadC_maYodoLx#s7@ohx|%lxVo_KlTPw; zdS_oam2c@JU&rsaydUhh@t?#UEdBmrJ=lA`!*6wF&nfG-dvn>w(9(e690x#k?njU# zoFn8jlD^4sj`9JvtEzs7+2C~Axxy)0+}^`j!y@knU+9kD;y^7{bH1ZF(W!=Os9tFJ zLc>jllcP&zzqo8yaAjkn?HjWz9qakT$K8YTO3bh;&`lY-z?zVMmj#|TAyW_5_E8{H zgBUWH$v$B_$}XlUB~|4_+fBi{M81i(iNU%fC?kadzN;c00ceFbX{v7H^Vpsyk^isWpuaP{X zU2hmz8nyYST}<-EMJeV#z?l-PYXmMCKpV0y1I;S23NLKo*eL@|^C~lCy|86b?^5m+PN7?+Dg0!y!fLZ?Lr96aI7VDY!QJSh8mprYjO{Hy@Do z(a*~ovS(ucbZ2nhWuyeJMh#I%ror1Q36ZH0Ix@KQ!a%}Y1o|^zaZ*>9`W-K%>l(aW zhx*}Uf70~}E}ey}Yg8A~cTLRD1t9gfbT)GiBU5*HS-S^s*W{SZdLdb7k#$tps^HQq z^Z7TbfPWWCCH$K1DlUGlsy5f*IL+?Cu{XzsJwE0rmBZDixJ^`M*Q4VgdDaN# zlGPCHME|LG{T62Cn9Ql;Xe1ZLhVQ50#7vmti=xg%-;Up%rf_XPPN_27g1>ZQTeOIE z2cNHE8oq^9VVH(+QkIm*csr2yX>9s9O!~22R5M8p$SfYw?Amepcz<8pA<)XqTE@P; zWh>m1k%ewDmZYLZu47T30iY8(9!%1Twj1r zCf%38#@E3!eC|Q%po4ipUx;Vqbnr9*-ZTYjEC`PB}*pm5AQK7s+2Zf%c$+Mm*l9%3eG7P1y>t)~0lf8yy{WA_D_dUAIOmSKjI$D?i)UB`CqaW4&7r(?sKom~8&{E%IJ zeNNfZFpW5o;ZjYHR_gA@kZ8p#!Nku9M^Rxwfb=(5&aK0{r168R&S2ccK{6uoVf2Rj zTFr{s*ck0@wKuC}{_&{QW2eWKar;gckGUaR@y+LBj$+Qt$rLnmez0~eE##KVkTp6E zJz1I$*&>WF3eJBc_C)y1y^+9{aK*+cC*;6_`Lcs9*;ZgLG+KR=LH`smSQ9YpbKn#2 z+%MgOL&xenu)x(C+SL$<_W%VA)>>EHTn*zA|2qyX}GnZrrhU|EZ64=&FFLIAycx#v2l~@{@ zn$9Xio}0Zqo75ec{LSQ>9zn#Y*~@+H953ISq)e9UxRo)i~* z0(FJ$x;ey}Qz;Y;%HFgpTBh9^4y;aB1Lc&Pb81_n&dgSLgJ1S-D%(_O;@vrZ_MUZpK4^-6^DVB*~A=)X`C_pT#1}-vxv-qG5}fPJ6v!=1YAoUs1?> z`O_Z~;B##c{Xp+;xN}Wdvpe)!b1r!_l@6xkGQUgw?E|& zOKbI7q_HJfab>-gUXnL^U2%-2U;E|EMsT0TI7iDGk%~=Im<6Yoq-!H~ z%dXY!u?r&{77r@<(nnWe3v%#xb zB)4&%DZ)C7fK06EXX%m+2`;@fh7}sRxi9hiodC|=)}mpj! z95gT**#2+;Zkhf-JT%8WAK`>Hpg?;R)(}xHi5<`O-dt1r6^l@+3mL%+-e7}u5;dj@ z2>_zS;o)fcO#LtI89+V@E{b_tzDh`)x=!nt;|vq@%^}0{@PzOPZU#Ew>xxzzS7_vD z>Yg2tl>1kNZkjZ>;hmQ-_5@j&IWS%A$v?W=0HNe3y85?~zz+Sk#3j@{vko}IjuMr7uPtM6RUi&!J6yH58sEZezW52tRv`D)w` z<3vm*!Y~Zzg|`U!JI7tWQ}uI zYs}?rrPE|(tLjyGuUCz!^01sLyQ#|gO33WH;wl<$@)&U5;R<7#;nD2=rgOwvB2r^% zz2fSLOSI=t30f1_SoZ#S02`iUexvbp&+LEH>ombB@AHZtz5DXDd?;rI?)@}vzAO{$ z2;;>s+4~ilQ%F{Xc7+OI5yH-;xMh7pEW)KYoy?zd2GVk;_s>k>y&6eXqEv~-l5l&Y zOss|nQ_1evbZDLW8AWSlp!S~-=s0oy!X>`aslWs` zq#=XW1cjPj&nfk5|3;}YkJ?e{X_-=Ud#k4@_KWw{H#-%%0ks!HohbEmjFBUj9BDA{ zS!3i}jgj){EMAdh-=>3(5xdtwlH6k|gVNlasE7@4kv<73%osxVifCp_!0{ zW=t+LmwC|Cc+f<%(7ZJ7ptw2Yi$L=nbHwNL)Gb+PnyDy@o7HAMD}NLdT+E7(^X1{j zB2mV9ui=foa}yn+qH|_<`1Hj^b-zQ&Eh=@38yBZ?arb z7(VFZTeA|ixqF5jmzOuYUt-Ug*eTved^nDEbTp7YbMBrqiH)5wch69s@#BeDuZW{E z24;#PU5yW(ba&qw4-%BgFifnPgU7XRGA+qS-8j2p&%VlzJ{FRbE9VE}`qxaXkWR2?qisYgPyj2?C;%#QX>ZH&qu>hp$U4rlb}!|2fq zbO6O24LriyG@MkZqzNbx`r9wNi>U$Ga@dIns%v)<952G+I#d7$vBAAFAtb*ozEi^H zADGWg9=uuE8!CCEaereXce^nId$W6z53`|&OQ`HA`KGVfDIfPQjZ2+zH1ygFp#YXr zV)+&qmG+mljYcos=!1e*WXVHA$!=++$xoxgSJ5)FrG*r;Qz{x~o*@$;`h3Ha z61|Eal*05N)I|%8XvBUkVsByNYeYz|rVsaDFmlhe(^mE<@zXpj*k7j=+~ub(L^~yj z<4T1;#OlW6(gq}%ZXJ+g84G%L_rru)rN*W5uFv!CSD10^zJDEqPl9spUhck4#W#rp zxACgJuA;`w==YxL-*KtS=(&#YOrMKJ_ql_CN#4byT%5omj*My#cYjKO=-lCD;*B`! zle;^}Dz%RED#6p^le(jfG9=OGW0}TKe#GgEtg$h6y1jGm=Jo*aHg4`3?s{0nLDjG8 zl+rN#R%5Ei+^t^R)~*+IJ*y>cqB;KDCHwD}q2&2QA^jUWg8NCLt?;Q|vgWquL6LDc z{d%}rh}6aC$s@^!3x$s6gdY0KCcBqoC?y+c+VVF+NHRV2*IBX1a{lHL z=+2pAx~Mcm4wLy7IV4so$o(3hDsqU5^k#x4DKIaU)H-bqr_c4 z+rVVXI4M(xCew5H@c475pc(URo(rqOajAN87z)nd{dXp|E5#MM-_9xSW#0Yq@eHr3 zb|8f+FM|0KA z%Mc4qzv4tg%tC6|nOf*}GcjH+AF0y_5>lV%>CIMV$LEa-|9|Q-B7KarmU9y7VcTER zspte%t{ygrTT^^=jr}q#88dJQ=&D8VG%-niP4y@;I15G{O z&#h-SnGRCV?-lIyE<0E~w{_G51r6gGQl_ed&`*t5=XBL+uAG?G&Gl-H^$cNWZn%X% zo>hK#7%ZB3)l*M9&I2UR1B6uxuXVZel~Yh{qU79-k@1~d_Zt}}n^Q!P_eRD7!2Z0EaqkSFPv`#Y)O7R~Gb3Z$ z_Y~7-9*Sy+L6p=*^BXNo577EiyWGQD+XG&lfO^ z`(mGh;=M6hScJf;$P90J)a{syJ$fqElCf5xI=v^G7HFa5cRAG~l+i6mS5^v4M z%f+DP`9k#5*vob0P_l$onrmPHoPXGXkt|f#2!AiyEVdaV{fKk?$9H+urS?M zEZMl}%rCl<0!zj@K1D&DpT&}qy`&O6QX_A3c+04PAdzf#&!8+bhK$yUu|>%K&nWHD z&}%50?GYB{%CM#Iao6yQ%)VAvLL(I)2NUg-7=CP$_s3aDAFB-8*L%B?;I&9#FU_0E z?XunO%BcdnkID->oZ53FjL%I{J!!2xbEx_2YE|sueq*44!(uCV!72{J1wu}N+%9RM zJ|+Y%GB@ZB;Gq{e`TsCM&bf2kOQz9}wsih`{ght|cb6LYEyGaZZ@4$vO1>6=IzVWC zDQz^rVxme+af!y#aQpM|zk5aHQxw;Yy@Pl6>!dJ8?d-_%xZ{TMiOJK&y72H%)es>PV*a&V7O|AiT;kB3rwucud>wzBEO2PGTnt0>?n;*d*B_LjQX%q3;x-ud^yPK%CSOk3 z!3SUMAoQpqbeu6k=%0#|ED`#oST3RWhdReW=;K7_k7o!yoaBUQYJp|PTKLx&Y3;2T zKTR}&?_F-ln{7%>Mnl%y-K05AMuS9NJU@d#f21e~v;qJ+_$hj_6yI8w%_Dg^bSdg@ zLU@4L@NPKU8iE6_R@n1>m=Gzk%AVI|Ge0ly=02R7wz&={vl1hV+I;5AqbuzajP_V& z59~=L8aGqmQydRfNU*b$>Pk%Q&bf!;BrI-BD4iuV2_%!11OfIR!15l~XY+S>+h&xe zEc>ihjtB&G$9kSS4~{i5jhYfBW45xv+oVfk736^ej&(~kgGZ7ln03SEzAG12IRM6Z zVpFiLotXe>#)5G2NN)95Wov`;w5#--?R5#QFuRLjwHtm6#Yz3mjvlv-*$E$d=e|qA zJh%2{S@Y;M{vxNZ%Qy(u)>5QpKNm?(Tp${@&yymkw`jc{e>XLbXsQPU&akA6wmMw) zDlipe0W4hhY_wuqFd+>dro;JJgquAJ8V zvvwYNKs#^O^?f59a6$pBF`SRR;y}P!+on}yw=D9YpZF>cu)}4q3$(^hyUG<9GAt~N zaS-ve8xu_SE2{Pq=PVA%ry-!r=en!dxrqx>zor7Rb@7y6qQUBq(1>B{Q{4daJ~GIw zxk()Oljxr>yx6(=M2Z`?nBSs{ovM#a!%4l&V8QLLq>Z0+jaGaZtm|f`U#aH>ZlRCoc^GvJ6oqBbJ}aDwp5?rD@vf1w=1tbArTQB$OC?${iWd+HoBt;h8}jy4=hZ^J;1%4Mgz@O9bEbC%-4BT;Wk-mhADzTo?$i-G>U$MubW_fONjmP%`WWE%`DXcnKKW7{mJ18}8m4)Q1=w zNOlwN(+188z168hb^M^*&;KrdzCsIy>3OU9d5hKV)4RyA3lN;7t@A(L|htr^)ykNGcl!C}m{G{W-CJK?f2>cXdgY z9qmM~)L3jWWl}gMTD)P{5?qQ|ohQR<-+d@h(!vz^Ee?S$3e+~hNL<9wh_>p`VpYVM zP{QfE$qYd zZ#fhnp%ouZ;o!rVxcGf~BA+a8D|+(Q`3|(!@VFl zspEo$ryph)YrUu4=>ZZ+i3hOTI3Y<^w_w>LO?BqQLrihb`0k)p?O-t*0dxDw;}&Rf z`+rN5!zxLOvE$&*{3b-A8z$mpl9!ag996+AO%LyDuz` zg&#tP^_rqNGm7)#BgE8Oa99-^Fen0AhtBz4F>3UfS#PRWX!P4$|5BIC#4A zD5B$BD9cGQcXH-uw$sQBV81t^EFP(Fr(7%pEM=?EAiTxwk$-ZX*2Hc~1cPK5ZF~Cu z{BXrHQ_hM8-a^kc61-pwM@fa@vgRRc*Z?mBUwQG;^pK{mWM#A5&bC>5%xB~8rf>06 zh947)$=_^E@TXj_j>3Xnvurxqd&#L>Jc$bD4x`b3;JK$Yy4#*bn$;|uTeh+)d*D&ArB10*2B0eV+f>y~c*G|Lc2= zzmVrYyw|v#BLAJe#>qY?|M|T}eSB?eYH6z%Sh|KTZ$e3d$LcouJ=FEp}7=hh+wpqQI>yX)der8uY*l%0$(1)X;dBWHgpDD80y-p=RLkE_JVYlmsCbfPc94q6p63Z;*QV#Y!+QT*q4tILSi4Skg>W-taze|4y+%b#m7nNDPos`RcvlJ zYxFzOJA8CTEM7u$obpG^qxa!U(a;3wJomr7vIl);*+2HmeohasY))xBoMg&=)hqj} zUfHT*`Il7oMU-v0wkt39trTD?Kz_C^#CzQ|46^T&FC3Y*QN~lLmh%2)?E^KJ3IgRG_&KE2>ZM2%ZARW(3{gHnpVV7Dkz$8Him!ak6E|>sA z(ecWBV8A@}EYEHMz+73#zfnB|77pqz#6cZkVJCh|p)rguQwDEfv7k8IW3NmZU==>c zlmP;nVUZpUksd8Fla6I|-#h^seDefbFq1Cx1RJa#dm=qHWX%&`C!s@%zBrKBH##<8 z?mit4#*VaiBsO)YYp3ROeZEIx>*U0~Q1FMSp9q`gbHJ?P#J-Z)$%%b~W2ega+Zl6a z56Oc%(xr<7=t~`s>7V$~b7r4z!urmceTI2CWX|lt<^_{=K3AVPr+p?Q1En9Z@xpi7 z=wR($APHfsSy-$6GIHxGdk6CzMss@){E@Kgw%UHz4G8Qqi8$ayO8*j?3l36rv1+nv-RFy(jQny;o&Dz;vVHL-=mAjfM z!@;Vy#K#!Ut7)U4u-$iH>T&8of$752WpKFe=ai?fLCB@s!zYXy#+Gj^`~P-}J)bs( z&+F`F_v_Yi`>e9(Fw&u`&4C>{`s4H#AsM<^!zm-$b!y3?0CGU~V9BaNY1n0C0_~f! zDGw#3?zlz*yOr47A|o+8Lc82mj~Wz4Wj=L`!NMUjUh89X1^omu78=(=!KH_H^ub7+ z4j6?0nk3-6$4g+l$NR?fK##4aRW4GrFBGTjKNRQ_u7zhF1sPSqZ#Ylu_2qb;H41jDkz7TfG7lPf8J|)K> zz7>4DL76}k3fudowH7E!b3rKa_GIo{C)x+hD&c=gtcw4GNR6w_0xkCbw+OP%5*UqE8|xwW{8RD>6JSg zVF7^`4ob8Y#+X;Q6P_QB+no8eWO{v-8yc%gdBh(Uo4NUD^XR>p$MPEi#O+D6dyJRn zxd%;}q(Vu3zf$YcHT&DPp#!@c%h3LMfU3r3W-3+ytp~V7 z`PbHi%?t*u2XC?-;51}yq{k}}{a+L5(TEWojO&`lbq}`K-IF!>JZkd4Xcz}hN?(|) z>7{2c&NlTdO4jt%v+u#mL1?MC;$%&KJ^LqXO7tvA)(q5hAZXUJG+8r9&q2wW!Fr;3 zb(WrId1YG3?sj8vjUALRd4+R_jN_CbdYlfNZmki6~ z$7w`Or59G239C97{9c$*4^A}G(jBUON3!NaJwHs=wCUNFtocOGPm%_&NP_#eKNnwj zu<||+hZ%hRPx5APorV1}AgWB(Y!Zx{k~MGX`Bt*V)zeMZY}0ew7j9b>ZTm~M?6H(> zflc&-)Ej8O)NJxzsp;RWVKg=TF{|Ik;ph>w{}{+yDFp?XiqsKT_qLDys53rjmS7|lU|^-8Gd7H{D`XhHm}*) zW2>Eu&cS*LF`>jBY-`l4sd6svQXe@MyCn@+zX{GR2vw}T^9=$7^B7t4QA@aE^%mE@vq81l^j@H4^@xYsv2y<+P-d(V&)aJKC~;toB;= zUBbZs-F7)gxWCP|Ac8V;YQ`o0c>RB>702(_iY24`R(O-VzZA_AWmW|rS>NauS#$R+ zJ_08GwqmB2(6XE6bf06*ePZ-GnA+6v?$xI{@Jdynh1V%X6$lCgGntu~} zih{R2&G?Nt7ww4JPlXekcSN14GdWXzVh3`Is=zA-S=R(Y1_rU3*|4tJu>5RTK{l*g zHmrL#tS}qaBOBH;8+J%GtXDRycQ&j8mY^4gP8(qE2?pm~0yd&$Ya{kku_M!{jO}2C z=2)}APuKob{5`O=+z>~-T(;Nie^ zueeBa0$OJ;S@x2a?Na&s#7g!~zQT%=>>*_Vqgrq;cn&9P2Ts=9p`CG^LHwUu$zjdi zD@D7o{l+pqBifNCSEww{uYnfGKVYf>+aXq_jBxU?fm$LWfwyzF%U%WyT+H1YpeSS~ ze&Mk`*4$NjJ7iI2ALV=>5Z(12h{L0;+{nWVCK+DvUj?L2))E2 z%v_W{9WF?E5Qg*NQsz_4fNoxH;*ES5enQDiIUXlLy}q2+a1k>Xx%ZPYjb@aX50m)& z1i-eq-w1o`M8AI78++V@4%yFfGK@s)yrbg-mrH0WUQ1T~eUt>bj*De?3h46zb5esr zfL0grQPO?k$QVM}*MZnZ3c*ztro_0b$Kdq}3fA6FIQZ=ytospfo;tahw zyHEYanDC{4(JSfjyF&XqVW@WwhFanM4!}^~ot-cw-45(S78GI&*|HO66g4L_0QA?K z$nqyn?_&tSo|F4qw#|OA-I6AOC-pX4&0NlPJsBc)T_NNUvY)Z+7f|Si($~D0q4ZX6 z4nGn|v>g?!m2|)e$B-k#iO1`g4>P&9BPMaX(-4^lswb|x_ARtl92_9edLNLTCB+nv zbt7F9k^1EMe%h-^o0z!_QR7!sVq(=Wtkx{A@(cAnwo|seFR1Jxe%XhvA^|C3F zMp+$=I@7QCVS+ju^(iySfsOi>pLQN;6Ejec2t3$gvoF z(^l#42>lOT5WwVR)`Ln`E5=Z#v?$>>_cnK>(b!`e$;=EMGuKD#%b6(9Cutrj;#2rw z|5>W1wvQ1uJCkUPv~#`l9nCZUnQ~!I_A9r!yMdGIU*>i;8j^S^1wPL+DNi518UvrV zuKqiDlIJJdaS=5xbyQW>D{?lhpd-wiO^b|BziwmvF!xU*=@c~XDwC57mm64}sH+Jx z0>0>#e!Exkg2q@}q47i9?;&PQ_uvqsz=dic;STpq&d=1r3f9ii>%MpQJQq7UHHchJ{h0-a1>|k?5CuPv=U75bEz6eoC$X$l zZg(g96@5&|G8(_{1nN*REW-}o&)&t}a8$^_|G>9E?%w&EL+}} zqyVT@@)-c9A4Yn!8Y^Uf52$7FK8cxsY>y2|R{yc0`mtDky||Le_5 zbyaScxn^CQJ{)Tp+_&z9*GHX1JRIN~vtX?8oqYgg)?IX#J*`vSA%VMSSYBel+9$57 z+ym>{s^r85%$o?w25AlMhnNiZLxOiR-0)t;F!Zuc8HQ$LNpei9Odr{A=o-x(6!cl+ z)t?05hjKlYWq1P1*FAIMEv8QemJTn>LcFEe0tuFwir@JiIK@$YyHs-q(|vgQEd$>y0JnH zwTn`+%~^$!L15}{Toz=qYyk7^t)_6Y^ktmm4AG44@J2L+$Fyq=kLWU=@ZE43FX7cYXjCh06{<06Mu^0Wy}N*WFh~XyQ6z;sQO4 zO+_j4-iG#Q@R7RywCBXU9ZWEJl~NSlRhLHyzKQP-@Puy=MWPzriOH zT@%G%55^Z*Lgu0 z;+(mN)ru~7$G!FuLjkVZGJM4j9 zF51yH3PVn#6>un>>0@-rM$DW9?F(ey4xwE-pj zkFDKnxvjT}qif4{1f+YM!Mle#=;SBEgfq{A=bhfW8KJlFURA#d>B)3Zkh7JLy+(XhPJn-o?+9HN!yRK{=B!x9dCNcj3{>&DbwFKj@WjYxs^6M zjcQM@+G*7jPez%{!rKXCPDL`K&I9}9a|rv5y4YToGB;Gsh+O2ZRK$1=n{`4ml5Sw+ z*!QZv<&Jsu{xuJ8OD*pQWPCYn<*h zPwb!OgR_U&N~NDOyta`Gxc$^B#u7{Y$gw%QssmU0VTKv!lDzLbzJZHFt)g(QOI320 z#R^wE8LX91Fzf75OqvzSN{<|$M%@N z%9pyX)(o$1jrC7PmvLtuhHDWF7tGdZpyNJWbxU7v9hga;dGZ3J6^zmnzv}p%q-{_c zY3^iSTPauTb+-de>Zkm}68<`KXVLBQ%@*A&=lD+UMOK_G0*{k#Obag4u^H94r;GJy z=H|JPaoSH1gJ8EWyF+%#Pi%{Q2N~)n89%Cc{^s{!=fU6C>G@tiFg<^~dGwyaV|hNL z?M^k798y*BCLC~Ms-&voeJYY2ii-8REno1<)q-LeRC5)XFq6`N5x*+9)qBaA#)v-6 zVcaKK-WY}Dh7+y$ICiq-gtl2_ZPxNEyt_AZDN9cQ8u?o#anU=duFX={7MrO!Eg=pc`Vl^+dUF9UmgoGSy9b(b9fLz;`^0KO_-;~J1a~A z&jRDOhh{@oo<`y!>)d_{0>z2F?J=M;sh2|#jAROV-93|dlh%B1aJQbBO&`Z};}Vr_ zcG?|9yAj^gmq9Z(K0#t{VT~8CIO?|L49(tuxJ!mzb;EBgSIs}edL!xnqIBz2{$lDi z2g4Qk57wbISkIvoHNgYkeU6m658Lcs$sDc-aPb2`a(^SZUfiGKUPNQPn{Z<7$I|KX zuj`HZA~Mj@O`ycbCGj#{05y@-jH0K23-Dfb^yvK@U(3;x&T|K=ngRUFp(ge973vbP zGTp=_n35Pu+*raMz5As!B$(7{@>3NHw2hQd*AQ_=+(iIf^j^ByL|{~ z14t`;Dv-jb6d>j8ax0xq1!}O5S{@lCO|Zb2%)2p8M?J%>H7uNonk{D>013Uhs_v#fr{y zDK=R!3WZEFJh1U@tZc8w?)7T!j3&Qfc$_8Q7sFu0eVg4gKxKLfI_hbphfV1Vq^<5l z_Uai#aWxvb&6r|9QEnG1HBEj zLB7?Pl(3)o?lK0K-dvp6B`k6VVw;U{7>bUQ1x0t)Z=1v{6@aOo6-y2_^K8|7e!uQmea^C=# z%h&P?!Mb~&@PXwYuOp98!S*VTD2oI^b6p7!Q4YDc&Qu|0L%dwN)o%IeV8(gCq?EX4 ztNYpn$1qp)Yx3t2E*mQ z?#R44b;2mgb#4I~DtT=a6Q@~3GR27Vrxp?$&bi>PEIZVUXryk35;^ye6%*$aBs2qi z5iSuCXGx(JwxpNL_I3&W-XUo^&Ash35yArv6oC7a|H_lknPPgbxw=R|@|09iPd#YS z8!Q8+Bb|F(Q>&vLiM!!yB9nEh7O;vB5B^uaM|%3M_5vG(y+>%8q8v5V7+HJg6a~eHGQ)F zou0l)3!1Y+z39I6dl=;xy@0W5__JT_Xi@wIy%#+3;sGuA6Pxe!AQd;1F;is=xbj2i z>CwBAujLCNFLxsCObr*Ym*+)0i8q40VwDD@rYOgfC(}(5+N(CO{6{z6!SN6}zlzz0y#zG># zUm3O?CI~p42C1FvVI?@+OFf^|3ry;jUh4Voyb~eQ=AK^HP0UyGY+Xr*v%(zqi3E2`sSkEws=gc}PZ*gI~ z%i{dt(jCkIiqV*OjYxlHnPY+;(70`?ljB*Y^!tl=YIG+(Sj0n6MYQZ6%nq9bo&6`* zJeKU>fyluC#x$ib`~Ez#Cr$6w*A=QLJ-o*-`Us0Vz=IjScv#f_)v+;I{4D$0=yA_60>~DR;`< zI*+e-_#`9wtKC2p!CU;`pAifnC7IwaiD9E$BF98$Q6x7?WJ(KzI*$^AJ03FDwQv^* zt*!=+G#cD-cr-!+b$uu?yCe|%hFQ_8=K$G!WuTTiYN4y z`?Sl0>?sejg+R90hirYVfo0)x6o2yEbth&aTPVm@vJm=^wG$=CZr>lW5Y(?!uei4( zWJ*oYRKQf7f$s$16P5g!mU*%iv~QpoaJtUQpeS zo0)V(>boY*+vd@G9}h3hgG%!UrO~QQ)32d?k->ab$lP$j`};aubb=}t&>qb4NHmhw zO~Mk*3p+d+#2Z4CGi5wd=W~}bEgGrwjf*h{woJ>&WhV#}tbXAFN;KG9J%5A+{Cwh2!w_C!<+~BuWn1<%JwrEa?jrWW=b%jZ}<`K1p zTWf0O{i98~C1cIw0Znj7y%j|<^>sJsjWy52zRk^EQ`nK2hDPmkVCH&qqTG*PH@1GJ zRkV(l)gG^wVbJG+!G0$R{LT}7`fPgaOLLX5{5~&|NjwDKr{Vq>WzrpCLQLj$Ssn;9RPTX~$ zz~3^J;*4;nDA!iD>bjNW6Rca0^N_Isb=D4V3f}!?b8621*Yh$lKo&)d)qEy}H{5IQ@d~J>0Q!}g zIrnBa2?h~i`jux#$y)1Rl<(t^!{S~v7G=hz?kMr(g`R|YTxzHPy*O~7%(>JD=Q017 zsApI%MKL*5=9vgxk(pA^+kMTL2;DOE5Q#J%9v~j0d)T=|t6Wish0qtRl~;^0e+77F z=xibM@3}>*Kz|vQChCEva4X#^AZO-+@Opg}dw(t1_-o*Yh0tE#U{>Q>wxXaL&V7Lg zi7)5uBr&!?+ufH-G<;C?I7vhG;KphXQ55JsjjH^6=F-sN90(h%I=;r>DnuirwD_rZ zPp6tRug&gqRfr8sNXfIqnk2wzO{#Yc4`RxpclNfZ84P|dxxag?-aTv4SCK=452_!< zt9tjpd73^oJ=sL!EH}f5n!BwPaHEo8uqQEdo&%Il!S9&b{GGCyJ#5F!WDEu)Id4rPW=Mv=8hY1ZVVx0N3B+-0!V?1qpwb`XCDmLDEU)_9?jVPxW5t`RX5X{qKgc~Cwz_t@bV0x9nH>`vDf{@PYsZ2N$L<% z`qY%itEg!#&ja#-s&HafQC{pESq8)m7iuZ|j8&h4! z2oh%orZz^7H}TCQ$NJCF=IN9lqx43&yW_u))Mo%Keqlrs9G??FaNY``5A2O{ZqDN znpooA;uQNs}$sZ#V_y_zra`D_PH$C7L$*Y6CU@<3)|4kH6>=rrr=Tt zeXzUlWJC4ZI25}h272RlwrX%@U61YN$?gs8n)n#)zJC;YKPIQ&95WW?siuw=PUlmVT9hRM+p|^ENVY_cMNBEn zYMc%tWyKFG0+=L0r*~El_gBhApoWXA6^`T$%`FUn%hEXCtbiBN{)>xcXPob1Bl)$v zn)YSJiLuH(8q>I~;)s@WLaM)B(d1cJB)A$yD9#@n_ja*XW{(Dp*t%{Z@XLgvOH!gNDTplfxwc;+3;Dhhs zN~Nv=5u`WWC&#tMk4m3?kaTcqtt__iC+g^P=WxA(CcNbS)0>9=C~F=}HELUtT)7+4tPFbWHPyJW%7_dk^}u%7zlNRfmFPbO%U znF>4ghwe3)=?fA-e+nbhugREmhuRE?N=1`+#l?ne*(Re>6MV2y2l2Px2|mJEzDe`g z$?Srn75~7xJs&^p2MNlSHGUBG*TiYG9_`?fT8z5@Oetbm^GVK|$gEv7CmEV?gSAhn z9s~+H(EK%pLg!eu-pUQEKiar_OluVp6paT+xz&I zv=Kc-D&F>3C4uF3C9%gU-DPSyP;=1CSH3T)^F=7RyjG$6#1WYSMyUW?j+N@!yr0Io zcf+J(tYuf%Wj^oP6HSGifq%EVuP^x3jj_cO|VjYr1-1 zDL9hNV65cpmcS`=FJg<5urnLyeWXC@(%w|tz*}tKVD~f{$*EEPEj>{fs(5Wm*QouW z49x9E%bn<$dF5$yc}~CumoiT^hu3aLJE^KIT~hgf0a3MWBg&6Z9SY^ z@n1<(K;=uNdB3sO31WEJHYgn2+I#)OEH;KXik9S+horJib)|gJ*dlFm#2PUiMGz2L+B zf1lZ!M$K*(+mC3Xkya0FSEy`mWI zr{3u=p0l~SUYO!J$9v{TGK`GF4;4S|ze?=jze*6{ze+UWzgiuyl^w6cI$p(o`Dsf# zUfF(`(BRU0gfzJ?So_{Xf!e2_s3k@~&(wVqu|@=MV!b#C;U!8#8)p{+wGy*GuSITc zWbsh3OFyX1nv|x!;7NLLtp^&tLpnMX$A8FWY+pCUq;@B5FfxD>=BzM;731`){Je#r z-7C#q&b7w+RjD^L^(Ifeff!FhU#+8KxN4XplXFd zRmGlQ-6FDwotel5y<{5H8-+|Aw@s?kTrv}bIpe6?%m$Nq(X}|r?H63ShP(ZB6IDdw z$u7(|Yh8TAJxUUUn2kmk;%q*cJ($AAsHw(LTw}!(vCXN|$%YjOR&)=+$h(~Ku{U{V zggPIO9x5WP6aAo4DT)f0H38asyaJ&2*lQrF%>mTYoDrkRwX#38E?3p=No2%JqcdWR z!cWeI_aR(MJ2xO!uI5ki!*9DnW_!=D!(D2+@zb8XdRHn7%Bi@wGAW0r*`Zl5{HxVJ72@6Hs#) z3C)ka-C9bO=>IT|0euRhV@$>^tsWI@;}RLPl1EiEEGLyh+#zW%Rg+S2ButoPFX1Rs z`{+$P+=u%JtA?@&qS5W{LW0nVSM-5I1j5ZRHgu%Ky0hTX(d(k4Rt1TUe#5Q?!&?;j zrh=R$YxIDQ*7Jmplz0cPo|nO^_9LN5b*Azdtc?Pv!di(!{6;65AdD4@DbQ`~-O*w%c!7d{PcW@kl7qK;88Nh4iT7*u zLItVS_cB3vt$s*B&XW7|pw)|b`mJu@_28`@tLepY{-QizwABNZ$FzDZy=G{|Oj2G~ zCz=M+>NynXKHV#~)e62|!G9%~Rx8QDTU}2KtybdwT0L1oYIQxN>$Ms~c53xE^`O<$ zdHSuM&+EZkeG$27^=-=YMOzL3;ykDy;6@>H5Sze1T)u*$hLJwMO@$_3fp4WJ_`sUJ3)E%#)wU;UVL0ZdF zbpT~!xt>CLxzp$>ntG2fO7>ckrw00s{jjHbH~s2*@22Zt_1+gyr`zhiFV_2N@4ZFu z%e?nZdjBKuXhWR>$M7_Zj7}b$5wRPXJFBvD=Q`^B8#vEPebr#zELJc$I`OuWMtj*i z)LRUj&_Ft%j4yU4)T*0iF1C{B3OmPKiL3->v4z9Y7Iy(3UY_~nS?K4PPiZRDE%mbU z9baG=OlM51`wbP495+9b}=ZBfa7BoLjMvdsmW6 z;38xjVnM9p!@lpsH$lb6Q%-%nl6u|K!GPH(ao=f|dmj9s4~4c(htAO_mxdEhvNL*; zgFyh0#Ns4EsdT+uuvoZYdBxMK$89f%W9S019az&t_@w>Fbdk#;6h#f+PwuPX`^lHN z_mkEvBW>!G(2-ork_U*5>7ye_v+BcIZ27TxKh&W2xL4Ham;qW4C*U4N^3*Nd)=hUw zTat75{KwgZr@utczE4iCXAi%go;`|Yd#hy|)rxgoOuu^Upr4mO*w@Er@qk8 znpui&2m?1ck03yv0yvlnwLon*Sn;{tT-pvp!_ukDAEaTn37|`tg z`S1JMC(U1i(S$qs?ed>`*Y6+vhJ4uGK98r_|M+{}^5DHN>Jp)+r~#)f~rnFCx-Q_YLcYAyX2Z;XCe2 zP>FWl$UrRT!Zr)_bF<@=9(dmp`rFM zs0c)utGEtrPke^AW$Ck`75jpM37I_YWs!9~F@X{JPz}z4{qkUvhsFHLB`XWYtRM`CJ4$eht_~+ZJ}J zz%w!@1V#86YHrKM#omopd@^+rPCs->VK_TlZbl3C*rBQm0io*RaE!VVGoQ(epViR~ ztHDD3s_ZRMzZ?HdC>yN07I4p&s_rYG8}W#Qpr)hVMDiK@{uzX(K4oQ!I+GCBGMvUO z%K{ZY7kGlTPf`NWNN>;6ZLu~Fuy{~iAE}tw5Z{{m6-lJ2PJ;k6!joe>4j{=NKO$9o zkXTcg*GQE^L*G>k+6)X++F08%^}LHK%bOcgKLN;El(x49Yrn>8`b=~Lnkd+sz}=ES zOZz6X5J|9bwvRag?k1|h;|%@D)nP;}r8&D3(T#_!&Jn2=NGR+~#as84MJ7=cYIFBX zbc*PQL?(eDz5Nn|)M`oyJEMyIgg)Kwm+)3fn#e%VZzR8^{C>bsj@>)Sf{?I1gA~?i zWpk}*CMZ8A)ZTYs{VIdac%J!agzG`Jat`);E)qG*Z`s+)s3R}^Eo0T^8^&d25(!3G;?#PcS5YSM;hhUy2Kx3FDR*v16vs^V^OF4; z897q=lzSMhHRCojBq52*tz=h*nPo#%m_#4W@MY-GW-~Gd0i&Te5|Hftj-IFaGz-XS zBNm%icTM+T7^m}j?NW7sh_DlEl5|JyXQTK?6ok5EA8)bjy%2zVP6kBAfe%Em);oIT zFg-c0#UPD3shRp|2(v+Qun+DP&?28*OoO0EVE>Zw&x8Yo49S+~0&a$a3ihWl%%%t( zP%`M~MqBdJK{czVnq?Z-)oa{mUp+Ts{FlQLlulHuBF?vq(E5Rh--*~CyLU3MEN5hK zNUCY_BJAL$6$ep~icPW0=p-vSM50g#Ce(f;{qqLeF1Uk}(aZOt&5M%xCtPG#olq!e zL47Yu#!hHM9!Ju!!<4`TxN(#{2vyjLEpWQ6>0dGqJ|9$EY2C;<3N+$+2RK+mX)Bn9 zMh_FfrI2-Y0stu>wiH_rM&vb&NN?D(kkIJ+&;@69FMcQx zdQ8pcgdteFk(cJ`XZX*Og-^!L?Y;sJuC)0^*2^~$YhRqqKmB66>U7%uZtU=jld;p8 z|K2d|j;h(OUTpV2JsE!ntKV^!AXF*AtD%O9R=kX36F=T9{T&5(2xraE8ALi>`%oY~ z*33kSCqX6auIZ+?rKfm-3D^9RiNjq}oBJbLlRhas?-1v=76Oja+MwpnZs_r>DSUd) zmH=95ojV~oAP@To?fNZaZMci)Z~3j_xBob~9wE$R!m)~CX{MDo;V3`(!>ul2nVZA2 znC;YbC2FoN5jJ@i`%fX838osaf0LlXYdBeXHFh)RI~Jm!RWFZgCA8Un`4jaTx0J=t zKgEZYoH>VGvPzXc#Y}otQGX*!=^s{l)*>YC4KX^Nm6!#T3tu0mnawvvlH-fQ75M{s zjenr^>E5U_aWG(sDHM2v{b#A3QF~%3ayrw@aHRvb{7z> zz0a3~b035&RW4zhVD?qzdY)OjFuH6dW`+8M&G34i0)>TCS!NiZ1CY~zslgy=r~5I> z1e0b%c57|i{;^lxBX!sBO6+A$M@oc_94lnG46G(-jz*wBGo)|}3y(EZzLs;R51^#Xxl`D3Zo$`|&U0>IuDFT!w@5aP zvz?jC81*d-c}`g)+!uBifsaTs3TMptOfj~Z+GZ}(;m<9@bS(Aylp)cInTUkXiEw^X zi=K&uwZU2`x~nMNb5xHQ@`!(!suQ{@UWdo!e95>TEDyIg0sv=bmmu#FFaqo*NSWnH zjLB;BmPM$W;Oo4a2c`>X(UfDtp4k?z_EV~<#&{_FD78l4EKU)7SW2>N4-%NSu|-w| zjNAIyhnV1}CqE3Y-L@FZir9G>GJgh`~N zlQ046!zw`CnR`^J0kIeK)e^Ly1&nlWu0@#7#7aIT$u2VMrsw}Kb$17-oPnowW}~z4 zETr)dt>NRi^^B(m;QhOS*MK*Hb2^qexncafL{GytRC9Y4$&~?*`?~aN)ZQ+~ck4N| zpd--$$oAvO^6$76Y$RqE=Ecs=0B2~kGdsDRF%Q4+)PDTkwvHy4YjL=7T%8>=#@{NO zkfGvF9v@9wH{PZ((-d{SQ^L^HdG-tJpu}h35>5LT3(1sG;#I*5G{5X!hlS!kq z-@fdru(61UW)!3U|98GQ52IOT4E{U5IbQ@v%KX3Ln=@w%@9p8m!lG7&{rS2z^6zsC z)PD)+mem2C6&0~=mK`dTOeSF2(@9xVlsS3Np5P-=8wQ)G)o+WLz{SzAa%zP%R6k>w z`?HS>3AuOkr0b20A#azCYdFb=MwDSXah_sP-YRc_fkr+i(9;3Z%=@p+@jA`<>^XkM za)BZaWWPB+;;;qBHbJjQ64OW*mV_%BrW|KgoDqjWV+AtM--3RmFFv?cZ-nM$nTfhjdAs8_`98BR2Uv8U1z zc@MUCYB@bKeZI+swt7XEh}|0hFk10ctXp*W1tn90R$>-&LEngt@L^rLpH6J;r^cn^ zwZixxZla&?Z{C{eDGzg+pNzt_C!f9ZLvLwxW;4vUy7!RTFx&QUOm`JFjVz+W*Jl?$ z(?#uWluR1A5Ae(qlV%gHR+xrkl%c1nr4>bc>^iKKW7;AnK*Yo+ar;)TW=E57UHnxG zZH`0(kT#{IxXTMlTl>`ikQ`>Z%oAr5o#raW^RUbY6h@C7p;H67Rxb?u4!C%Fj9s(m zW6H=;l5zGCPofO*UhGCQ2RaiGE{$6RZ(5?(e6W+YJ5yot-ge*S9TtYUbfL_k{G97Q z3VxMfCH=dS`kmaHSrQ)K>*OG7X(TzB?&`*yy6e68FQBbq`>|7%S_vKQMBPD2h!Nmo z4r|D;Q;Xb~Li;Q96Ru$xtiSDEtICzHd);Ry2!@G_k;;#&*XhMme>6aRnQs-VEVp#Z z@?a**S-g1n)P1$d69`cKA1DP_c>uv*c0Tde8>gVPP6BikJW*OrjvP#n#>RyoZ*1`X zKnBwxrtO?aA^2_y?|xf8SFL3JOV&5n{a&N4Y%MxFjp*c#OCTkN@faWA9DiqpYs~{|pfbDo(6h+($)C#3fNvk|H)S2|NQciXw_t6zhgs zEkZ`HipEZ&jMJ%f@hfe$rQ289Z>w0VrC2cmB~e=pZs1bE{f?soE-a$S@BO*=naL!8 zTC4s2{{PpPm*zh6Ja;|!+;h)8_bj|{`=mI_^u?CdwKVW-pX;rsyPC0y>9LT}+}v$8 zwW6>-tRGzK-~pKb%%fV_(J$vEuY)G5`3V!dSNB~*c&p~fdH|pwN~2Ns8hR&mBM}2;>HOU1l+jD`l*SR=n{4mHC@wDToBvWOVxK2 zFO`qXf!$+kbdKi`Kh0)tJe`-^6LAUCJ=PO0!>%7%@q9Qw6IXKkWss}MkylTiNfpmi z7?p*ybTUI$%ng_f%OuU?maJ84_GQ76jUNS--lt64fwAgp)tub=2^ZyLQ0ERlgg%gkbeq&kY-sA$;@WUsmdxNK_;n`~VVj7gm?KCqh4=Ytb9IdM}31D{;L|kj)uR;nXcNR;Rq{!77x`7A z+c~nvM@f`4@6XaoafTaS(SnJjb|j+RhQ>(GEAQgP+Ax?iX>)1MXnUzJTsSC_8dn-A ztSj9jXSl3&Olmva@2oEL3b%WP`zCTKk|6TYWEj4t4RKu?S8wq)4rY&rhwe_c)#Fvhih-Cs7>EtGYv%eq%S{gwVm3r_sHe(ci6(wdvPV(w^q`h$Io zV{HkPt{t3e+akcXZBwCl@g`U6;~a6Q)dpF|9}HyfSacTrAnWHbRQu9JV7GPP@w>XU z@dz)lJf9fm_CYt1b{3Cg7T;hNkK=j(69djjR`iR;gd%MsYX9^&?CN9iySJ$u{ZC_y zhi?tZ3=iiG788jltLV$fRFG3H4YqV6phAj12Srv%h|87-Z!{`g)+r-{+6Q!WMoMDB ze}jqBzw4q0yoJ1K_UP1cP$3q__sO3ZBl?St#4ZGcEuUjbX$8^p99a4=#@Q{$(lK4T zut&ocb(@WhczJ;j$NH zD}bRuV+rg4QVU7h5|5c{)hEK90N#^Q$G#O!Oh^8^-D5f`a62$3{c9SK`9&?}E69X` z4IgmIU~x6H0z8hR0Po~C$nvR?*aZc1h_e2_8i{=xowGL#NW1|D79t-oE}88+c%%;( zED{fK=$I2JGZwcjRKTsZIcU_qPA#7`68rXKmZH4;$~Vl1h@Df6zS$B*Jp-Oqjp2===-4;I)r98k#y8O13yh$xvUQX0ftIfC*+SHK#GR{#RC6 zDP{6jS>u6TVwsRkic}nUMT+f4^J466-61B{XSiMSbIhEr11=;)7|N_*hKv=fdE`}) z5?T;V9Fi6*#d4a}2VL_P{rkCDx}9?B$`>DreHb+}U)I7nfjibw#f<8q33(^q^e}F7 z{L}0pa{ph7#NKQ{_jx0YpY;kD^~FSD?@v{qInubZNweoQ?%ji0s9yDBM8i$IkbWKn zkf|5WNBzy=;c2J3l?s9MAdu;=bI>RXgqL@nlsf+dGQa7SJ!eh1HWoJSZpmdC)=bvW z=zQC)&kLX9UjD-1^V&=Q3Vco-hxc{FTV7(bIh2x|6@u_;D>k;HlB z^|P+7dT;`7OoH6_0;{rL+iS!P>bGC2CglGv@zY{IEpn;;a;ovl+m*BF>s!>6@Jh8S z#oIhSjkUSkYdo7Kyc%o}YjOfMZmDn<&k6npwDLv2E?KRa#g~z~h~VO_KROu>76QbP zVn@$Eok}8d%mM1m)g|zFAyeaOv0{@DH94_~`!>XHp~$wd!K@@z3pe>KOicfZ>aEN` zeAO~(N?^ri)&mAF0R*QY;74rh=K$(z){1)7^uy;c03Y&*5_9IixiTKRX|8nBbyWqi zBe7nt;i!(jEW#&hDz^ftC-Wc4RyFS<6Xza2Fi}&mQf+U7dgo?yc=u)jvptkjCYNSg~Qw`d&xi~c@<}Y zb3#`Z-{e&_KN1^+rUi%H$(iH0&aIKU!5HbV!BXNmW3K~&cKjhOYFCRZZ!HZcMwUVj z^Sol%(xq740!!xlx%{ta5~W5okFWzf*(QUpCvgKic2%#_2eRfQ+|>*~*Nr6@)}CpL z15+5sDzop4nl8z=#kW)Ik*?Ow$Jqng&|IxAlF`m(4h|fRnDx!qcMS{?Q@twQb$R14 z@vg#P%M(B!Gw5>M8*zE)^Hkxqj7GMQ#0qNuvi2ek1oE1chYF) zw%D~l#|-5mv#)puxt7>hzbdGD!cvP;ZY}Cdp$fzw8k<{)u1JS@^96x#l^c<*W{!D= zcbPl2R?hx?-<#)2&RYNmd)_>G>054OWLGyr0J@*MMaJ-psLqRnSRHYfU#(dTZ==pJFi4pi){LaDX>LQIoR-= zKFHtJq#Qaxm(#aul&KN#RNGZ~zfA^bNZhLYIpxz@1LU>Xb{F+%DCWQ2LzB6khs-#K zT64_J`o#r@aI?u=uV15uRD4W7dw^l7n(bG+)loVkzb+Rd#Onx5?m>NZj1?12$RO zrYJoe<4`-3qp6uXe~N@uazggk*heerki0Ry%>#>cK>m`M%sPqf>Lu^7-9}TONWg!D zoV|k8xoPb*zgJyM#dn@n+HMa2@18rD_*)V_)y5ZTHUbC@W`vX9Z{is&;KUqLIetsb>S!QwpZx{!~x{Z4CO=b(%L=?U{pBp@82*nkQ&|S5^!c-~rie zvONfWE-=U1k!wYE3IMn8K|{1I7yA19z$b`CkmVP8I5nv=Ixrx|aiPjeZBF`DVf~@B zJLiCG)~(2*xKSNd3izbBF)CC9B16gOLaT}2XNT8eCLd}aI!%xsfkDuGxEMGiow@8~ z)#dtN=l>bTT+W*Ru+rjHyG;gfAaQv0dvaPQXqK@8==W1X_>k0Sbw|Yl-EauC+$9hn z^_%Ri6E|=+66Qt@y(_)cgh>=aIAZRyQQ=l;Zmy&%%C9sR@nCT)MfN&@#`FBaYz8cD_V&m{b68T>KdQ$5KsI)P5& z${$J9gkqSxlBG2vF~Jj5XbZlzy{5*0U*)I%1o}*KYVgp@09gXLVa*gj$7Da9q%?gm zfADvHqzexA;9P#1z&5&4@yIMM^!fX;+x}Qw;VH*!Z)&0${i8AsUg=i5*WM&x0XK3q z%~nqS&6+ibzjhKVRkf1LrmJfTVk5m||2NO05uy>*72zS&OCil9iU@oExRUjCrS_FX zo^5(HrV0sVh-EVf3WdqY15XzeEUl{vfQ@3(a+C`4AU`6^rTHEk3yrk&Y6O`9PDfPZ znIL)Po5NI_OcN5DqILrlhpwx|vuI#zwSLvrdekS?I@0!WYqbu@)|!>O8eFxgm#e;+ z4~qO=))EdOoH|hk)F&cds{Bc|azQnH?^8a>jr;{*9QWz9feI8;5o)V87{6*a?zSv26Mc0tdsqRxpsV% z&7UXgFg@6`&d$yrTh_B)x&g`lYbJfx`EJivYHNI$gM{lDXAbL|%E$JfyLxZkX1(-p zZnml|u>EqX&0;&cLg>(=M93lnlqu|}Jeh@zD|%2A%Y1)11UoeiiWiDVvne(Bn-^;^ z1YAb@seS7*fN)~KgC#0GmeOP@=2>ZxOZAsl>PnW@g?L?8V}JW6SbnQv$cV2{3a21V z4SVn3aexZqO;Pni)4OhWG<{2k3yu;oD3-kO$w9*)`N9 zC~IrWzMITDHAeg!YUYFXs#Tr-EKE%Qv{x%8DRlI;49*~zYNB^1@~DFUO{zY{yLO2c zC6-N07cwsYZs_{1KogA2k@2qmh~V6{LkyioZi?8pM*`ykld6k@nr5 zt8=;Ab5k7N^1JZw?zQ)vFTDK1_u712&dFZF1>j3?Ca~xcY=-|t2fExb%B?WU;D8oI z0Bgq+6u0u_9Xtv*>;kHAuCVyxf%mjm+-auCyx+b$@p6y-nn$0W0h@}K@#k-U7jlw6X$Z4&mYTLyCg&2mAs^2OCNv zWjN=u^?c9HIR=~6i-YkLKd3a={DB^NI-LG1SS&%};3y8{qh%|CHz2(M$Hvc(R{s;( zUJ{X)w4dIICLTva3R@cbZj}_1Gw0cM_D~HJCg4~JJqXE?fm`|N#Svl%1mkkdfdin-TMwlcgbCZneDL@ow7V|rj#w8LpFM;CD2Om4JH6KF>? zRE07p=0Kuo3IPl#+M|g~2I(&Lv+AdNhmu+R;c}_GTmQ^a(BWx~*_;Btx>3K0vBhkF zGEa~P0?`0axnQ;xg=uY~{)@KoQn`5FNrNAHa>>+Pz2PT^rtV~QYpd7BUSliGY_Ckj z;5kzM-rr^)s>1mh*Q|~E!_s&|cTk}*MQA+4m!L$uB$nozq(Lu)s~?I6>sLpsR|Ic7 z(VEMR@WdSA<;Nz24=-o+_TLT;rRWj*6Mx9-z1H_~xes%>XOS%YZ&s>#u7ljE5u4q{ zx`X@t8-2b*pYL~{-#Wy$^;eq=PLnuFaT}lWcoZpMNTu*53lb~v?H*fL*}<5L@BUl1;|^!2o5Js3uTL= z$b;%FM~ff7(cNviA%=Ij>;I;MEu|~V_5X!-zyI+~0bIiSoJ{<~oe8GPj~u6((?7@N z2OFcE%w+LeP~V^B$U{SjS*@H&zic43^u-`YO6Zvxm}NwAdPiN zM@bhXxW61I(cxp91q21@SMR`W2Kv=Qxx=1@LqHDn{6@ceVka2mrOqtM94`B6FqIeo zj3iz`;o5z~EsA^KJqppCEaTW^4^Cm)%(oGgqv1ev*vN&@ik!?_8K_P2(+Pe$)=xb@ zt?|<;KP~suQl&7D2xaY&GHvR2qry0rYzbq}GCiA8ubt&T4uKI5*3eaR*~5Jx@aZF3 zxXlqc91?q(^`@XPPD~%&KY!(zgZRlpu!#H8Lu%jh-OunSzWYeJq;isfPfVZ4Tm8Ce z)CoSu*36pDqlI4fv~c@3z;(_&sjEG+2L-^maxJwTc+NW{*mnwH?+yvq{UCQpSm?I` zck9j{WAbWzm=aW7ZvLhRzvHEpP{;NDH@PYgcU7wGfn?Bd&LOpH%k??`Z&p(;oAw#i zs9YZ=nOfV2{(nBBXP`&wl|Rq}dCm=#b6{S!fqB_BFJ|lqh&UgkzWaq=(_4FNx@>k1 zBJ(Jgm#2+98qFAT=zvv6nmu(pi2Wqj<|OkYa>M5g$i4DRlYIrSrt856;wwB`AohQL zLeId)XR}6l$n2-?Vh`a!U9DHPuWdKmz6#X0@lu_8pER$}gZhPVWKRe)bg*-c1iagsLHMe?tnam|G+E!|p$~*t=#}m3 zIrKnff$-O;MxFbiU!4z>{~@nlSNeTEBHL#lldastT^Wk5kMfN%R_ox6lBSA2#WNC$ zQo1w2oay>iV@~la9<3L?=W{3Po3A%i1lcl#fr*~5ZG*Y)#tHb z+{G{arC2c9z0K(PgP-7TDE5*#zP0k(HNYEAE`Dvyu7W%~@z4aE=OH8H(1Pvx z@ev%4&d0YfM`9cb<7wEs7ABGaH(W>x#Jirmoz_QdBzYd*Be>YdwLc<~$Pm12KN>yY z<;KB^jEg!u;jW*~43KA%10F^fbUwT}?lz3&@{LwXvV=Y+_XhKwc7TrlQO37@#S@ff zc$vb;0UdH~(XxR2!N1fMZ`EtyIF9fi#_nYn@wVLV+yGu3iVc@?!qwIXAXlKyOKR+9 z>l)ST?!ZSnilJxm<Sr3$_Ew%#;~cEz=W??jO>mG!;rC%xhvP8_ zcx<-daL(U^@YYGiYjWhAYyjN?KF$Yx95d%x(~EZVOMSt0 z(M{t!5YlLT98^Y@c-^guHhjhcr5OWp5qnrGuJV=}Z%~_lw7nOc(2ACXw>*vCl=$j^ zH>iU-AZIvu*i)H7%F{`nC*DI{^~6x);P--OEDs*JIC#U4s2|N{%mQ%N`cdK3;#uP{ zWn|JKEia+%lTrv!^BQ~FL-vFIq?31g7<>OgMWN9ej7`v`K;wMarj~v z`HU)?yb5*f@U_7s+q_`ix^VUSaIkKDH1UpB0}?013m4*)>RVt(%WDobHu|Vh7(L)) zbl!cOXatQ}BCX3>vgNe5RkjnEIC@r>{GK2I$33mvXrg3pST^mQ6CvJD98vXnIaCS)c1W65kU{$XhP&hi0@p>q7g;_17mF9Q%f z)}VQBU#CqW@}X+}C({?l`X5hx7h5yBn`!Ga-}j>`;7s25lj3ABe3o5O8Oc8%~Zfc2Al@GuS?JOl#r%Nbbdb>o!b<<|RafTLIT zq|V$>0{?S-agBBK6kl4yqK*V;ia}l3;U!P&j0Dz30?Q-GktA5Nv|;M4;jD@zPc-yY z)Fbe0IPfupTHz(nUKuGI-w_F{pj*+vGvUB9kg{;#g>c)_U84b9Y`5-BVY<6CS^+p7 zM%Y9&gS*-z0gQm!hlT?mh679Fv@dz~#z^7Wt0Rm(@LV+TY&6g&e;rWZ46opu0~;tn{!zH%Dfi-aVv!#esd$|j81KnZ1)sf& zN9n?f2;B?38?AVbj88_eM`ZT+6|Q(R??t3yIVB&HSH23Ilivj(v34=4!>d@_>jja+ z?^=>4`Le4&ydZ}|@vcErYrW*D!!}~+aCFVAbzbm~tzI$+38Xt6-2bSn{PHT+xfi$< zR7)NFYV|5Q(@D)`ylcCu$0mcAaN!#@SwHNfTzP+RlwZDO`OL}Qf|F`yA2s}vmwt-x z^u`kuEOB`{c;6nu`*sXs5mw|4TFz#6JOSoj?iYB5!E|^FPru;jW7}7RF=D#w84cM% zi3WJl=1vvfg6w+X-M|Rk3P{Ts7iL?dFU!LV53P9Y*0VZ(y9C&tI&5v$R;q0`FR<1c z5WTsd2UA*5a9p5v5hd1ngPs8m<^DC-i3k91TYR_|=oF9^<^UVKi;m?1kACnX3p{=; z?m5ZpssMW?mhrH);nc22hZkf!Mx&ix;U1CHKK)o(J4zX%ZRy@#;pynSF9!*$!Am>- zwgaMpbzo>1H;sj74P)&r_6qBVpC#h8YNy*S54e0^+hH+m;+4%dWt> zNZ?r(Q4fgJ;|M}x&2s=uVBLw5SiFePOs~OM#=RHGLc3>zJJz9=KOY%Ad(>b8viws3 zHGAG8*5G)qk#(5rZ$AE$ERD&-)>ss5K1gH{=R53t?c24f1Uv*H{Mg&k%Hj_j=s z1(tccn{B;zK7J{pFf*6I3tcuBsh_PpK66e%<4%5MuBPoG2SuAA!=v+p%^24*k^!UR z$9158#u1EPwoWKtuz53G$Af;fIPuu^uXJe~%QF|&CLX=8HvV$p!urJcXV3$gSdw_O z?ad;bC`D7TodH)zI5qw&jH7jVI{wmi)=R0bFp&CNuI&w^;P80%cA!mcQ%J!$BHS8O zKNf76MJsaDl^3NWbYVlw6PS62&~oiU9fYYEk5V6Z26D6C7Mp+*9=KU<47gu_8>zCa zWzHvhYxRmolQ~Akpyba*6U)pn=Ig@xx0-|625(r-7e(_Bo^Xwns3 zD;h$`T)Lgh_mGq*Fh|XE`92qRb~SA39MV=&_|c7W`Of}^XVaX^_Yn5uy4{e3u56R# z$T#+vU*6yi+aYjxs?Hk_$(vTb{XE%X(RwVTQD(lt1HKi0DMn{v+H zb&!j{v>I>kLF?@up})Mn&%w>59KOHGj%mB2qXeQsB+z(kG>d2^(eMYWlk}o}$Rt>*1ac z-w(-=<)kp2QinRRRMz2P`Eo{WCNZ*#w1c!f%hZMw$}%z(#nD-J!yk|!q2jg{HaZx; zo6SEn^)o2l&{Rn{fo={D#d@G|+)K;dE~+`<1e~vHoofo)I)^&PQqwx$2hgzdPY=wH z9LHOkyN9rlWEN#W=l~XbCyaOc5JR6Yu=D^40KJ1N&EO3ui?7B-?^SRYpo8z$`qIxZ z0%Xx{1U$GAh$Ti(qSpvK8xgdjgETh+3{RMX!eqlAx?3|!VD_tf&~X#`9YjGF8%EDJ zXYu)EUd#9WC6Jm1+>s_$23w*i>&X3Y*+z%8&j0;^yiKn~GmOp2YhKP#^%P}NGHj`{A>h%ppXrh01Ay<%{-Yb4mmK8$3lTT zSs(FO#ZQ2N;!d7ty&rtJ?N}(F#b}pD$j*N1!qu?88hH*lF=W!KF(nIr_j1r8 zy#~D@APrgso9~r->T6MC4E9iZg!6MS6SL-jKH{A9DJ#xFi$!Un#iGnYE4Sc20a$%R z)#|yUb-h2N??T%#%+!Zdr`OowJCN={UL!1_JO-VUR2TJx6DuKC zeexX&m6{B&^nVv;%LSxA=Kq!aZ_ihGH%X<|st*g451`g)#9@=^JpHpS5sv2^{a@bv zNU&K(MxO#qP=*y~$rlSS=N_jw_1YHwSU~*+X-NYVKOopV-?k^$-okb3>Kk0g8>E$p zaFRz;&^6fL)2xOblI1*v3)jKO zPP$&D(f$8)Nd8+*W`*f{mD+d-yu6C+3oDxpJ6+5xczTz$hGly z8E~K2tQ0-404+j)jg8HNy@83Zb|d!R8K45gOlLCQWb@ zb$+(Vkrpd3@b?ixQJ1G9)HTWXNZ-l*MPFMDfMOh~j727)SCk98SiN zlG@El1AY3-#|$>Exw%cR@v<)y^FJY?2a2>gdx0&Mt_>ohFc}~Qrq$=wcs3{j@vS$R z-^i#nRX527wcv`K(4g9+;s4D4*B0aYGh;$@DEf zkp_snVrR)RL^h_!X51WWqV5iGK5*vb+%UH{{F!bSu;W`_XNsjFyuZWF*eCB9Tup`( z%b1#DG+oDNx{lFw9i!hSrt26@*D*|2mz%C*dZz2d{OLL-J6*@*PnW8ovSjqn z-EE2JpTjAv9<)Rm!jD^tBHehYUfH7tuUd{}5(#N?rVvzsx5Ix|c>2+Lct*_A@4E{9^D{ccJ~?N-1q%NyWqbg=eGLgS#RpH8BdIV0d<|P zc?jxJY=6N-IYH};jZGo0G+M2-6ZxJqBP>S16~1o%aVy;*q=s3f2TpWZY4Z}Dy7l3= z`hZ48Y>v`)bLa&uQ7IIvHf@};BH?B_e558zzx7+cxo0HDZ zYNWzNYhYe&)_^+^eKlD(sW#r#-MB-2_1c*`)hDJ+$`qj+#M{KuK6Y!2M*5zbgICRa z%mGM;aY#qw0rHt4n0kq&=2Z2v8t@cbC^zWmVNF+GQ4r%W-v1j12fz;0yhc=U^|ojv z6K3zy%(?V-Y26i2AuL!@3f z+<`g;Z$O6w^@r6cB0Z~pU0%bH)y!+$C}-TX{*+mbW~ZewfBxW~I=2xT+2rB)#?hO-Vw9pf|Bnwj2mwyj?bXG;Qmxk!O z`5K}x3&K`Iw11C+=;!X&y3$_#Qa!!)i)Eb9xJQ@pbb0!oK=Vo{F^>z3aVvSjL&=(Gd5g3#CE+Sj0I7+m zO&#@iS1Iyor)_(Ezd&r@g>u*|-LMl}1t-`FB&zA=x;6LDjy>HdHNV@0{+K+GunbDC$#)H|li z<(?$qAFk*2x*3AGmx|9)(jKQ%_8*UWqkmo^-lQZIpR1%jK95BIc${k=Y#w+Py=l$9 zX^$`Hd7PhpOvO8SB;U&Hn|Av~zi6mKi8ac|9ic!VV&w4$VNtt$4@t&hzVm&phBwSq zQcO%c&Bc0P37l&`bejqdkEQU+nf@L+F|9E7!hjq!<>AVTPgqr^$%!m(8`4A{qN%?P z(fBjRV{wVn34yNE>UOsB4W`kyTE`-8l}hRoi7*g$e*i9mMtSkBQ1^^I5Ki7UM9_(* z{xD>&DjkJuyO5Ns_hc6?H-{|K>hCuHozj@P&F-JQ_Dt;0s$Jj>C!2=|;ys!V$6{lm zuA}*IZ7JTUXz6F%3Z$;(D1T*%x!_jI)0sPHHdlB_H2LQtngu~XQi^bc*m+)~7hEAsGSk23o;-7z|5R?y^Pfu089ad?AQRkZ7Map@ycKh!$-Il_ z+?4F)-gcXx@K&suq))3%l3(Gzef_iT!rv^^&6+)34m&!+z?)tB_IO0D#}A_h$@EaS zM&Oxyd;KmqzwPzA#N3kmEmXgeUx1*)oMs2nZi>#xdObM=;h!+uOH7~PYYgVerzcqU z>0@lcJ5dN5nDf$xU`NvzY%UYu;)Fr-QOVcDutXbL{Zv@R^5-@RkI>Q7Z}lCfZJs*y zGQ(#9L{s-_8s!nVbsjR@KihP)_Cy_l+zn#QTtr-s}%uG>1>;( z_=e&g)iP1#X|yK@y9+|V5Y5FlddV9#N#5`qTS-Kc%DuRKRfKDp>)Yq+ zV=A`MYyu2ak{^}KR)K=?L_bbeOtf%AJ%ye13Qy< znv8o$T@RDiRic-=d9M24wAvGkC#E+9dvdr|Zx#mS<*3{xU+*b<`bI5&GnkCb@x^fb zohC2&qgg6;{Suov51O(U4~M`3ZcY^x`+vwGp&2nI{{l0y`1+TflI5G|RxY_P{)#x4 zv5GZ0OwBJ}b9bO7gd%eT9&RwNnc}5p;EZcJhil$VFTSwldGgOFj)kSp-FzdJ*IQeg zoT&|-%3J%PEm^zV)W%mk)yzoQVjb7c;%FYcuS$xlXj67Cu6`V)$({7&1XBdipr+Li z()9wf4+8>!Ko{-S*V0UTU5Qv5ooo1iC1+AlTNdKxNJ?n1jIop{>5(k>dV&v8TGOIB zvpZnEK;iUO+@W@|2v^gx#n2bXnG*rLDDI{823vv-+BmA5Eio!IAeVu%XXG=Y|Doj)cwg|bdsNpQWSS1MHn@d7;*O}?+3eZLb$6Uw8+ z>+6Od-|sM8oLzt3#9+eLFJ&l}G7ZW^Ey=?jKx$fPSZ7+y#xR?~^Tnv5qKxG?haE_7 zlew+E4Tr{VU&9Z>m}0MbPtIns_aoKoV)xk2x%0fk_0)}ap;fQN8YJnSlegN1DZF1g zF+KXAY;+GVd9!*KN!{pr*G5o4bo`Sil(yHnSx%miWiJ*Jh}z#!Qq;4#Z z;oIO=OX;p}pG%GSMopd&@{&`^38GgOsa_^ZGP0Oj`N(=*ip!04P?FD5y|Dl82tXcQ zK@^_a()f&$f>{5=*phC#RGaNy4~XD?xL)$;9z?!F-D``ryV+yT@qf091Og@Ij+TOg zWR;m}5rnudmH&9#Dt2Qlnt0T#;450*y=UHeb+_aZmDT}-+r;dL*w24rP)Lf!8lq-_ z9_!;cNNL<{X~T+;(}wFoAsg9tmo59n_IQ4%SD$LlWQSo z3gIi=8c(;(ov!5R_yC?>J!^S==p|+MsKC;L(`$s=2{QTHsi>hF2WQzu8Phl9`J(=ss*>@y!)%DA-w~Ao4qrQk7p;arWude{tVtoKDz}; z!)c0%1YXBwD=gv$6Kk@4Wft#3$;3?Jr{t|$nZkKy9UnDt@qWLy<=!yAB<(weZwyc+ zIV!#F0~i5^X9Zg}vSD#A!%fxfXz~G3%W$e?E{T_DvEjn2+k?%E`tdu}|N69&ea|Oh z1oMeI5wb9|oXN~436y^}QmtzUCV6U6QT*k5dO2_CKOg+D*c&9m2L=Vq6kn45?Axy9VfvMv`-r%%B}?`dMT=TT`9 zDKgq-$_=}%V_pLw=7bjl17 zvKMK)2l@7W^$jWqYDZ~2$tx|DxIaG&-*(|!-+Ab#dB`I4wS5Y_T(8PbEKXiw@%W{0LnGL7G&)7x zh=ovwoD~^9wJO*mKd}C`0m`j=UA#M688_|zevd{cKJ5sTI*=zekl_yqU4_SB>I(96 z5hN_dkcJ~5M}a1p!-SK!cYxgBxMQg~Q;8bvCu)F;Kb@cK{=sPy{;Tp9>M`wg*&%L$ z(@Yb7yoO$3v3eL|TZj;kmbFEmtFlSTY8$~e`N~20M4|-z^oc~Vkn@{l%mMsUkaLYW z?izs;%h1XzcVH3@<>wZY0PPp@{FQH6x^by3_J+CG=KstzLUS=c;3%vNk+3$ptcf^Z zP=!b$6HVSJ-fn&%I{Z#?biw9JExD*X|22A*>R(9`O) zs2)2cJoHKn(pRs}!+g;CI0}hSSe$R~^Kt*J)M#;DNaHrSfm((SYtbQPKyKA#?&HQ* zchOP?A)Xw!=j9_AtG@A@_E!`KsR9s0&kaZ|9p3Zw2LF*|go9%D3zZ zH0!aY3g(zw1fqq9xU z)Ws-cY>X*xVQ`k|r`7n)qG)m|_`fz%wi?}%LOj0B+zUhNh^!w;@oEw^3rc-I+O>7y-shY#7(Z%61ylY}C{$ZgQ`Yeor{!Nf3e@ufxK z#8eKXFHL|->|urhNn;p{%FNjTY@)}ivA6cj|3}wXW#j)Z*4O!N z82^d&^$7dw=UZR*O#AZI*BgWW!|Uq>1|zEc#jdXq!`Sxv`WLK!M@{Wz{cEkVB~?FY zTRzWxEs7xJBxvvPP$}iF&r3#6#i(h2 zuluB8`JR_bL^0%B5{B2NXqQ;o3s}jWDxQbD(stuMIER7M;vDQT{T$2(wtt_K@KPS% zB+-X34-!F!G9zJ4oJ=#M$-QJmj`qgrq6}Vnwx@9QMwB%hVV%DL+3%{qsRow{YjI0L zP;4(D|GlR{zmHa#QAHX(Id}IGe1l!VT@iU`u^SmTYSTBJRx)cWL5kNCQ*3R6c=UA) zij6ziY_yNV&9p4%A@db4Nsm$pg{!xxvoJp;D5BBO{x{W?G@gO7?%qr5N&-tql@LSx zPgcYz&)>oN_ndEZZ7_}7H(iAssQE{#+27E&Z$hXWY)Cb>pxlXNwM$2q1nksh zyEH&uYAjI>TN?Iprfv94;KzB6Vc&F8$*eIYcL!#Xdb)U+n7-+5`o`w#O7E?lOKW;+ zxKy8$ssRz>Rx8+(Hv9#X+d=KEQ?a{rfU$uc{m_INq&>v$gBOeE8@H=?B2(&|VqJW> z@NkJvVhK2?r1B%mdRg~TcTp;n0xJD#SAQ%7c<-z>he=POuCO{d<4^4V??|^lEWAxqIWWT zP$v(EU~QS%f%ke>Na{2<(Xq+il`pYWCl@E(+eqmBtbrpIDXFZ@~2+YeH%Dge98PgQQ}_@&NN$WYS-i zqU%)F)MB&zGJD6o3nlh^1P5~5lr9iw9IR_=Gy7Z23jVNl3%}Im7TAbixJp9zt_+LU z&4pIHD{ojMaE6c2_LrB6K^%y+STTY?xP4Udan#h8+aJuSp&wU`c+-CccjbS}GUoX; zXG1L{qASIjuSB*DivGi zJjuIlG1ZW>nG2lXoQD|&JYtU$s6P+c>y&MJ2)RZb`}Z}cecuZCdXCl77fIT*A5ggNj3y5sq7|dXc&J;9FI=K! z%Cg~7NdEmf8M8Prv`CxX0Q#Re9~gVd^hIF&+XcNaUhpN1YGbWco=u+U`2E~2hZ~)& zvW)h|l=s=+HmC4Ylg#=!tDH7!qE z+uyV^Z~`0`MawvhynXF1B6nO8TZ?c$MFN+i|HGani(YQ*pyp~6x-Y{!e)Co4JY!Ie zVym^LSPD8weq2noZIk;?r$(ZokqoC@!|%q)}eOkyHdGA4Cz0O-TR)%!JG zrn1%(sa1Hp%sTFuLgZS@Qi8thg70T8g}5#to_vS4%4Ce{-!xZKC7xdKMb-R0hVAnp zPOU>yLaXzhyI)G*KprVl$$fKTCG;gzrl0otpFR94QV3AOOYV$l%aqtA8cQUO*&jg& zxl62fA_N?g(7E2w+Yd8m=(A)NiK5IU;l%!!+ho3JD|bD~V~7>7g=IP}1dzIT;)?MLn32++%{_fS?M4%aiW!io%gPY)zS}d$=i2*U*^CUb zXYcPd_x}Qa^D1Wk+vIQB-N!G2zsap?yzVO0DTHtw5Lssi(FSV(M--@-VAc~(BCt=M z4qC1lP`d#~l(n)*!qMeTY23I1X^t$dmM#RaPW^Y9n@fQDZXf8lg_p<*~= zemz`$aH@PSgC}b2S&!i^D^QX;+re z@2{*}{X?YNG5uQ}B3-ZgMR?NpG8ada(~F{I?;tY$Dx%&(n9p!<zkdjf@GFEu?dsjwbCSe1wooqu~Da72%W=HZw0~Zw${CJUTxf>GD zZGNTI+Oq!IO}QYh0R;xxUJ_n z`ZY`XIH&K%k$9t5w#nPD9QQ7GF>90UwP^x8=!@&jt+dFcg*M64)yc-V6z#R3)q?rP zq=JI19NN^WBG?XC3MX)`v-*TR3j4iihTAW2Kg`sAEXMimU^90}5Ka2HHk|ZIIFXlO z26qzPAT;rU(=Z*206uVnIxpbLYd%hC8Wap_QiFniu4WgQ9}$C`Xsc-Dz#DA2nM&F2 zu01^hC*H@-jH|`vT=ur=Lg}G%(2Q(oMHnBG@tusM@Z^}RnPeyXx$aKs+{xr?1sN+^ zIgQ{GXF=?kXxSTRon9Ntb&Xj>kh_E~;eo>M$EMg0VP93tv8VYoS$ml(zLtsw)f0iL zZea?JaCa(>UZ-|_#_Sc+7wy7X?K%HjQ>V^pV>JoGccm?u=;XL^FLmrGUfb)1 zUSWm4JU!8mOPqhWMvM5l$JEoxXKgaL>hD~4VschsW-@{vxq#)E%5^)|6-gfa+qcB? z4jBSQGrQL^{N`sV)AIa`9jebRkL|`)#MsPf@y*@A79B15S(O=uduNHHP$Ep8!a)~T zw47fb%p1*zUiBtMliD$il1Ycuq*04{Gt=Kop4y>9MdMBk38ra-*>D!3s8f+*A^1VZ zEIBao9I>hx#T|?Soh@9rLa&fdT+1NB?ISIz zqud#z=b#Fh4s~Gq)1!jc;6wPe@Q>7DP6MpwJpPcU(B=8Q%QHxMByTZC^EpRNEF{NT zOVjtIWDN+z@#_S_`^d|E@nm`VJWt4%ni8@rt%#0sh7U!m4D=HL&J`i*MOg&@Pxq^pG9ob6x!~$Psexz)65zqE3l6f;xoqHb~DBOARP%&y9QDX;P`NKz4dhh^#EnKXzf0e$w#^#xby5ifL&Njzq z>#WoRE%&!s+s$7}x3D)2SwZ&%+zwA+x(N_?Zi#QSCPk>ztkTIUcehsC{S@QwFsr`N z6NkH}ulCbJd{Toywhg2IPJ_Amv8RLU-y-B`L2cq`cR=t`4`5wKT$cx01*v5JYtkAf z%R7-;U?-uq#!Ix=Jm@(rvi09SLtp%WlL1KNjVZNssa|cCX)EA ztD@UXqzW)Jk{oe>^6Q9~aE+&K=a5gmEhQvC7}C;G-1At}^Qe5v(B2Z_No%0bej;2k zS6EDgp5J+f#`M5kIs+u@Ih1sv))&0*7QLAt2z7d?dw;182OI94uN0*(Ny2WKryr>Y zGzx>#N463((N#Fhe!f?bGb0bs=agPaUO>1_p48Mo;bo~qAI!ljm&^xF4loV`OkKd$ zZ7UsS^H;8wsup*k*{<|+Sp-p2#WO+KuYX9>+H?M}_5z>M(ybHhh-^#$&~vi#hCVIL zaan9j%Xm(QXvHGE?XUSS-pLB1-MnzHQ;kSgp3tX`Q&ny=bEItdl8ya4Q>#CmqTEhC zSk)kdT`Jry;0GhM-TJDYla*^;C&>dqz-6)B8pd-v;a7i}?M1se(KSOga|QQ5o=%>( zd9n?W&a|R%exD`~J_=kKD-8;p09q~b*#rn9_joQ{?-I>M` zw|`4R++wZ%k&@Z9YFG1C*XrG;W!Gv<`M<=q`taTWM!Z))n)Ij5U#q8V{Fm42nfs`c z%D{urYM^OnVwB#YQkT5935b|S$J;EIJVci5{47IkF^4kWvEMDKKpiY%oVTxv@y^;>{^M+f^>)=(Eaw z9Dn50>>B!%rstB8uG*%b(Q~r$=2v<*{Y#g{Hob!9G>TgQ5AN6%`vvdZ5_#=_?6M!z zr;dp#_ks`EMPy@%^;!12tD5YxAHR_w2i%{xzAw_dp7s6a$u`T)tz_xvV`055=GOO5?e|{m z+uA~IV}0*|mSYyP&QZ}%S!Baq^{t`}SJ?cOhpnbq-X?aO%VP0oD$k#?zSCo~i{(?A zZY5)OA+6Ulb^)t;H{Is4*rqq|+;@FH$UC=0`tO%r-N|DxY|;a z7Ov)obv+AmSyYo|A(DfJ8+Ymf9Tm(U$&kgpSM;2$ybaXNUyXOVEEWk~<#`)C^(Qtn zGOw9yTr-t@ni-YX%uJrsxbX22&C-en-Z{if?v-76>z?o3)k2l)_G#MrWYf;tpg(1H zn`3tV7NHuu@_Y#V4==J1cuCL6%AfYB_75(Lg}}=^=a}k%?Mb`Ygj)``kBWMm3Ci;p zPwt&%ed_gYrSQ8CQ+*j9+_>)XYe`n_*{1_Tb9rvgwPbs9Dj&2c7mH>vnT8X&?DcXp2*JDDRZL_HDaGx-KNgfrj zQjkw>uW4)T4cc~l?~HcsEwb%0e!o2@b3}bWcVQ;d#wgnBLLi2)*NG+_9Eeh+o4QR< zoxeY;$*)>pTNH>9rtah-Y@~6SkUY6$eQiju;%yBDz``;m){En=pR*iqoxR?PGjcmapZRyk2{k5zbZ|E;&}>uLOFBdGiCwvGsEjJXKv zsfKc*r&kGCNXu=SXeecw^|+`qgT)knVnlSHiJUmuf8q+@WH+-Nxa6CH;I;;#?-Z+5 zlI%ZBUnR@ctwf5@-}`u4(Qla3NFhZ*`yBxXi|SzesV7p*>HOxNP`OKabVTHG?&>u8N827mw)WVe4^z(gUmP_8wJxi#@k{TfU75^VV{FKgGg7R5@U#L^ggT50B>^&@Kn zF`RpzSfhP!r%3X&5Ok&~P_x~I=8A*Ob9fmg9I?9wZ67s*-!S;2rugY(Kb_>K6a18O zh|LEPv1yH;TFOm%OSwr){r4e1E%DQ0KP__UK$qOuL<(mNy(4rpoyEu7$l^$#)58sY zBynO$okscfJmMFQ<1MrKR`EzT>hHNr zS(^)Wzj9n|Pmr!7I>VzTa1Cs)E7p&iR3yFi)46Axj&}VreLjie%P=mRR*15!A5Tm* zM3SdZ2v;w5HDJI*Buj4lPA{=Fg{wQK9Vd6Hhf!y7Y$tSYM|al*?^{-1{YdN}Vy#Bx zygP9^U_CulGwbNYS`^lZ!0NxrdZ)|X!0JA}mUzyI&A2?4pVnaWS2!c!(TqEyOUole z(MeC%RfVe`mcy_y6HxD%;HAb*vPO!vf77I{ubTwM*|T}Fj@gsr@H&Q$g_z>WqXJg3 z-rQJ&%h26{n}^G*^#cQq;QKHy{(6_!y$siaXYm@7CxlZmY`S8oq9Mmdp(}U- z?C96MMD_Z9MU4lpuVWFixW?E;$l@9^HXOic5oP&=obO$YSCxkq_i$Qy!>m|AL-4-W z2~!P|+w1;bs^>M6v6KdUt9pM_yp48qETb~D{K>TCGl$t-INx^T@NHR-5YgS7^>v`* zvkDzNJN0_)f&9qd0~IS&9(6tnx7QD`Yf%q&F^1c>8v^(tZ|_CQHcOYLaUUqS4*wV9 ziIYd}Fj26O)}JzFcY`GyLqzGn_BEY?R+)Kf(*Kv+seDe8#&s1-#G1y@+_Pe@dH9fR z=$OdLdlZnNN)+Ct_kGmy(0Yy$DhQX0x+)b^zaCp?&lY{Odh_v@HAM-=+Bq-aAF!GLD{qE_ zCp@Fe^8KurhK6R{*G*%2<0R=wV1O3;c3173=^w5BU>dk=9R~hBa{)gAB4wXe+5N-a z4^fg@tqVu+84pX0Q-z6fcO)%MLp#zcGzMEHi$Y2VJgZN*GPA!8N0~St!kIzy*M8Gh z;WAn3gSkX+9sisT9C{Vf(Uy3vz}z-MutBDNbx1CzK0Z#+hLLTr^J#q@vq6^jI!o)r z3CvKZ7vWyG7*!6irbjQ%-z&dPh1x4`CC$e{C`zceg#~gVc5&QU^;lTop?YSkI{{;_ zPP1Dada{NVH}N%VXwh@z>4kaH75k6lz+nvgbK}6t670}%HIHK}IKa7{!zx)Bx! zHhsvToA6qQ;{k(mSnh3&eK9XD_;SJK+HCv;gd1l7h)G zo`NAK*xVAR2qng`0gMUFD{j0Rx_^A_`gZuW@bWcdQuW6JpSlTl8_Vp)7RgRjH#QR3 zsJCoNHM#vvy+~ABFB*3D3g_)!xa)wETNe&++#6HQ|D1xSRk4W{Hf|q2uifeA;qMP! zFXxP`FRr6+Eh?``U=dmAEloAeY(@3uZiXykl&mov>qxIDEUiy|tFS29Tqv$3zOg$N zWlP33#W|S01H|B1@M=4W{J;Ic{$`uSBU4C~CEB}}&uaLK{dgel1Q#{@r8dz#M72Q- z7eoVHwb#EkpI^1{k3p*s2{-&qeQ;5S-A)K1u-a^10bN65dk?>71cWY3HWr5B%ley_ z)Se`~|D^{@(DL_KXia7>{UHptF7G?d&$9(C;6ZSHTrU_hTX5kFW19JC{=uKj-5{`8 zqOV}@I-9neU-#EG)^2`AYIiX~*J4j{=V5Z7ZG(OQwSq17zbk>-3VPqxMAPw`n_!B8fB+8=O56 zq{TT1q9`(*e+TuXDlfIk;3XsrEBR_Jqc3@M1Xq;sVdV-VSO6pV-awTaQ>9QK{MJ<6 z0RO_P>bzIe+^et2$CIC{Rj<(Eho^JJb}sDdzNv1{V*|75LUugP_9l4W6BE-zvwVFI z?MTK#^*7CAA5l!LW4r*2(6}O2XK^?HDigqnjT0&Yop=$bnKV-8`dL>`X6;VNcbwcF zpF@}QkGEPQ`p4re_oT_SY>H4% zo_-|yy5Z_&Q}=_KLJ_Z?;w6Jj{QI1b@g82$+I?~jbduxVTF1AhMtlztkmDws4E`m_ zLgXF=<`fVlOYJNKYVQj)U0qWUyCeeiZ>pPs3Bm5c`#R#A&W|0gRXveXShUt;MmAkN zxu6jy+Cmq4JcA}D`L3=ryAF9LcwcLL<4&>dRq86R1|JiwpO_Thy6ZeO()f!^A^okN zg&)gp(q6dIk87J|)}W`l3$=g{4HM!U&kwfr=NDIH5WqF&F?S=ufvRbELE{iLc@`t% zH}N@26rUp&RO=gLlhEDZVVA-{{c3*?;`da08^2VguA-W%An>^HVI{W+ox;iSlZfvQ zxc7&GVD`~Hu2>ew7uYi0W{zLI4tB1~n^h`xyUEcz=-+P3}$vo`;@Gt?uXj)fF^o2j#knRn3;lvE-y;t=p2OAaSZ-6WcZ?(_R|$t2HJ^$0KDvh*U>6nV z;?!VIyx6>4PTR?G3||8F+U}=`)M>Zbohz98W}GdHo|zcodxZZuChBK>67?C$8+Hhl zcl^?hM86i6g9YYy)M5`4Y}_&@Q}=GR@7hgm*T;Es+3YBL*3DB?;3GJgwv=DEmZPI< z%#WBSYFz~TW^3GyVTo_xc72uEOFsfDi7V9?0QMw7LFn>glRiodLyH2Lc1_5gX}deM zF3?5ymuR=ShPXy{=_T!I2oJwqQ`+dSClxK%W`gk$I^eGOL+X{zr7mymDbPkM$*X== zQETczn-1@*1%wWV2nfGmJ-p_yRFEq49c0hg<n7c5w_=#MXErZ!zyT+nzR=!*T_apAV_KKuVlfNx;)CKR`ac7(5(Qx&bb@QxqQ z(Sq(roC4pIFMAc8kb!VuT_m9Zcyt-brR8jrdmxabi@0t;HdnS-5VWRdUV294dm3x)QL<0nSbNi{#aePtgahzrql#d`vh{x|6fUg2f6b!I zcWf&MyD^>s6e5X77$WkFJ!=P9Zm#=L++nYby_q@Gb_Gc?U?2;JntcY@Jc6$BOihcH zJr6hn)At4(d-QJKWebidooi(H= zQf5uKyhXV+61?k0v7XgWUWvH!4R@?D9}gA5mnh1WM35CJbM{-oyOw%?9q<12hxM3t zv=?Z_*i>!>qDf38qlu5BWgmpwJ}!&|?|KK{(=Dw`(gIB_5{Ay$o|&(1C7(V)*q^r9 zQpeTcZdKk*u*uR{MoTH zlKNI>NwjQ3uA@)Ov}pq-ptvYA1l#_wpBGr{_L6;;%7h3fYskQyJF9lHl17QV7EbIl zB$|-p2nC&%sp;;}a>tou`SSGN09Ef-Tb>-ab?(2Y6QW40>L~t3AsuJwPyLV*Zr$-j zFMlYdk>pU>+Y?%1X1TfGQpp3?nAZ-|_}7^6G~)_9uEJxHwOJqX)Y7-hKe)zjVyd=_LfOnAm)T6^v#8byswkn$!psV-x{2u_Z1|Qpw4A8f zjh}qAx2N~2B6oZGd)ws=rXS_Ax$%h4Z2H@5?4+g0ei7_W(yW<91fqjmoB$i|G3^Gf zvD_P8SJgN;lHd$3%l*3YOfhhR#PgXk9#G^fJUBBq?f17a`c&m`n+*Oh635QQ>5S#* z5l|7DZB$KsV<2|&QpBE|mpqGYE$exlWFOl{O;E~SVj}>Iq93E^hnMZgsB$Fv@pkoN zqq&V^lGVDv^*=qRay|I#&(x8@Bh-Vj)MWzM9{4grC!1Sg#k5`F__bqUJMJXogX)dB zs!X#;eTriAx_Oy*G}fU(FDn%)AR2G4tKl^RaAHDIwtxGZ95xNvRF)&Z<&{0sw}vp& zDstcTm)SZS8OTg{$C2gX;Ug`$s|4;acRv}9sY^A?RvqvEG+;}`nAC`G+mTjw{FD|_ zgY|3SS=4Ka8A6`8pu-$s`>@LVfYhoesm@pJ%Xaf$AAFqCr_*^u4w>5J(o(fv9U#OJ zGG8u0vH_5$P!6nAw29-B4X?tQ4EqG5sLAw&)}Uo#OvWCnU)|;@btD^OZ@PV%f8fZS z0CSf7x|@k4&l`(zX|VYRbd9snSi2lSRBzfo@(NmR?!L&%@J)rS5iiT6nGon)LatCF z(gF^_btOfZ4W;BiqRU(iV8iVLHCU0Cp`icDPe%Wh!h{3i#~tSN+z_Nj6xfNVton(a zk1mph7gDilq8p%;=t0mvt}+*q_8DcUR$dsgW`N17p)lM-C8>Z8O@N38n{Vewr24!` z?A5GWp=aSQvP<_ot!0Z(J^m6KIth5kct%yRjA|MW;rv1Ov0d2ud4ynhRUx~+%7u95 ztgT|58(!rp@QPqviAH&)M)}i2^kT%GcJ!6UxX;DiCv3} z;MtC=X-8$tQg(P{Y*bA*r9eqJ94Mf5*8Oa=j(3^yl$p2nGLP|H2MHBw<3Qq;X}5XM ze{bfyhk51>)g-Swl2Fd&M;K>Wnz&PbCNn7ZEidop0wl>=w6&UVW$QRkb>s})#_KgG zc(6K)U}(iU|D-X_yZ)_}fQmC+S+J~SETasTr6$?r7I|0>mQ8F5mR&GASa#XmU>W|E z%QS+NXTi}@L*gY;RW`Y$TuE}@?eC$HRzQ5Fn`Acq9PQ3L`>OfUh2fG03)k`0L_*|@t20YNzg zC4?YVR4VF^QmSaFQhy$(q9urc6cMaNM2dLA1gu4>5E04$zMpq?vpFDY?e|^R_x6v?oM)byd1l^sCxGM%$H&hShVY+wRNuk!T{Z1EP8;TLWw~&oSG^d8mlY}GKLOx0 z0eVjH4zx^9{zE5;{o)nUR9K>Mi+iQxG3;89;~w!l6~^6CVcWE;3h~Lom9lqoqhXT} zeM$`fwOjNy{)CR$53a&|DM1#u4`G_N=Kht*kZie0>9ecj|msQ znRg^#t-ngNZh&18$}gy*Sv@^8H`T54oGl(S>#YZ{JPz0CGXkZG;|+`SARAQ{C>wdAXe3 z;FV*uErp9+pbZD^riy0zp?61En}nax9$$C$;RxPc zc9nH^*{7E`-yV+OO~ttX_{2=!T^82#?y`_u(HvMM_i%io|IOWHI1K$Px#}}VbqN|> z#G~qC2SlWU9Pm|r;z-fBDb2kHV+M~>JK&$lkK^{I14dA5Og$JA=^fx8LS;e+CsNpf zg<&K0o$hxxZLq_bU3j3j@T0@r3pEg+YE-kC25Yi@_Dm`NJEqTL?WzA@J8oV7vrlI9 zBT%6?Cs!ubT9Tf09EckuP93{g^fBDjaI|_oLh^G1TswsK|LoG#=r5CT1i_0!7S=l2 z+t&{b8$ejO;6`#4J;nuR}y*R;aC(jP?Mjzf}cJ=Lbb@6;tJ_4st@pY52 zqTyDH4gQu12>V-i?OSlazAYrhH9gTIF4l z@pW4nGX?iD;Wllk7rvX0b>jbwNU3O(Qr;>mr97f8aaZclk5k$te1KOUd$3wCSPD1O zY@J{+ia$@OOyCvvJmS5pmRSb<{QpcFdZuI@gpO17RlHiqDm(tA8}h;qnRz9b+mYA8 zxw}+xTzxkT_SMxb#{Zi`epD4qH+I2Oo;&YA+n3=hwwo}C5Z0@x?V|&G<@URwbDZ+Ww!cL9LX*SBu?ZAfJEY7F+M;8Xj<$y$|K5 zO_>YivHnou-ax|1Sz+^&^$vV31fhWXfV;v?%b~znhH=kRMN9N&a=8s-BJ{;5<;H5P zh8{InL4;B&OYpxNra;X{pfpGuZ^jFWUGu2N3M2tU^cn?tu8;wy;7;^${7*#a8(gO? za@#22zmO8XKhMNyqNOVJzp%?--+?tpM8P+$!S`*+8{tcXN$WM-_OhS9gqj(T-z`A%M z@*Ho_lhexIOfLVcwGBoBwl)j+&oqkXqBjp?D#U3_T{|8bN6I*Zjv!)>_qdqYWR&3_ z{$lY2yHR#x*tTLygq{upJ!h^|ODP|R!H90en87Jd2M{|*;LM1dkyzk-&glYr6@j`Ek=Z7+~aL$A>txiT!L?rm1em+78 zF==8tc(3}59mQ)<9()#NWt39>rpc>%2wICtM}LE|=?o7`OJ&1p@;~zD)G7hhe=WdLzyA&fic5Fq$fDX80vI7Zp zXky#36=4I$R-_I0ay)g1s9Ez10c*b_4%cjFfZH z&5(Okc1Xznr*TC);6hc1sQmr~AM|~PvC*dNebkTQ>c>?z-|FIg+&zSnQuGpyL!y`zF%e0MF+fT2*v-f>@~(WG z+hHimXM$CzLC70`nw{7(o9O2%kjWdVOWsI5katfM#n~{y*YH|2rRsA?`@xjGXXGC^ zeCWWa!#`W~{{nKx1s~UOZGn#w9}Lp!3$Z6njs6(oy(0~0>lJQ<;+KsO`#lge3iB6u z%_Q}J9)SBpaeWlR;Vosu>RgYLkp`WD1BNtgcu*{yRai%8-^mH8Gi~Un{!H8~XB}Bs zjZLAD2FFs1tpPqDV5NbW9;DLvv;cF69o^$R_7dC83d8Ls!&uE84D`D5*aN(~ZpQgj z!|H<)oh|)((F7gt^VK79jYKIC!>mCm){Lk1M^5pk9P`SgkS0ocMgQ_*l&?@aMluKeaz~ z=<&H%TUU{d#P%NtTFUg6iF$NZF+(A+jyg-b*qd~dGEk=hmOBDwnCI`$kB zPQxL)l}$?Ysg$a^VCF0mes<2*z&GeArw*hH{Ws>MHf1X?g{KbPmR5eKP1$0Z@vT6W z_{;|PreBj<{s2#2pnl_?CG;*|tE6)CkC8fU9g?Wdz|fLGHw|sI&18~VVMRl|Yhsy2 zxzrfnsx439DW>7jnr6&EqTT9X4S zmQr=ZacyOLyx>jTb8R{1dk{NF1Sjun%Q4?yTOKxmuMSWq(*dTiHTSaPaXgVyF$&^` z^}S-$ajR&f@GKc(W6Bnde+PpXKeZ0rd0lnbf$C#f-XQ;G28%pIX4<;yW9?8~l8 zFY%pYYBZJSxx?|oN*4Xa)}sF}{5wjNN5&v8IF~6EQ{%pB#Tk{?r%_n2On-Xpss?c- z3SQoQ3Gsq7PR39{>^)Mmt$d?jk%-QaS+?B064%!Ay-vmC?|V5=E15sM0ji8$DdT>2uK zjrUBW3I$EAYPx%||^b+GbWYd12xXEF;jazHYPV63zt;mez)CJE76C`Zbz1u&jRW z$M^JDR+ov4KnkI~{rBWw7kE&RbS&!?Sl@A+ z8v&;kekjI8{`F#9YI?-crfdr*TO0)Qcy|iv%B;hwm4%0)VPOzMccOUi5b?b7JmLwB zO@hze{6ho_(<(}gSMAWGQ-?yS{J71c5=cu8KQT%k`azqr9;P@c(Py9uI-m{kz!{Sn zem@SGLoKgEgHJ#mFHEku9SvOs+g92db;OOz#eYx1;Z>V5eua{z&#)r6rJfVEwB+M> zj58$bXu57PKArLRz#AugjDYp1(&R1HA=&Onx31? zu{D;BU49%tTSJqk9R{K@cvpsRI4iD4eW^C5q1AZp7lQELLGvE@ib(uB6?iiULL#E| za&12+2Jnc%m<)nn^ROw{A!myp!U=xOJ{iwju)08(;{yAelvDUmIBypphvI~PYshYa zY^Lb5X}Bqq+eL_ag&gOhfO4FN0w#f;Qa!dZ^>WB-2@1dr6EQg+$G@FUg?1YKH%n35 zn;iY#xXR%jsU0TkZ&HRH!2fzdb#6d)ZbEf#K!NcezP%_b1*k(xo7A^(hb$KE;^!eU zNE!d|q9PGh<9~di2w}HSJ{CdoPa@8?S#%3^BrNW!8XqjwhMYOH>FHQ_H%<>PjKEud zz9>?S?*Hd6SLDSff2R`Qjc(Rq<%$_=@rEec^Sa+q2uNj$WpB#+P5;zN*^3 z@bF0gRpnJU{cO`A{*_UP6CeFL6#h2%JP#WGRRQ9Oo$%4>ds<>lTW{f>gHM|N0y~0Z zVGIF9cvPDO3;47$KWZ~H_GWU<|M)uqV(7bOObyZ27LE2{)HKTHu1vspSolwJ71xJh_gZmHtMYBJySwa*-4snmlgVKLSF z)@$26*C6Kvy*Z(LOY8x7A4tT%vBgX4FLgVC{@Yi6XXRd7UK@LyxgU?OxNb=Ke1 zycpewykojVK9^$UeLQvtqwh%2A5@kN$?SId$*Ro`^k$&L^l@YuG3tLuZAOMI;@Q~S zW4ELKwGHrL&BOZTd()~IIrTM6*k4hIw04!y7Pxxr6uf1##oTYFTI@&f62E1_cA z21{_Jwe7dBQC5eU)ui?bw#HeBFwH;m+x?AmN~kE_n4nK0s~z9|?X!ki*%RMEa!20$ zuuFq%5-KLIPt<#u)xLnxuVT`HqVTzEoj3n<$Dui(U8)L_To*In+7jQ z?NwQ|y}f=n_8Vz8J$mTrx~k1#y0-kA*uAk;7#*W4pVRYC-hNNjwhQz&+}{07?3eb* zTPv4d*wyuDK~+^-{Ut;QgC{}1Te)dit0A9$SGE08{T2q@iP+Qj#LW<>grl#nwGxG( z#cq)%?f>Lkhpx5K+!A{__5{kBSiT$MYWlS=pA1d5Vj4vL10-Fv=`XK_BrVNi+ji%w zo*_x&oCvbEyYjjIr+fVNXoKX@@GTd`{Og^Nv@519*B@ZK4Rc(!I1CMVX}sRSa$_h< z_u7W;dFMT(ZYBX1y<+mF1PWjOPk;0N5tBZGBTmKK#q8ZpEo0GtFEr!;Yv1l%< zkF^V(B;T5!Z-7(GQJd!m z|N3utAA=sdTGt{65G%_gDy|9BdoTw9u8-8?Th(u5>o-2U$A%x9B~)a!O(;K7^@V-m zk&*bW3$IV-4EW_X97xPtcyc5^Paa=>vN|3LzonyApQweZVXk2S_Qb-94}`998thD*eK$YFq1L7VO_mH@U#r;U! zPsBYUZnd~a#r<5|FT_1A?g??f68EIIwc?%@_l&q_#Z}9hyG`6Mal^%p6t|_gt;B6D zZd-BNiQ8V>OT_IUZbxxDiF>)YPI0@6%b!cub?Yu}PjP#R+gsee;>L*EU)(rx2Z=jG z++pGl7k7lXqr@F8Zi2W;;wFnbR@_u^$B8>Z+==3*i#tW!8^pay+^OQ;BJOl?GsVpo zH%HuDac>njU)(}*i^TPc>le2~+){C8i#tc$JH?$R?gDYk#JyYG#o{gzcd59`#9c1# zed4YV_d#)2iuJ%y7Q=U5j zs(8)gq&%b^A@xU6_mMhAir4bxcEygP=1x*0N%=|LMe0^kYe-Ee^*X63q|T5^CDr}{ zP@_rpB{hUpDyhDtvPj_)>$==KNa3cyy4(jywI%f@QsJcDB!wTApe0G2Aa#~hHL1?n zFV`F*#rxK3-XV1Zshy-eq&AZ(BlQBQ6{OaaT1#pTsaHs?B=uKP%Sas{RYvL;~AXSUQ+uWB)eL-p`DgG~}-1kTwAjNCfYW9$-CAE!|qcx~aq}r2umQ)u~>qreH z^%$vZNUb21PHG9Md{XmB%_UVrs*+SbsWqfBN&Sh`4Wu@c8b@j`sRUAoNev@)oKy^` zx>eEk9&s3@onXgdJgRpLa}$~4u=8QX)UA4U{yb%WQf~(v1)GgAgT1d({B*`crY}Yy zzBRIZ{9xLE5ub;^Kp-t28-^kz+w!>>42FZ{vkFXGFdUe2tM?e-STZ&^_!>wpCez(8 z;*$ub4<3r~f`M?Tc|cyW0aLf?xU%Qo4eA9v_#aFmYB(GgfT!Rp5e}pLoy;2J!(Tz` zKNz1slc_Symt_87m~Y6`z>(D%@l_ztN0hxIAEF#8njV+IvI1i2R-Mlu{lbvltne1W zW_pSbSY4Tq$<(dduk7R4iOg#Ttj0YW!ptKx8V+MVK&H?zr9k^a!&H)~fO9ddn7d3p z$keU+7~Os|$&qwuNH8#ezpB>3G6Vi+0VO9TK=EmdC-mtH)E(epzOc*{VAuoV0~)4o z)xLSiXxt^BS(6MtjDlqf*<@HPGC5=tVSAIHxgC~1^I`QYI20l=fwWC~GCeUQSemQI z^@9!Ro0Zyruf)rwCk+X_QBo_>)yfZ>1}mltE?eSp`tT5r2Qy-L=M#Niqz{i>3#{i# z@pT`4cF^ZZF#Hfj%ql!#0v`bNyKtJ+BiI+y5l;*Wyp2;8Ow9*|q9Eo8FjGOzrH>uX zTlykq+g_XL(`bQ9#`Z&biX6G7%IBrga$tt39y6v^eCL7T@mbXgKFe>jeZ7 zLxO4Xj9R@8yBJmsXL6=>3)A9Ti*uz_Wg-O26_3DDE{+T_33$S^CIdASK37^Z7?p-Z z0W2k7y#ohJlx02w!?gYl#1A~1N$dCX=V9XD@A{Q0u_9Rn)0oD`Ww0D^V(=pp)gD%S z{s>}_JcjczEaQ|g&X=$p^J4fzXT}kqOTnZV55?if<)aX&XBnoF%py2V>TSl=l0bA4 z%<>sNSP9#U1dXiCB=}Se35I=z+~cqzVK*sz+z344EVvU^CBgoQ1Pwp9rB4LQRR^L; zxgKK^cDw>!oO_wD?g{5tuww2Kw{BHvK5z0F^9f4*X)w%(S1M>*bM^lNvgv^k>Lxlg zB$%fQR&&y)c!~++Y1-^;;5i94Id8d=tfmBf>Q)8kZbeTqAlUaDyDAS13m9gHb0?Uw z0iQi!m}ootdMc@f1Sn)w(Fd=25 zG>IX>vc1XF^1(2li&=vCU|2Ts`5PFP?F#xF1;Y}EPcKAdoWC-G=i$)jeKLFCuxzm` z+b`e*%hmx5%O*ZZ45n_?G;4+5VDbDtnzba}#w^Dnt(L$tGSk3x28z#nV7h?eE=<*d znGx{G!IMc~#MG^Nf$|miEV%n&-(ohoa461ik|~4Z1d7jEFeAZ;say5ILS=u48CYsS zfZ*Vqz{hvOQfSKA5|ywnSn>G+#3&@jU&W}tC|-QP#{-5fav>XRHkdvEpY>pv+S81~ zH5h;DA_UVPg6a?mM%8a17%^0Q8T?lamSh+y!6Y#3x)$)UXB(fs%rqa&brKuSD_~NA zy_w1-7!Hh6N`^lUBqKh3!ORCE<{2<7=%WnwstG1OJHZSEl|dhSxHXTxN@ggSG@u+~ ztp>xSKcvqC5f&pp?;7T7`g|PfW5W~1k)&ILxe<(*8DPAiq^9qI2@2dNV4MM;Fw}jR zM1a#!&opajR=}48SL$Vaz==IL8~qv#+i(r6ei%-Z`Rp0aGn`tkfUSU)R8SE07_5E} zPLnuhe*-U(6QU5T$YgYo^bqGeG55ixPZoV1h5Zc#P0S#O27PX)4^J8stmpVaq4Bwk zKFh&yh>PJ?gmISB=Tk5~>$y@l;K?;$o&f6qfzzbi_b*a**)gdYL=;1U!2kQD24f=T}y4D%VxI5r3|ABs;) zFm5nnJYX1SD!nP|j3Z;}852i*UIcTyM1aHd9mXkQoEyO~4v%Y8E*PTt6oDxNBjzg; zERV(Mj4-TE1)2U}SRe5j0frN@m^+Nmr3j|)G0f9sRvD%OIlRxUhzZGv%5s{45_Yf*k_;M>1Ps+1yoRK7^H3n+$(5z`^k%nSO8>jMv<#5pYmN z%O}Y&)Z^tegFccP)mheD^2r3lbw^521SU9zy#WRlQNM!l>$kyh66K{-`T{td`o+Kl zrf$_UoMWi5Tn>9LpWO$`OxM6RKQD3L&(bqgPgq%rqe#$%?6qIBa>3>S1S@|f#*o?# zrj8+Lr~z~V4yDf*U`7GOa6RG@p9a*4a0-k^uy4#<`vs%YkYJ2Nj5_6jGloxTlEJW1 zZUpM<;53Z6xi)DHhs`Di zabfCKnfCZjSU4moF(0ylS|F4tRiM=;c*3;42I_ypY0@4ht&FIu%PP{m$3 zPm5QI1jiu?hE?SkYK{!a7@&KImD+l`_S!d(zEpG2_fPb8VFPa z;JhD5AeetQ_=q9F+Vd<&^!m{xXzKI-JP@4(^XEMP>SSpCJcVHXXXw-2J$oeo?9>5_% zA-I)y4#rq4cDoTu-v%o^>CWZJG&QPaeEytfT@@c4GH$aM9j@{ zV37$Fb0Lbi2Gj`p90U^u{ESRbB*DeD9VTjZHJGtLIr!u$+ZAALVleKl*?i)|%`BPQ z;G?;?vD}h0ry=&Nq?QSW!6YBf)5U>c8^Ww*>*Vaz>*2fu**kk2UT zMX(=}83!jQ`XT+yJ&YI0*Gm48$ww21U(@%oZ{{(V`Y~mJGG5c0j)VP@jUKtnMds1`LP5b~4dGao-3x}SqXh40iGq$N0Pn(-ee@xv4)YdO#yQSe8kKGGaXb9luaK7 zL)nu!5qQFm5g+canzYfw3oxF4ZR6{myqGq6ZF{(hCZw4^ENDHS>k*N zf+nT zShk**%yGl`$wa}S3`wxwU>Hvf=MT1o#JK|u$0nx-&BN#>d7AY<*@_G;9|RN5<%0>Z zSCXKS^^5uuv-0x^#~7te%p|zXSA1rIU|ko(>UnU^l`myV3<*Z3iY-U(z2z~SfG$H{ z2W)p(9R+~TX{1KJ{afHmDVTY5T`5Kl!ES;tf+oV1} zMxUgb!J3F_$o^&3#8~I*Bq_gQNN`d~Aa@Ncd*^ysJswVza%{rsDm~i6vOYLk!{H|E zvt*u!W&GD+^(%0ijgRO5QT`lZtY0h>>H^ETLCg@i%s&}ckAl-|{^{^w{99qwG+0Ji zNal9K+yRvLyIgC;pif||_WMur{{yDS9!!d-(qc%kG#;QXfYW4;aE^B-!7dEQ=P8_D ze_>~>a$qlJuTqvcLu8QQB=#uqA~HM>W44!)Sqg^oC&5-4W;K1*f?=zQIS6JbsK3zX zO2lTIadbu)CWXvp;g(M(nK;8tBXbed6=FV}UWS=LrUm+dxPXom%s$}lWKM?q*f8q( zP~x;UjHK4lFp^Jq!$=AG8%D}D!Z1?mN5FJLYLl4c9>ef+A~hOgi2Bo3)^t1=;u~~s z0>d#SK7Rw#4~#U34c?3^&D+s1Z{vxc00tqg=FK!d^MSh1FmuS18D=G!-x=mbGS9%7 ziv+~iZ1yD4BI|7>D|p<_jGLdzs9i;Us|(Q)hhILJTRFFu^@bPhsJZ zKxNf!d@ur*2^WzGRILKEY|w*CLm;hJ=V5TgaaN#UbC%NFCNqU>G7}9m6=Q+mDCZY1+OO477i6DF};k>K>EPKSnCxtXZ)Ug#CbA`1dX&kdzX#H zIYO#MCYQ_r^MJG{$(H~*(`YWfP3BeV#)a@e-^!ae=`*&z7^^HC0!+WTb?LvcU*}rI z?60OnLxP2Plgt5Fw#-pj{Slldbz=NL1NBA#89&h^XokZwek!a^fYT&?19ZLY16y5ApIxw&2k~J+$xsc`AA{%xE9Mt?67;zQ%mo3TE}=f4 zVd_>LnUC@D4?P161AfZqfti56OjS&RWe}gPhT*##`F@Hyi4XT`Ibb9Xw`XjI$qcsD zFqvd_feFU>Cz#HGI33_kL5KzFE`}LNraK(QSwUt9oQuG41E+6*LlNYAC4C1RDjgDR zDHsM5gKS`e^U4*%!Xd#*Q$|J5MdHbD&1aSiVD)S`P5LqPyN|mOKnw{gL40SfHo#K2 z_{EhROR*K@Jsr>enfhn+;dCptBSXpP3KYXVGkvJU>u2Dwtt9Dvp*|mg3HEW|1--<9 z2TZWuU4(^0f-y$H%CQ0G05NO;W=U;Gr^0DcZ^}PQ$RYC-teOr><{4N~_}KAVVD(FI znvKu=+0}z#MQ|Bq0-3<%o(x(i!3oaBfx2)`VRq-5GtBvSA1gejW)Q5AjJZKzxCDq< zWPBb3>Sb`6l=Xahu`RgZuq=Ir4KorbUqv>lOKAN5jKcU#jMW#Vip(fj z&4#%QRt(AtV|`m1$UmNsu7qWjTgh;mV__DO;W9^`Ww82YIP`IoX`4_aKAa=uT%F7;GBe=>7ofn@^9TAgm*@YN>-kqq z#qohAjSUbBoC|wQp7HgltNs{Trz-lhFL0RJJO<4++ zMXECrA~-JuWQj|f<|5Z*to+cu=(+T$smzlZvzB7&R&B)iBAH{eo(ORPXgLFHYq#pu z4X7`M6V&|zbuxKTLdB3^17?wJlmLGu?_rnLuF z?|{=JE$jRu6JFQDeg`X`06Kv-j?creOhJ5VKm?}`2fW+i0ng?%KU-{)Hrwn6%;iIn zh!_&AM?N{O4$&lN*bFSiJXl={r%7HPV{CEWt5=kIfnjecR)bn8o12AJiG~q{EFBDk zwV_WtI9-5O0rdblP4cnyyJ030KYW%JX9~Y0GYXM75_^!@X_$p%-Z9MmWDXhTcVwy! z^S@-MG~Ejn!&k5rV!pc2{34JFkQju5F=zR2gJkpE$fy5=WeMhzX$6ORiTLnKUKVr- zeTKr3K?z#U@|b{}h|?;< zN-dv0{lH8J`0&?TjI)wHzXh`wD8`F=(&uXWtN?Sr^&Db_Cv1d)Kt0MZ%VICy& z6)Y22MTYYznJ3AFqhy>yPXc9humrXAxfV}42YhY?!{wos9nL~93?^~@0EU@zja2_@ zd`6J@3z$oRnPm2X83`0~G16z8>wx+)I85zUGSP4tXFlTSQO3tfCfzXHlUm|8p>=EFCx+HHLJ0!s7sOmJIQW_k6P4ZmXHVbol6eFic<_Q2UKf6`6T(n82SI zjIYdtasFVKKQq`?6YMaVuioCL<v+la@D0=$UvbAK%p0(Tfk80H9B0)Ho?Sam0>#4=Ly5e=P{h*C`;GVXD65; zKruGt%zQ=zb!)@${lD%AhjBz0dH%#;+)?O6!%SkZTTHOoWbTF29l<^&^E{j@!2E*@ zXF6vffyo!^$?va})N@96xMZYDlUJY#S0Z z6eY$V1*Hsps8CGIP2{Qjsx=y ztiBEoTZ|)AyFf(2Qeo4JKuitzd@PCS?n0UrhpnI{SZ&q=V%Z}7ov*oh=)cEK`#c8C5uoF@HGnZD}Md8Dr|hZC%Gp#0cJUV{Maw%;+z z?S|P&W)|#?Bxuh6{^;Yto}Z#Fh6G#VI60P=%-3YNi(tw4J)2BKcL2rI!lln8@R9E@ zn#eij@+yR2q2Gp;E%_P}GO{n`6~z(iCYqR(L;v&BlR&X zOCdg=gJ6bYxQB6q8V}Uhz-iKtW^Yar6hnf7^C|+X_HeeyV>m8v!Js(0C4+qgr^PwR zcJmE$=H%tOoq0ZIp*zo?>-IWb&K#HDnVDDU^3Jk63q5?A=E}??3Ufz=_}4Ov@_e|wxSle{8_n9cWDu7g;zHX8W9(?pYobgdGN6!hme1 zkXEG_j@5S;`h5)CARzN;kd4VDG!tLmYC{Q>XKQUljHVg^!51>Pfdd0sJ?ysS$`&8<}ANEBdgf!^LYE_DhcQu zel;ao`P}{tjuK^(=`$keErX|fcgO|?5P}CoFrKq#wwpsbJ6hdblI`=tm(dpu+A~^Ns*2 zT{y#^YXbHyEH21IN>^WPynPwyJTw-Pnsi%UQM}*p&BG+;R@UIo=vx9=V(B+JBMVXkz>p+Khd!5PXbOGbl${eNgct1(dC@i{q=KniWo z=pPz6HbfYkmZ!d;d4+lYJXd~(DJh!Y1E$O)P#2C_OM&xesTu{htYpdfHWC5uFRhYh=JOj3LxfSTm?2WTrWV^~4c zy{f>_^%hGl_nn_PHXp=V~HvL@dQ^j;tcCs7Yx<;3DA8*$iBdOgbP!%?gcpC({B z=B$*+)F3bUo*Bh%W%YlCh%rhyM1qV-NleOf=c6E5d2W+0Ja5But1!x10?;EI<}O+6 zr3fm=>necy>GoxL^NK>AUyHJjgO`#@Ah9u+(~}rD1!7X}A3RQfhHr|A$%a0T)V^+vUxlWi925cJ4Sn z*G$IfT`k1dmCoLOgHKnRL}ydO+y`l37@0p-;*Jl(^=%ngF@%!QV(T>S{)={ zhI^RPs!@Hp|Fh(S>a*m>{ea7f?0>Foe9+=rhn;w zD_>WW97PeInVW~r(@eLM>NIp9XQs=CO^G$5{VZjoLYr`X2If5Ig+7c+>{II}rKXLT z=PNFNaQkyH zdtl%s#XC8TqwTqe!|oAFsI~Pg^b|IH{1b}Ou+gclK{x&0>SOOuO}NNYgwpvS5YTTW zR*u&LC5$^VzborDtB66CbB?>9$R9E}{@~vD@nxJTMa;^_bSxzbHX;PY4b?o$Z|!cz zi$c<5vtcQ{RMV~HyH8$WK6V}0+xYs7FpHR3*kuVbYr6?x#)aA7WN`7e^5NS5-_g(O zi^}~}V112q&Y2^752^!0SdI`S#4OL4SKQeXtaSm)tu@VX-AAJ*CTC1Y%UvlHZAO9r zuWdbNA`ffp>GEdfs`{;+D~pP4=)o6Oeyk|Ima5+va~RTZqTA;w_99@vgzMucCSe=G zEjX9C9O_K+uumpLk9$Jv$gL1t4(?Q}q|$MMhC`uQR!|9oV~d7iyqPM|>%{>Z<%|i1 z9G5W_E2Sh%5uwL-R1|RT=5zKc^z_0>KvphxYFYkb%&8^gd`WvRd6U}+RlaHS?a=>qZJCekQ0wWs_vYL zlS^k-QL*gyP^WAJz$qIZm{UJK>+4J^_G0Ss7GNRi&D8@lfe#7~=F8B;Fel}^yfdH? zVX`fB_V3keK>xliLpl^^0a?ZQoDwHbPD!A`#IuV&SOBdv({8>lINWD%geDv81!iw0 z&74EKqc>Jj7AD7Ot*uslIa@-_mV<-P&{rANU=~{=)Po0_`FUA+{`^^1%k=2ua>h>_ zliY(%ieC5RW5H&!u7b?G8O3>=f5$+i{O&|f#%Z2x?j(FT2jQ`3hEB30|K@CWj;lD|kM4m&UV>qco}LMv2`7}?;UJP&CDavl>&zN-sNaK4UH+wPY$; zNW}x`@jGX@3)$&z*-M)7z!2VG2M6i5!T9IPPH_jLx3j ze90>1CqF$he%Zx@P4-3EunM5EZ zO(am>bo4akw~h;Q!--+P2D7= z-e#3FuPtZF0zoAKXC_bpm`k=BuU#<2kh;tSr~vj*T)HtC)XxFd0zxr9Z;4%*I23~} zlHu{@&ER1Fxy#J0YJOg!HFJG$d9ltG%I-gl`wG_5fnoKrXp1cNhmr#WMn zQzp}Y=Cu&cpQwTKFvqpoyS<)1)`4!c1YtzWdlw6gz={&!{xF*g??(-FojKQpg(@@C8$vsa5n2R zf|4FwbMRG%a?8eWrWd*gBr0V6w8k73@U-y>lT#C&%BfPuC5?BE8K01tf&Gh0O5xL_ z$>YX2)r85TQ&WJ>7^j+)9zW(3f@fe86CEamu=)70y`8o7IpE)qAK24)WjP*fDY33 z#v)z3y^vZUFzSNfa2bJM)t-^aAsWGI0EucEJjd6U$1*IrMR;SBH&%K3EAIg1jZ@x% z$~#DT2P>2a1!8$v9F_*<%=fCIER_?Za$;3Zf0Z*p<;1C+fhuQ^${DP3ituix2uZfyxt1c=Qsi2STuYH_DRM1EuBFJe6uFin*HYwKid;*PYbkOqMXsgDwG_FQBG(f9 z#j2A2s$_sFiBly5RmmV#GFX)iQ6)p+MQ`{c#}edNf*ebbV+nFBL5?NJu>?7mAjcBq zSb`i&kYfpQEJ2PX$gu=DmLSIxExg(;2@sup&@gZq15E>mG0-@07z51%hcRTh=Hz*?0l;g;pL_BZ z{XZisC$G_BC~AHNPCTr)sN*mlTdF^fNT~ETcvD+k$mu#83WOE9TQMSL;bRYl7x%Zo zfrgDe5tb(E!S!z(LXIyazlM6x>{3hHkToulw0!F}GuMMxG0cEZSFBwH zj$V2E#T40;D3|pj81IB~Ju}JkP)Sq7S@=95@RV<) z8fGHRAFODR$LG(hDNsCcoG=`-&YsHfp2VKI*7`BezyP>%Z> zKEgz5?XJ6J^PB`iVs-!xzD1KQVn8Na2KI_$>JK3%;|xM|;YUBL6+RAd`Ek~l?ZlT= zArIwP2IUx+F=>1tk)WdVBVt262}DoCw~OAwb7bAKao*jTHNKmZHw!T=U9&;bZ=8@Q zK@b{;R%#MIg=t*d|4Pp_RI>95GqCFm9Q0)HpeKVz8;y(7Y&a=&ed`)UJ%idfXBJ5) z=-t#e6*{sCJhqNn%&Bii#!Z3GiGw&UcA)irUk1KDlLOL0I0Mej@C?NZh>7yG)sTs9 z`E1aeh*;+gKd70&KOlVUfJxRFaT2z@Zpk88HWKH{FLp>OzQskm@>ASo>kAu}Ghoge zANfnOrn!o6dK(&5k~nYtI1|5_yqQ4YlW(V_W_?3Wvc2I0{sHV;l#ee0z3%)V;j*ag zS%nzzY83rL%(&S8vbdQq_+ke%_#UP~*Ga6#;RiMn$;J)PDEtsg7r(R#glpvADF4{O zQ(XCp!I_*NI0Yh_I(&cpxF#{aKYZ+f$$?#>iFv;8lQ46ljK%IB7QX+)kPo|9mGh(@ z*SPZM2_G|FjuGpteV*_GQ1%8<8i$V=jaS{)(3F;MD$lX82{`kfB#Q}j`HjjCN(=} z`^1i!Fd-FZh{br(6_WC~!VhT>wwdzwAL}Y{H3(!SC2lj}2TYoom*YQ2gl57I#(AN; z0N){an~Bp*_(9fqN=gX~sL)m5hlCGpl*aAdaD5rvq}9hw@(;+!h>abJBa6JO4Cv8( z0|3|UDoDv1Hwj)@xh^jc1M%~Wl&r)=JR6D^y!=^%$IAza4e~eX4Ybm;lEbIGKP4@8 zNHg^x0EH_PC$Y`UR?UX*FX5Zi-0F0FMdFO|KfL*`vj<;VS(4tL*FLIFu>4gSf5AvT z71jm|qxat6xDHl#!KFR0`2C^Q7hVuXd!0+$H7uW9Hw3>UKa8K5?Q&SZntWh_l>+&z z@S(!iDK-@brQ$en3$C14cRj*ij=XUbUtDMWqz<^XD}Me6oQ)rSE(Uh&fuDi{Z|aG= zO>VTQ(q8y&EpQv~;7vBwwJ+jLv#BMq2oD@S05^8PKMHs9_~E~62!2_I-)V2cFW%My zvqwU|nQBwZN8$fR0e8mZIWT4n$_2y+H7X9j)qXZbsp-I^Yw;6w;5;P~LWsl*;CE)5OQ2soW^G`?*IPN9<`VRML^r}J|1MkL< z{w5&3m|eJV0(cmx7=Jg?yUnI9--Gmk{rBR3CIBA<9t0l3PYuriH@t(JJaLIbEq=q9 z4E+3Ev>Wj8{iqM{?)Oj*;H&SWAApMwqC5pQwd@em2j+f;dH~NHMf$+x6Ywvzsr%}X zF7QxpN*R6_|gyR$GB9yJ)rUcAMJKO{?`@^iQ-_yL>h^ z{YtGq2NvRIt;x7wqE$bwHUSp{tASB5S}jJr^}x7d^mnXQ(}C^r%h$!gTY&3;vvE;C zScy#)#c9bPdIy*a%oz-S;M4dy?Ji&(e|A}FQ$2BG zYER(iNg5|-DA$dsA29W1Jii0!Pt~djxN(|Rj{uW1v>Gx8=>u;7wwkWhTHq$&RzAJ#AG9)z0BA!%MZw1|9{L0f$#2U*Kk7*aEaKE(|FJcD)zv4cr5qz7XSTxmNE0 zC;l4sDnt7MCjf7`Ppjp?ZTBNT;M*%uexSlXZirfh^nQzQz+2a#9e|Gmmjly(r_}~v z%Rg(i2Urg5bhk}qzNA$yuseQMx)nI=FGvr#qzdwa3nBJz(P|}d)9Wb5Vw?JCJJJI# z-T`?49(V)t0G#tC+6Q;X%sH;riX|8~Uukt1cp7(FPOm_DPC~wbEl;CgE72afoGB95 zG3?Z6)8&vihfN*-HR=x>ejnsM(xy5f+~O9n;5P%;0b90&tOEA~hu;tRy8vSexU3aq zX@yPg17-tTwY90Wz~vXB?SKb?UjeHxf{Z?Z@piFIjRJ1E6k`Fnq%-n+5OV57eBjY; z$OE{oJLb-ZFwS})ec)5kHnkME;7a5Tyt|K0^$t` zzZi1}a5-?z#maW@68!U&Y9{ut)` zr+O$o3cqZg8m)9fZ>2x)tMtKGrB}z{GM<4b_aLQ@4#q{;LzRAd7%pGFO4*KMj_*BO zIlRM_j=Nfge{r?4KRiO&Mqth_8>t*`k5qciC>5C#uXM?1rB;ttVV?jej8Qr>0rOTO zriesk+nt0qx<=_0$tpYw>%fu}Wj_c^7_00n$Et|UX)3b)c-*@%UfCX>pzN)#QxQq& zD*V;S_@@?Alx@QG%D(Y>w9AdyL*JzI?rAEr^)1RVYPyPSm8onk-AaFvqjc{gWgB+~ zu1C5*4)-hD+7(L2{04pb8x@}Y zuySmDSlN?TDO=&A%CY%TrPn^8>_?waI&!_TtzNGpWB#bZSN>7iPy7+|)5n75*~Y%8bn?qs4>l`%0@jnqtI(cXREv{a(C@D){nTr?$m}&`dw46>$gL{uFz~Cb zDss^4$`QE@Kg{2z!XmdT`^4=k?6K|mFZI9^z_vS-{p=1Emiz|VcBis0+o>EIcS5Fi zs<2jAuX_De**ok~VVPK~?%Sp8hjuAPyEl~@{U*Y`sT^BCx7w}3#_m>jtYeYecB_cT z-%@(`UKM_5uhO}1EBm^)l|J-0wlnXm@ce_yw)h_^qV^xkQF}<) zA`dIagu{^Q!^+<2L#2BjQMTM_)nZe%a+KBJ7xzb%{i|bWlh4uDU!twQ#4UbU!#?>+ z*`EDc>HL$Z>nX_N8OZK;sMmL@#Y<8y}OSN+uujqNA%UU-TkyqkJVvM#cKPBSZzxlpmp*n zZJ!vg)oXJ!1Vm8*k z{gbqIPS*C^$vSK;Z0{+$#oJS`7EaM&Ew9Jie!aG(-=OWous-&jsv~cmsvWsl6CazV z9ktW6eeEq;Kbxg(C)_&X6I}9fB1eZM&A{3*L)#w*ZUDB;)oM_#4zJGDjuCk}!h5TZ zXjg#sqCkgtEJQsEb=aCB9d@<|c@66KLdxy5oyGz?gVf~ysPlxZEr){ejX!X(p9aar&xlo5qh24nt z^e}K)nGPGhNQWJVU3<4yPu;CsjJ`)Zy!Ys^7w*9t3LLUnJMLVp!*(v#wj<>_V)_!) zZwcCKnbr?3)8P-_t8JHK9ewJ4ZJ)hD+fF~I9it!8YS}|*=ZCa?^h)hmvQmd_Td5;P z{04f|Z*&rH@iN-!Wv!RI zq9eDwqLuAc9hQxCdEKiz;`Cp1i_*>7@$_aLIRR^P{ub?6w?*3?c}?p%yHSR}YvujB zw)fqq)q;K6e))SkeDQnQ7IgsQaY&aKdf!ek037}Xy}?>ee@K@)VF8@*y(4qWAPd24d3bTR%f;C&{-YvSRLwB zr^EIE4+C2(=+?>>Q470E+ajK}*}_i3rrT|5s>9}30KK4hxJ_?}u!Wt8utk(a+H8?6 zZ4vWY+QN^wwAosv7*&+}A!ls9{u{rj%vDt2c9b}-o0U~hwi z43085)?m89TMXtJ^ctLF@NR?44L)pet-+@aZZx>X;4Xvv3?4Rk)ZkYJ&l-$Ox9Zc* zU?+p!4aOK8W-!6vID}W8`U|)kn430LKYH*6d=>~5#=r?$$!NmseGx&(X z#|>^U_>#c}od;(B$5uIaU=Pi4$+J@8dMu>G4y`Qw$ndhL;`xTHRy>emrhj7bov;UI zJf5`@H?+3!h`~WP3*hs7gKG@7N5XtQ&fqeGyA57`q2-@$5Rb(iHsu)bn3eynt1Vn@ zu-u>tt~y{9Wj?cUc1h$JCUN@f79KL#3wuvKpJQ;hK_~W}e150FcMSI0VZs}1iG3-< ztu)vjdsFg(au;p0%7I5>qJL-c{R}1>Txu{7uE>=C=)bM>jvEZ5J8wtBbT^yuD`IeZ zjzk_dxYi)gK>3_!pu~U)q_;Yd-nR|Y4TPJ1#CrbhbSpm38kx>fgSBpxF7{n~e#SiS zm}T)R47Sd;_{9dpOuRb{#$}pxOvZuoO@G76KldEvYg=RacbIR*=b0tTQEhO7!7?bR zeEt~DDv6QUj}g}zev095F*v>4@^@mt#c+KMf)rEwnU(KyoQ2Y#XQRX&2KN~}Y;f%y z%m1{&33DxejluqRTKqDDwFXDwOqS{1XE4w%8%(=MVc;A$<>A>Z!}DyG80NKbrQt6z ze4rh8Hq3B=c6iE!d)DAf1_SvXH~G$M)E`CFR(tR)nCbB>n0NyFW#S<0lZnX&&y_#V zrWr1fKhLPi2l9{EY2_2BFTBJYH~I7Inc;c%O#HF+=UF)89WvOs{zd1=KTy66#(#OE z_K`xuDKhff=~XKqo>enHo@EnxhD}_8{WCFO0_n&6)vEtGlm147n+@(TxW`~1{`!}! zcz-hZg29&!ZZ`P3!JP)*GWd?c_Y8h$@TkFm8T`uNHwModw7+cSA8GIx2HP3@mBA>3 zy$r?}9At2~!O;ej4URLIZtzBfw;0Sam}{`Wpx59`gL4edH+Z+fN`t>P_<+IR7<|;= zZw>z5;L`?QH2AW?Dude${?*_fgMT-8z~Bc4KQUNi@Sg_1G3spuyn=#~A!i<>x=`@3E#FKem4Z@*j}ji6)(!4F=>nAP0^6 zKOk3);&F_>y_g28lyHyE61aJs>4gSiIt4Hg;n8!ReA(cu z2Dcd8W^kv$-3H$_xX<7LgNF=$WblZ=qXxe)c*5XGgQpFiHE4U)j5mWV4YoGe&fp~m zI~jBuj565MU~hvl2ICA4F*w}dD1!+GlMSXCoM15B;0*?+8k}x0+hDH2e1k;>{RT@7 z&M`R8V41rWtlww-Yu3}Of6Mw~)}OL={8Vk{ zC9IdSUd}q7^*yXtu)dG=8rJJryIGgA*7q4Rzg6`dUZON{ROmY0?o|3L>)W#x*55;d zR}|KM!XGMpj_rx#BEQGnRNrHWJ42YbGo*<-Lz=iVq=`F2nz%Ehi918OjO+h^{e8&# z7HbrdFWs&Px2x>;%3kMdo%=s3{pFQP6DNl1`3>uV`xGXQ45brChIDwf(!`Y^{PQ(R zwibQT1F0&w~0*+&9wtRmAyGo@cwVLFH@zk=K>I#xcg)KEn2g z!6_ua#4RL!mGv}h;u=!=s()5`Eo<$!=#A9z(CO1$-yqkc>$QEP$``Yr&8)Yw-o^R| z>$&RnbG@d2Kl>kH`;)9sv%bjsSFGpqAD+X1l>Ixdsqt{J&SPD~x|{V}{-bmFFZ@`I z&oJ8)$CJkYzgWBQ>l9()e16_`nV!Rci2YaKp@wAdVeMtz!}?X$bG5&a=P%9ipJe|x z*?xw#1#T+Uw}N#y>yxZcv)21h%^d#Y`TBT-PkPnxae0X6Ux`CX_0_N@E-7JcU-*xy z$HyesSH^yHxe%A5I_Ra-Cwaa}V;j#8>G?UG?&tJr_S^7@s&|z23~S{*@m|E~v7f4T zPqDtq8e9(9pTcNrzoJ~9_N3bp=Jb4{z7XTk?jJkyIGs(Jh$Bim zELjkreyX)DM_f|MCk`p;c5d&4z7Dd#Fk`bmn9Tap^=Lh7AtdVl*!cQqmK)>xiQ7u; zBW^2c^HqF@N-teDd%iB;Y~-IZFmYy+{pC`0c%sU0X?kJ2m^!Q9%4M|aS>_rYKe{&Iu4 zkEs4EFT*Nfy@+Q+ViC`r%Xccb7FF)IJ<>DzKzQM%>tGt1=0o>Wk>Vk-;s+|!lr{!z5!@jkt1_#XfH*^J& z8UDnlh1O2wn~8{f@!7Nkv!;1>)b`!ayNs{TwDLXH-|xd>=ia^Dh|b@4(1$QRq1Y$7 zG2uMya}+_n<#At2`{O>W9Yxp|8cif3Y(YK>z~Yu(hDPJt_^fhVt9O*d(Xr50hDI&J z*CHD^5pSgQt9G8q|HaP3+p{OAuAJD*Cb?_jTD3g z@m%TbO+G^+lTN$3*zI8E-O3>dr~0L{;C>nSPlcRE=kQR7?N!a|Dk~{ZUJdl~lw=@1 zT!PW-;dI>Ka)hS``Q$@Xl$(g*W}T0kfi=!*O)>?1nOAd0Q~mHw$NdO?Plzp8A~m_sbg=i?7bV;b=Pe&Jmfzud7jS|T%pYPsT zf*3))@FyLPXTYJ`hbv@9AMKseXDq`$rPGNFI_;82(PSn(QnVQy>wE-FW!9za_;f~m z%21`cnKPMnDVrIxQoeGeb>4I%QWzp-RjTbXm;sL)d)k7XI2y6H>%B9a0k`)D_w}~p z_-%Q-QtnE_8SwewI(&PnE%DJ|9?77;t}eJOmTpHg;C3tB^r4Ak8SvFqo9OcSRDbjO z%DMqxY19UYreR$Y=)_i3y%|r#9^daM#vyID+6{7iW9fU*!#a_Mi>>QbCnnRd?d+}W zpv^gzo^2=DPN!#kkZfmCZ4%c(Agv2C~1d$cIC-PHyFJs+pQ>(A#e~B{P1R-+-G*Ob6hU(b|j{a)@t+`wT=; z##adh3zT;}Y8ysow=Y%~Sb5})&R(QMK1-=`k32Y2its%;ZaHSZMX1yRa$cF?3l?Jf z$*1qN$r`6*I=&q@`I-?o3qkQHOq1O7i5yJ1bRt3?;yvQt4uSM(<0f)jIovlTha9xk zq&r+Y6V3(`>GF%2uc;Xuge=0J$!U3FAEHN7-msa6plf)PjLeIuJj7gU>cSmf3!X4T zCZi5~53FvWP<0h};0+?=mE++RlryGs9>Q~&kU{~{w&CF#4+i(uVIF`AoS(G-1!kh$ zXgs%yrl7PB${MFE-_Gs>sMP0eqX&pla!e-sFx7zPr1*OJx+!r&B{s`wv%r%I%X?wU zpHevSJvM-_>Xb1}8Szn@kw`u-i84c3$ixj}8wNwxJxB>HZ5^Ef$_`7^iCCqChT{lP zx+x(-32ps|G6^tBAkd-NUt?o^7sfQez}&mN8gaYv2zS_=r*ycz+;X zr=AWMKA+8Lv%1=`(c4<^fL}bonVtr1`|_?j+gGvF5wVZrGYYoS_^gHPbu5Nn+^oO_ zkvNerbcP}-9FEWrZnHTYF}yFfC6}!9TXNZQIKrP$OU8bGn{9IWxBlC5yWKY&?i#nd zXLDH@)W?peZd-rE+35+3QE|J|nO~Y;dgleKmG0~w!GiLdh?ue>A|lRNu4BTLkJr)> ztGc>*Omq&4qxkzI%YXZjy{-feKhxfhZ+fL+QVp6p4+A=JT z*$b|^j@fTzZ#rD%^?G;L*L#l(`Wq7M&CiSHgzI@x6cw)0yo#&BeOWwQm0$X$55&Vx zr_=StRd@4S!IssW>?*7(cG8>6<=(QnVp0s*Pl{ur?3~rmf82%nRx3DS1wH4{M~~;I zcuFSKg%Gu&3?3UBPg-lL&n76c^{^9e&B@;%o>FWcZ^72Bo2vb934hJg!W#(NziY4eoDye5=_c&Y((&((c^E?*t4-5<(8W_mQskkNFyG{$$(VJpvoohw0%b$1W z8Bu@MiezoYhhyTFz42F8BL=#nx^a)kTYp)&8eMDij*7zSoa@#J`|+HpSh}?Ph;>cm zy4Egrxx9@S?VKFv3*xLGaSn^3t?sIARU_8GwyGDL`;S^jvu=pzMNEXUJXO^JPieoq z^b|@adN6w1wBv8>BaZJ`*BpWG{F@lHJ``^YY}n!&k7o!YiqX$md8o*>(zUY4l~ZuZ za>cS-t{3c^%9>ZKxN04CpA>7RMd@$Ev>&xs4eTGW`mwh(dOYumUsyxfDZTrbK7$<@OM0$4-WOwz=Y;>9IBvfR z7vB{}#0eZI?~9va0!P(#dtTTITW73`*#j5s|0$lef6uzPu^Lxi0noBsXw9me|iW`r}bEfeb3T>9O& z6mR$^xAOH(19uoWWZ-8Ed`e-20#L6H3|xvcK9b`3PZ{_f1K)#^$>qOo;1dRZ&A{&( z_?Ce^=y|gLXAS(Wf!AUyB$ppB@EZnR?o2K3H}H=P{5u1CvQzE<#=yTc@XDOj@_P;Z zoPqz{z+YODYQM|C-#73j1Ak;-JohGLyjl!AY~ZT~{$sV?7F$id>J0p}fnx?_TvY~XPNzhmH=2DWf2 z#P*Ay|87$_-kz@*`5O$p&A^WsxWT}F1Me~L69(=#@DT$K8aQm=Ap@T<@DC0Af`NZ* z;Fk>iGXwvVfhP<+Y2YaXPaF6n1Al7ZY+N6cx8EHG=JoRN?Yq**Uu)nJ13zHkDg)OW z*k|A_1Mf5NlLr241ApJZ!v;QSV4VIClh9_vX~Kp>+lJj;a?Vi9b|+*xBoA^IR&akgq{jLhggChI}2e2J$D65{Mge zKV&lmp#o)WqCbV+0x5$$1o1$&Lmr0gfK)@i38{fR0`Wp>A$5?Q5L_B1T7~ zRC=-kIl){zBq%*Y_=>DhEN!>L3`o8FfCD9@>=qm$##1Aca zW~(GZEWTlJUm0jvg8H!7cA1=q#rjJjJ1o{&oaK-yN1EQSSby>d+{HSH6B?2nEriLi zP_sCNVWEZwcOgx4bIj(|RmnhJXocz3L@a@Y)+7lLX&7~;Q`5m@qs0B+=W-wPLW!g{ z&eL+%H4(acU6b1Sg0SlF2}+Nz4LDN!0WoB&P6wfE6bKA7)u4 zf>eV~laiuVipV6jPK0STz9z-5Nf2w8QG-=JM5R%RI5dhpqggY)0MTXeE_pJ(fMl2W zG!4TSkkk=Ua1l@>7|nhm9jCo$zKhggbVv<`3u#Gh@!^S@l&r`sip3(|les8*$qQ-l zlWfdmQ3MwGo>DqaS@HEUZ-mA!K~f>Q39(b8rbYpCN12F1DUyMbpq_kYiA_&aYtQCy Re51s@uDGOlJx$=?^}mrxN*w?I diff --git a/ZipBuilder/Template/Crash/extract-keys b/ZipBuilder/Template/Crash/extract-keys deleted file mode 100755 index 0da57003f9a..00000000000 --- a/ZipBuilder/Template/Crash/extract-keys +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -PLIST="${HOME}/Library/Preferences/com.google.SymbolUpload.plist" - -[[ -f $PLIST ]] || exit - -defaults read com.google.SymbolUpload | -perl -nle '/"(app_\d+_\d+_ios_.*)"/ and print $1' | -while read KEY; do - APP_ID="${KEY#app_}"; APP_ID="${APP_ID//_/:}" - plutil -extract "${KEY}" json -o "${APP_ID}.json" "${PLIST}" -done diff --git a/ZipBuilder/Template/Crash/upload-sym b/ZipBuilder/Template/Crash/upload-sym deleted file mode 100755 index 1f8327dcebf..00000000000 --- a/ZipBuilder/Template/Crash/upload-sym +++ /dev/null @@ -1,273 +0,0 @@ -#!/bin/bash - -usage () { - echo >&2 "usage: $0 [-h] [-v] [-w|-e] service-account-file" -} - -help () { - usage - - cat >&2 <&2 "Either -w or -e may be specified, but not both." - echo >&2 - usage - exit 2 -fi - -SERVICE_ACCOUNT_FILE="$1"; shift - -if (($#)); then - echo >&2 "Unexpected argument '$1'" - echo >&2 - usage - exit 2 -fi - -export PATH=/bin:/usr/bin # play it safe - -# Load common utility routines. - -. "$(dirname "$0")/upload-sym-util.bash" - -# Make the error output Xcode-friendly. - -# This is a bit of Bash voodoo that cries for an explanation and is -# horribly underdocumented on-line. The construct '>(...)' starts a -# subprocess with its stdin connected to a pipe. After starting the -# subprocess, the parser replaces the construct with the NAME of the -# writable end of the pipe as a named file descriptor '/dev/fd/XX', -# then reevaluates the line. So, after the subprocess is started -# (which filters stdin and outputs to stderr [not stdout]), the line -# "exec 2> /dev/fd/XX" is evaluated. This redirects the main -# process's stderr to the given file descriptor. -# -# The end result is that anything sent to stderr of the form: -# file.in: line 47: blah blah -# is replaced with -# file.in:47: error: blah blah -# which Xcode will detect and emphasize in the formatted output. - -exec 2> >(sed -e 's/: line \([0-9]*\):/:\1: error:/' >&2) - -# Be long-winded about problems. The user may not understand how this -# script works or what prerequisites it has. If the user sees this, -# it is likely that they are executing the script outside of an Xcode -# build. - -ERRMSG=$'Value missing\n\nThis script must be executed as part of an Xcode build stage to have the\nproper environment variables set.' - -# Locate Xcode-generated files. - -: "${TARGET_BUILD_DIR:?"${ERRMSG}"}" -: "${FULL_PRODUCT_NAME:?"${ERRMSG}"}" - -DSYM_BUNDLE="${DWARF_DSYM_FOLDER_PATH?"${ERRMSG}"}/${DWARF_DSYM_FILE_NAME?"${ERRMSG}"}" -[[ -e "${DSYM_BUNDLE}" ]] || unset DSYM_BUNDLE - -EXECUTABLE="${TARGET_BUILD_DIR?"${ERRMSG}"}/${EXECUTABLE_PATH?"${ERRMSG}"}" - -# Locate dump_syms utility. - -if ! [[ -f "${FCR_DUMP_SYMS:=$(script_dir)/dump_syms}" && -x "${FCR_DUMP_SYMS}" ]]; then - xcerror "Cannot find dump_syms." - xcnote "It should have been installed with the Cocoapod. The location of dump_syms can be explicitly set using the environment variable FCR_DUMP_SYMS if you are using a non-standard install." - - exit 2 -fi - -if [[ ! "${FIREBASE_API_KEY}" || ! "${FIREBASE_APP_ID}" ]]; then - : "${SERVICE_PLIST:="$(find "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}" -name GoogleService-Info.plist | head -n1)"}" - : "${SERVICE_PLIST:?"GoogleService-Info.plist could not be located"}" - : "${FIREBASE_API_KEY:="$(property API_KEY "${SERVICE_PLIST}")"}" - : "${FIREBASE_APP_ID:="$(property GOOGLE_APP_ID "${SERVICE_PLIST}")"}" -fi - -if ! [[ "${FIREBASE_API_KEY}" ]]; then - xcerror "Unable to get API_KEY from ${SERVICE_PLIST}." - xcnote "Specify FIREBASE_API_KEY in environment." - exit 2 -fi - -if ! [[ "${FIREBASE_APP_ID}" ]]; then - xcerror "Unable to get GOOGLE_APP_ID from ${SERVICE_PLIST}." - xcnote "Specify FIREBASE_APP_ID in environment." - exit 2 -fi - -# Load Info.plist values (Bundle ID & version) - -INFOPLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}" - -if [[ -f "${INFOPLIST}" ]]; then - : "${FCR_PROD_VERS:="$(property CFBundleShortVersionString "${INFOPLIST}")"}" - : "${FCR_BUNDLE_ID:="$(property CFBundleIdentifier "${INFOPLIST}")"}" -fi - -if ! [[ "${FCR_PROD_VERS}" ]]; then - xcerror "Unable to get CFBundleShortVersionString from Info.plist." - xcnote "Specify FCR_PROD_VERS in environment." - exit 2 -fi - -if ! [[ "${FCR_BUNDLE_ID}" ]]; then - xcerror "Unable to get CFBundleIdentifier from Info.plist." - xcnote "Specify FCR_BUNDLE_ID in environment." - exit 2 -fi - -# Support legacy account file cache before giving up - -if [[ ! -f "${SERVICE_ACCOUNT_FILE}" ]]; then - xcwarning "Unable to find service account JSON file: ${SERVICE_ACCOUNT_FILE}" - "Please ensure you've followed the steps at:" - "https://firebase.google.com/docs/crash/ios#upload_symbol_files" - - xcdebug "Trying to extract JSON file from cache." - - CACHE_PLIST="${HOME}/Library/Preferences/com.google.SymbolUpload.plist" - - if [[ -f "${CACHE_PLIST}" ]]; then - fcr_mktemp SERVICE_ACCOUNT_FILE - /usr/bin/plutil -extract "app_${FIREBASE_APP_ID//:/_}" \ - json -o "${SERVICE_ACCOUNT_FILE}" "${CACHE_PLIST}" >/dev/null 2>&1 - if [[ ! -s "${SERVICE_ACCOUNT_FILE}" ]]; then - xcwarning "${FIREBASE_APP_ID} not found in cache." - /bin/rm -f "${SERVICE_ACCOUNT_FILE}" - else - xcnote "${FIREBASE_APP_ID} found in cache. Consider using extract-keys.pl to reduce reliance on cache." - fi - else - xcnote "No cache file found." - fi -fi - -if [[ ! -f "${SERVICE_ACCOUNT_FILE}" ]]; then - xcerror "All attempts to find the service account JSON file have failed." - xcnote "You must supply it on the command line." - echo >&2 -n "$0:1: note: "; usage - exit 2 -fi - -# Dump collected information if requested - -if ((VERBOSE >= 2)); then - xcnote "FIREBASE_API_KEY = ${FIREBASE_API_KEY}" - xcnote "FIREBASE_APP_ID = ${FIREBASE_APP_ID}" - xcnote "DSYM_BUNDLE = ${DSYM_BUNDLE:-(unset, will use symbols in executable)}" - xcnote "EXECUTABLE = ${EXECUTABLE}" - xcnote "INFOPLIST = ${INFOPLIST}" - xcnote "FCR_PROD_VERS = ${FCR_PROD_VERS}" - xcnote "FCR_BUNDLE_ID = ${FCR_BUNDLE_ID}" -fi - -# Create and upload symbol files for each architecture -if [[ -x "${SWIFT_DEMANGLE:=$(xcrun --find swift-demangle 2>/dev/null)}" ]]; then - SWIFT_DEMANGLE_COMMAND="${SWIFT_DEMANGLE} -simplified" -else - SWIFT_DEMANGLE_COMMAND=/bin/cat -fi - -for ARCH in ${ARCHS?:}; do - SYMBOL_FILE="SYMBOL_FILE_${ARCH}" - fcr_mktemp "${SYMBOL_FILE}" SCRATCH - - # Just because there is a dSYM bundle at that path does not mean - # it is the RIGHT dSYM bundle... - - if [[ -d "${DSYM_BUNDLE}" ]]; then - DSYM_UUID="$(dwarfdump --arch "${ARCH}" --uuid "${DSYM_BUNDLE}" | awk '{print $2}')" - EXE_UUID="$(dwarfdump --arch "${ARCH}" --uuid "${EXECUTABLE}" | awk '{print $2}')" - if ((VERBOSE > 1)); then - xcnote "dSYM bundle UUID: ${DSYM_UUID}" - xcnote "Executable UUID: ${EXE_UUID}" - fi - if [[ "${DSYM_UUID}" != "${EXE_UUID}" ]]; then - xcdebug "Current dSYM bundle is not valid." - unset DSYM_BUNDLE - fi - fi - - if [[ ! -d "${DSYM_BUNDLE}" ]]; then - xcdebug "Extracting dSYM from executable." - fcr_mktempdir TMP_DSYM - DSYM_BUNDLE="${TMP_DSYM}/${EXECUTABLE##*/}.dSYM" - xcrun dsymutil -o "${DSYM_BUNDLE}" "${EXECUTABLE}" - STATUS=$? - if ((STATUS)); then - xcerror "Command dsymutil failed with exit code ${STATUS}." - exit ${STATUS} - fi - fi - - "${FCR_DUMP_SYMS}" -a "${ARCH}" -g "${DSYM_BUNDLE}" "${EXECUTABLE}" >"${SCRATCH}" 2> >(sed -e 's/^/warning: dump_syms: /' | grep -v 'failed to demangle' >&2) - - STATUS=$? - if ((STATUS)); then - xcerror "Command dump_syms failed with exit code ${STATUS}." - exit ${STATUS} - fi - - ${SWIFT_DEMANGLE_COMMAND} <"${SCRATCH}" >|"${!SYMBOL_FILE}" || exit 1 - - if ((VERBOSE >= 2)); then - xcnote "${EXECUTABLE##*/} (architecture ${ARCH}) symbol dump follows (first 20 lines):" - head >&2 -n20 "${!SYMBOL_FILE}" - elif ((VERBOSE >= 1)); then - xcnote "${EXECUTABLE##*/} (architecture ${ARCH}) symbol dump follows (first line only):" - head >&2 -n1 "${!SYMBOL_FILE}" - fi - - fcr_upload_files "${!SYMBOL_FILE}" || exit 1 -done diff --git a/ZipBuilder/Template/Crash/upload-sym-util.bash b/ZipBuilder/Template/Crash/upload-sym-util.bash deleted file mode 100644 index e98b0b1b189..00000000000 --- a/ZipBuilder/Template/Crash/upload-sym-util.bash +++ /dev/null @@ -1,396 +0,0 @@ -# Copyright 2019 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. - -# Output a clickable message. This will not count as a warning or -# error. - -xcnote () { - echo >&2 "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: note: $*" -} - -# Output a clickable message prefixed with a warning symbol (U+26A0) -# and highlighted yellow. This will increase the overall warning -# count. A non-zero value for the variable ERRORS_ONLY will force -# warnings to be treated as errors. - -if ((ERRORS_ONLY)); then - xcwarning () { - echo >&2 "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: error: $*" - } -else - xcwarning () { - echo >&2 "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: warning: $*" - } -fi - -# Output a clickable message prefixed with a halt symbol (U+1F6D1) and -# highlighted red. This will increase the overall error count. Xcode -# will flag the build as failed if the error count is non-zero at the -# end of the build, even if this script returns a successful exit -# code. Set WARNINGS_ONLY to non-zero to prevent this. - -if ((WARNINGS_ONLY)); then - xcerror () { - echo >&2 "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: warning: $*" - } -else - xcerror () { - echo >&2 "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: error: $*" - } -fi - -xcdebug () { - if ((VERBOSE)); then - echo >&2 "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: note: $*" - fi -} - -# Locate the script directory. - -script_dir () { - local SCRIPT="$0" SCRIPT_DIR="$(dirname "$0")" - - while SCRIPT="$(readlink "${SCRIPT}")"; do - [[ "${SCRIPT}" != /* ]] && SCRIPT="${SCRIPT_DIR}/${SCRIPT}" - SCRIPT_DIR="$(dirname "${SCRIPT}")" - done - - ( cd "${SCRIPT_DIR}"; pwd -P ) -} - -# Timestamp needed for various operations. Does not need to be exact, -# but does need to be consistent across web service calls. - -readonly NOW="$(/bin/date +%s)" - -# All files created by fcr_mktemp will be listed in FCR_TEMPORARY_FILES. -# Delete these when the enclosing script exits. (You may manually -# add files to this array as well to have them cleaned up on exit.) - -typeset -a FCR_TEMPORARY_FILES -trap 'STATUS=$?; rm -rf "${FCR_TEMPORARY_FILES[@]}"; exit ${STATUS}' 0 1 2 15 - -# Create a temporary file and add it to the list of files to delete when the -# script finishes. -# -# usage: fcr_mktemp VARNAME... - -fcr_mktemp () { - for VAR; do - eval "${VAR}=\$(mktemp -t com.google.FIRCrash) || return 1" - FCR_TEMPORARY_FILES+=("${!VAR}") - done -} - -# Create a temporary directory and add it to the list of files to -# delete when the script finishes. -# -# usage: fcr_mktempdir VARNAME... - -fcr_mktempdir () { - for VAR; do - eval "${VAR}=\$(mktemp -d -t com.google.FIRCrash) || return 1" - FCR_TEMPORARY_FILES+=("${!VAR}") - done -} - -# The keys we care about in the JSON objects. There are others that -# we do not use. Note that 'expires_at' and 'app_id' are not part of -# the original payload, but are computed from the environment used to -# make the call. - -FCR_SVC_KEYS=(client_email private_key private_key_id token_uri type) -FCR_TOK_KEYS=(access_token expires_at token_type app_id) - -# Extract a value from the property list. -# -# usage: property *name* *file* - -property () { - [[ -f "$2" ]] || echo '{}' >|"$2" # keeps PlistBuddy quiet - /usr/libexec/PlistBuddy "$2" -c "Print :$1" 2>/dev/null -} - -# Retrieve the property from the service account property list. -# -# usage: svc_property *name* - -svc_property () { - property "$1" "${SVC_PLIST}" -} - -# Does the same as svc_property above but for the token cache -# property list. -# -# usage: tok_property *name* - -tok_property () { - property "$1" "${TOK_PLIST}" -} - -# Verify that the service account property list has values for the -# required keys. Does not check the values themselves. - -fcr_verify_svc_plist () { - for key in "${FCR_SVC_KEYS[@]}"; do - if ! svc_property "${key}" >/dev/null; then - xcdebug "${key} not found in ${SVC_PLIST}. Service account invalid." - return 1 - fi - done -} - -# Verify that the token cache property list has values for the -# required keys. If the token_type is incorrect, the expiration date -# has been passed, or the application id does not match, return -# failure. - -fcr_verify_tok_plist () { - for key in "${FCR_TOK_KEYS[@]}"; do - if ! tok_property "${key}" >/dev/null; then - xcdebug "${key} not found in ${TOK_PLIST}. Token invalid." - return 1 - fi - done - - if [[ "$(tok_property token_type)" != "Bearer" ]]; then - xcwarning "Invalid token type '$(tok_property token_type)'." - return 1 - fi - - if (($(tok_property expires_at) <= NOW)); then - xcdebug "Token well-formed but expired at $(date -jf %s "$(tok_property expires_at)")." - echo '{}' >|"${TOK_PLIST}" - return 1 - fi - - if [[ "$(tok_property app_id)" != "${FIREBASE_APP_ID}" ]]; then - xcdebug "Cached token is for a different application." - echo '{}' >|"${TOK_PLIST}" - return 1 - fi -} - -# Convert a JSON certificate file to a PList certificate file. -# -# usage: fcr_load_certificate VARNAME - -fcr_load_certificate () { - : "${SERVICE_ACCOUNT_FILE:?must be the path to the service account JSON file.}" - fcr_mktemp "$1" - - if ! /usr/bin/plutil -convert binary1 "${SERVICE_ACCOUNT_FILE}" -o "${!1}"; then - xcerror "Unable to read service account file ${SERVICE_ACCOUNT_FILE}." - return 2 - fi -} - -# BASE64URL uses a sligtly different character set than BASE64, and -# uses no padding characters. - -function base64url () { - /usr/bin/base64 | sed -e 's/=//g; s/+/-/g; s/\//_/g' -} - -# Assemble the JSON Web Token (RFC 1795) -# -# usage: fcr_create_jwt *client-email* *token-uri* - -fcr_create_jwt () { - local JWT_HEADER="$(base64url <<<'{"alg":"RS256","typ":"JWT"}')" - local JWT_CLAIM="$(base64url <<<'{'"\"iss\":\"${1:?}\",\"aud\":\"${2:?}\",\"exp\":\"$((NOW + 3600))\",\"iat\":\"${NOW}\",\"scope\":\"https://www.googleapis.com/auth/mobilecrashreporting\""'}')" - local JWT_BODY="${JWT_HEADER}.${JWT_CLAIM}" - local JWT_SIG="$(echo -n "${JWT_BODY}" | openssl dgst -sha256 -sign <(svc_property private_key) -binary | base64url)" - - echo "${JWT_BODY}.${JWT_SIG}" -} - -# Set the BEARER_TOKEN variable for authentication. -# -# usage: fcr_authenticate - -fcr_authenticate () { - : "${FIREBASE_APP_ID:?required to select authentication credentials}" - - local SVC_PLIST - - fcr_load_certificate SVC_PLIST || return 2 - - local TOK_PLIST="${HOME}/Library/Preferences/com.google.SymbolUploadToken.plist" - - if ((VERBOSE > 2)); then - CURLOPT='--trace-ascii /dev/fd/2' - elif ((VERBOSE > 1)); then - CURLOPT='--verbose' - else - CURLOPT='' - fi - - # If the token will expire in the next sixty seconds (or already - # has), reload it. - if ! fcr_verify_tok_plist; then - xcdebug "Token cannot be used. Requesting OAuth2 token using installed credentials." - - if ! fcr_verify_svc_plist; then - xcerror "Incorrect/incomplete service account file." - return 2 - else - xcdebug "Certificate information appears valid." - fi - - TOKEN_URI="$(svc_property token_uri)" - CLIENT_EMAIL="$(svc_property client_email)" - - # Assemble the JSON Web Token (RFC 1795) - local JWT="$(fcr_create_jwt "${CLIENT_EMAIL}" "${TOKEN_URI}")" - - fcr_mktemp TOKEN_JSON - - HTTP_STATUS="$(curl ${CURLOPT} -o "${TOKEN_JSON}" -s -d grant_type='urn:ietf:params:oauth:grant-type:jwt-bearer' -d assertion="${JWT}" -w '%{http_code}' "${TOKEN_URI}")" - - if [[ "${HTTP_STATUS}" == 403 ]]; then - xcerror "Invalid certificate. Unable to retrieve OAuth2 token." - return 2 - elif [[ "${HTTP_STATUS}" != 200 ]]; then - cat >&2 "${TOKEN_JSON}" - return 2 - fi - - # Store the token in the preferences directory for future use. - /usr/bin/plutil -convert binary1 "${TOKEN_JSON}" -o "${TOK_PLIST}" - - EXPIRES_IN="$(tok_property expires_in)" - EXPIRES_AT="$((EXPIRES_IN + NOW))" - - /usr/libexec/PlistBuddy \ - -c "Add :app_id string \"${FIREBASE_APP_ID}\"" \ - -c "Add :expires_at integer ${EXPIRES_AT}" \ - -c "Add :expiration_date date $(TZ=GMT date -jf %s ${EXPIRES_AT})" \ - "${TOK_PLIST}" - - if ! fcr_verify_tok_plist; then - ((VERBOSE)) && /usr/libexec/PlistBuddy -c 'Print' "${TOK_PLIST}" - - echo '{}' >|"${TOK_PLIST}" - xcwarning "Token returned is not valid." - xcnote "If this error persists, download a fresh certificate." - - return 2 - fi - else - xcdebug "Token still valid." - EXPIRES_AT="$(tok_property expires_at)" - fi - - xcdebug "Token will expire on $(date -jf %s "${EXPIRES_AT}")." - xcdebug "Using service account with key $(svc_property private_key_id)." - - BEARER_TOKEN="$(tok_property access_token)" - - if [[ ! "${BEARER_TOKEN}" ]]; then - if ((VERBOSE)); then - xcwarning "Current malformed token cache:" - tok_property | while read; do xcnote "${REPLY}"; done - fi - xcerror "Unable to retrieve authentication token from server." - return 2 - fi - - return 0 -} - -# Upload the files to the server. -# -# Arguments: Names of files to upload. - -fcr_upload_files() { - fcr_authenticate || return $? - - : "${FCR_PROD_VERS:?}" - : "${FCR_BUNDLE_ID:?}" - : "${FIREBASE_APP_ID:?}" - : "${FIREBASE_API_KEY:?}" - : "${FCR_BASE_URL:=https://mobilecrashreporting.googleapis.com}" - - fcr_mktemp FILE_UPLOAD_LOCATION_PLIST META_UPLOAD_RESULT_PLIST - - if ((VERBOSE > 2)); then - CURLOPT='--trace-ascii /dev/fd/2' - elif ((VERBOSE > 1)); then - CURLOPT='--verbose' - else - CURLOPT='' - fi - - for FILE; do - xcdebug "Get signed URL for uploading." - - URL="${FCR_BASE_URL}/v1/apps/${FIREBASE_APP_ID}" - - HTTP_STATUS="$(curl ${CURLOPT} -o "${FILE_UPLOAD_LOCATION_PLIST}" -sL -H "X-Ios-Bundle-Identifier: ${FCR_BUNDLE_ID}" -H "Authorization: Bearer ${BEARER_TOKEN}" -X POST -d '' -w '%{http_code}' "${URL}/symbolFileUploadLocation?key=${FIREBASE_API_KEY}")" - STATUS=$? - - if [[ "${STATUS}" == 22 && "${HTTP_STATUS}" == 403 ]]; then - xcerror "Unable to access resource. Token invalid." - xcnote "Please verify the service account file." - return 2 - elif [[ "${STATUS}" != 0 ]]; then - xcerror "curl exited with non-zero status ${STATUS}." - ((STATUS == 22)) && xcerror "HTTP response code is ${HTTP_STATUS}." - return 2 - fi - - /usr/bin/plutil -convert binary1 "${FILE_UPLOAD_LOCATION_PLIST}" || return 1 - - UPLOAD_KEY="$(property uploadKey "${FILE_UPLOAD_LOCATION_PLIST}")" - UPLOAD_URL="$(property uploadUrl "${FILE_UPLOAD_LOCATION_PLIST}")" - ERRMSG="$(property error:message "${FILE_UPLOAD_LOCATION_PLIST}")" - - if [[ "${ERRMSG}" ]]; then - if ((VERBOSE)); then - xcnote "Server response:" - /usr/bin/plutil -p "${FILE_UPLOAD_LOCATION_PLIST}" >&2 - fi - xcerror "symbolFileUploadLocation: ${ERRMSG}" - xcnote "symbolFileUploadLocation: Failed to get upload location." - return 1 - fi - - xcdebug "Upload symbol file." - - HTTP_STATUS=$(curl ${CURLOPT} -sfL -H 'Content-Type: text/plain' -H "Authorization: Bearer ${BEARER_TOKEN}" -w '%{http_code}' -T "${FILE}" "${UPLOAD_URL}") - STATUS=$? - - if ((STATUS == 22)); then # exit code 22 is a non-successful HTTP response - xcerror "upload: Unable to upload symbol file (HTTP Status ${HTTP_STATUS})." - return 1 - elif ((STATUS != 0)); then - xcerror "upload: Unable to upload symbol file (reason unknown)." - return 1 - fi - - xcdebug "Upload metadata information." - - curl ${CURLOPT} -sL -H 'Content-Type: application/json' -H "X-Ios-Bundle-Identifier: ${FCR_BUNDLE_ID}" -H "Authorization: Bearer ${BEARER_TOKEN}" -X POST -d '{"upload_key":"'"${UPLOAD_KEY}"'","symbol_file_mapping":{"symbol_type":2,"app_version":"'"${FCR_PROD_VERS}"'"}}' "${URL}/symbolFileMappings:upsert?key=${FIREBASE_API_KEY}" >|"${META_UPLOAD_RESULT_PLIST}" || return 1 - /usr/bin/plutil -convert binary1 "${META_UPLOAD_RESULT_PLIST}" || return 1 - - ERRMSG="$(property error:message "${META_UPLOAD_RESULT_PLIST}")" - - if [[ "${ERRMSG}" ]]; then - xcerror "symbolFileMappings:upsert: ${ERRMSG}" - xcnote "symbolFileMappings:upsert: The metadata for the symbol file failed to update." - return 1 - fi - done -} diff --git a/ZipBuilder/Template/Crash/upload-sym.sh b/ZipBuilder/Template/Crash/upload-sym.sh deleted file mode 100755 index 5fc7d1a2b3e..00000000000 --- a/ZipBuilder/Template/Crash/upload-sym.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Copyright 2019 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. - -echo "$0:0: error: $0 has been removed. Please use upload-sym instead." -exit 1 diff --git a/ZipBuilder/Template/Firebase.h b/ZipBuilder/Template/Firebase.h index e7b4d649be6..9420658a2f2 100644 --- a/ZipBuilder/Template/Firebase.h +++ b/ZipBuilder/Template/Firebase.h @@ -36,10 +36,6 @@ Firebase services work as intended." #import #endif -#if __has_include() -#import -#endif - #if __has_include() #import #endif @@ -64,10 +60,6 @@ Firebase services work as intended." #import #endif -#if __has_include() -#import -#endif - #if __has_include() #import #endif From 26e358ffc22e3e65c84973f897a7b0c918e7fdd9 Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Tue, 2 Apr 2019 13:21:59 -0400 Subject: [PATCH 128/214] Make fuzzing compile (#2693) --- Firestore/fuzzing/leveldb_fuzzer.cc | 18 +++++++++--------- Firestore/fuzzing/serializer_fuzzer.cc | 4 +++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Firestore/fuzzing/leveldb_fuzzer.cc b/Firestore/fuzzing/leveldb_fuzzer.cc index 8533ec69008..6afd83a1c8a 100644 --- a/Firestore/fuzzing/leveldb_fuzzer.cc +++ b/Firestore/fuzzing/leveldb_fuzzer.cc @@ -61,7 +61,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { LevelDbMutationKey key; - key.Decode(str); + (void)key.Decode(str); } catch (...) { // Ignore caught errors and assertions. } @@ -75,7 +75,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { LevelDbDocumentMutationKey key; - key.Decode(str); + (void)key.Decode(str); } catch (...) { // Ignore caught errors and assertions. } @@ -89,7 +89,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { LevelDbMutationQueueKey key; - key.Decode(slice); + (void)key.Decode(str); } catch (...) { // Ignore caught errors and assertions. } @@ -97,7 +97,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Test LevelDbTargetGlobalKey methods. try { LevelDbTargetGlobalKey key; - key.Decode(slice); + (void)key.Decode(slice); } catch (...) { // ignore caught errors and assertions. } @@ -105,7 +105,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Test LevelDbTargetKey methods. try { LevelDbTargetKey key; - key.Decode(slice); + (void)key.Decode(slice); } catch (...) { // ignore caught errors and assertions. } @@ -119,7 +119,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { LevelDbQueryTargetKey key; - key.Decode(str); + (void)key.Decode(str); } catch (...) { // Ignore caught errors and assertions. } @@ -127,7 +127,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Test LevelDbTargetDocumentKey methods. try { LevelDbTargetDocumentKey key; - key.Decode(str); + (void)key.Decode(str); } catch (...) { // Ignore caught errors and assertions. } @@ -142,7 +142,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { LevelDbDocumentTargetKey key; - key.Decode(str); + (void)key.Decode(str); } catch (...) { // Ignore caught errors and assertions. } @@ -157,7 +157,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { LevelDbRemoteDocumentKey key; - key.Decode(str); + (void)key.Decode(str); } catch (...) { // Ignore caught errors and assertions. } diff --git a/Firestore/fuzzing/serializer_fuzzer.cc b/Firestore/fuzzing/serializer_fuzzer.cc index 4657ded5e08..a4d476a394a 100644 --- a/Firestore/fuzzing/serializer_fuzzer.cc +++ b/Firestore/fuzzing/serializer_fuzzer.cc @@ -30,7 +30,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { // Try to decode the received data using the serializer. Reader reader = Reader::Wrap(data, size); - auto val = serializer.DecodeFieldValue(&reader); + (void)reader; + // TODO(varconst): reenable this test + // auto val = serializer.DecodeFieldValue(&reader); } catch (...) { // Ignore caught errors and assertions because fuzz testing is looking for // crashes and memory errors. From aedc47c942853c62535901830656ca6442ce3804 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Tue, 2 Apr 2019 22:33:48 -0400 Subject: [PATCH 129/214] Add quick instructions for building the Zip. (#2545) * Add quick instructions for building the Zip. Added some instructions to build the zip. Will expand further with releasing information. * Further argument instructions, formatting. * Split out required arguments from optional arguments. * Remove trailing whitespace. --- ZipBuilder/README.md | 69 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/ZipBuilder/README.md b/ZipBuilder/README.md index 8759b101857..54586f73e46 100644 --- a/ZipBuilder/README.md +++ b/ZipBuilder/README.md @@ -1,7 +1,74 @@ # Firebase Zip File Builder This project builds the Firebase iOS Zip file for distribution. -More instructions to come. + +## Overview + +This is a small Swift Package Manager project that allows users to package a Firebase iOS Zip file. With no launch +arguments, it will use the most recent public versions of all SDKs included in the zip file. + +It was designed to fail fast with an explanation of what went wrong, so you can fix issues or dig in without having to dig +too deep into the code. + +## Requirements + +In order to build the Zip file, you will need: + +- Xcode 10.1 +- CocoaPods +- An internet connection to fetch CocoaPods + +## Running the Tool + +You can run the tool with `swift run ZipBuilder [ARGS]` or generate an Xcode project with +`swift package generate-xcodeproj` and run within Xcode. + +In the near future, releases will be built via a builder server instead of on the release engineer's machine, making these +instructions more of a reference to understand what's going on instead of how to build it yourself. + +## Launch Arguments + +See `main.swift` and the `LaunchArgs` struct for information on specific launch arguments. + +You can pass in launch arguments with Xcode by clicking "ZipBuilder" beside the Run/Stop buttons, clicking "Edit +Scheme" and adding them in the "Arguments Passed On Launch" section. + +### Common Arguments + +These arguments assume you're running the command from the `ZipBuilder` directory. + +**Required** arguments: +- `-templateDir $(pwd)/Template` + - This should always be the same. +- `-coreDiagnosticsDir ` + - Needed to overwrite the existing Core Diagnostics framework. + +Optional comon arguments: +- `-updatePodRepo false` + - This is for speedups when `pod repo update` has already been run recently. + +For release engineers (Googlers packaging an upcoming Firebase release) these commands should also be used: +- `-customSpecRepos sso://cpdc-internal/firebase` + - This pulls the latest podspecs from the CocoaPods staging area. +- `-releasingSDKs ` and +- `-existingVersions ` + - Validates the version numbers fetched from CocoaPods staging against the expected released versions from these + textprotos. + +Putting them all together, here's a common command to build a releaseable Zip file: + +``` +swift run ZipBuilder -templateDir $(pwd)/Template -updatePodRepo false \ +-coreDiagnosticsDir /private/tmp/tmpUqBxKN/FirebaseCoreDiagnostics.framework \ +-releasingSDKs \ +-existingVersions \ +-customSpecRepos sso://cpdc-internal/firebase +``` + +## Debugging + +You can generate an Xcode project for the tool by running `swift package generate-xcodeproj` in this directory. +See the above instructions for adding Launch Arguments to the Xcode build. ## Priorities From 8cc5d652ec237a8da655696e5c1003f2c07f0cc8 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Tue, 2 Apr 2019 23:39:28 -0400 Subject: [PATCH 130/214] GULAppDelegateSwizzler - remote notifications methods and tvOS support (#2698) --- .../GULAppDelegateSwizzler.m | 244 ++++++++++++++++-- GoogleUtilities/Common/GULLoggerCodes.h | 29 ++- .../GoogleUtilities.xcodeproj/project.pbxproj | 6 +- GoogleUtilities/Example/Podfile | 20 +- .../Swizzler/GULAppDelegateSwizzlerTest.m | 193 +++++++++++++- 5 files changed, 450 insertions(+), 42 deletions(-) diff --git a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m index 8bbabad3778..368e7b71dbc 100644 --- a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m +++ b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m @@ -14,7 +14,7 @@ #import "TargetConditionals.h" -#if TARGET_OS_IOS +#if TARGET_OS_IOS || TARGET_OS_TV #import #import @@ -47,14 +47,39 @@ typedef BOOL (*GULRealContinueUserActivityIMP)( id, SEL, UIApplication *, NSUserActivity *, void (^)(NSArray *restorableObjects)); #pragma clang diagnostic pop +typedef void (*GULRealDidRegisterForRemoteNotificationsIMP)(id, SEL, UIApplication *, NSData *); + +typedef void (*GULRealDidFailToRegisterForRemoteNotificationsIMP)(id, + SEL, + UIApplication *, + NSError *); + +typedef void (*GULRealDidReceiveRemoteNotificationIMP)(id, SEL, UIApplication *, NSDictionary *); + +typedef void (*GULRealDidReceiveRemoteNotificationWithCompletionIMP)( + id, SEL, UIApplication *, NSDictionary *, void (^)(UIBackgroundFetchResult)); + typedef void (^GULAppDelegateInterceptorCallback)(id); // The strings below are the keys for associated objects. static char const *const kGULContinueUserActivityIMPKey = "GUL_continueUserActivityIMP"; static char const *const kGULHandleBackgroundSessionIMPKey = "GUL_handleBackgroundSessionIMP"; static char const *const kGULOpenURLOptionsIMPKey = "GUL_openURLOptionsIMP"; + +static char const *const kGULRealDidRegisterForRemoteNotificationsIMPKey = + "GUL_didRegisterForRemoteNotificationsIMP"; +static char const *const kGULRealDidFailToRegisterForRemoteNotificationsIMPKey = + "GUL_didFailToRegisterForRemoteNotificationsIMP"; +static char const *const kGULRealDidReceiveRemoteNotificationIMPKey = + "GUL_didReceiveRemoteNotificationIMP"; +static char const *const kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey = + "GUL_didReceiveRemoteNotificationWithCompletionIMP"; +#if TARGET_OS_IOS +// The method application:openURL:sourceApplication:annotation: is not available on tvOS static char const *const kGULOpenURLOptionsSourceAnnotationsIMPKey = "GUL_openURLSourceApplicationAnnotationIMP"; +#endif // TARGET_OS_IOS + static char const *const kGULRealClassKey = "GUL_realClass"; static NSString *const kGULAppDelegateKeyPath = @"delegate"; @@ -333,19 +358,6 @@ + (void)createSubclassWithObject:(id)anObject { fromClass:realClass]; NSValue *continueUserActivityIMPPointer = [NSValue valueWithPointer:continueUserActivityIMP]; - // For application:openURL:sourceApplication:annotation: - SEL openURLSourceApplicationAnnotationSEL = @selector(application: - openURL:sourceApplication:annotation:); - [GULAppDelegateSwizzler addInstanceMethodWithSelector:openURLSourceApplicationAnnotationSEL - fromClass:[GULAppDelegateSwizzler class] - toClass:appDelegateSubClass]; - GULRealOpenURLSourceApplicationAnnotationIMP openURLSourceApplicationAnnotationIMP = - (GULRealOpenURLSourceApplicationAnnotationIMP)[GULAppDelegateSwizzler - implementationOfMethodSelector:openURLSourceApplicationAnnotationSEL - fromClass:realClass]; - NSValue *openURLSourceAppAnnotationIMPPointer = - [NSValue valueWithPointer:openURLSourceApplicationAnnotationIMP]; - // For application:handleEventsForBackgroundURLSession:completionHandler: SEL handleEventsForBackgroundURLSessionSEL = @selector(application: handleEventsForBackgroundURLSession:completionHandler:); @@ -359,6 +371,83 @@ + (void)createSubclassWithObject:(id)anObject { NSValue *handleBackgroundSessionIMPPointer = [NSValue valueWithPointer:handleBackgroundSessionIMP]; + // For application:didRegisterForRemoteNotificationsWithDeviceToken: + SEL didRegisterForRemoteNotificationsSEL = @selector(application: + didRegisterForRemoteNotificationsWithDeviceToken:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:didRegisterForRemoteNotificationsSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidRegisterForRemoteNotificationsIMP didRegisterForRemoteNotificationsIMP = + (GULRealDidRegisterForRemoteNotificationsIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:didRegisterForRemoteNotificationsSEL + fromClass:realClass]; + NSValue *didRegisterForRemoteNotificationsIMPPointer = + [NSValue valueWithPointer:didRegisterForRemoteNotificationsIMP]; + + // For application:didFailToRegisterForRemoteNotificationsWithError: + SEL didFailToRegisterForRemoteNotificationsSEL = @selector(application: + didFailToRegisterForRemoteNotificationsWithError:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:didFailToRegisterForRemoteNotificationsSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidFailToRegisterForRemoteNotificationsIMP didFailToRegisterForRemoteNotificationsIMP = + (GULRealDidFailToRegisterForRemoteNotificationsIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:didFailToRegisterForRemoteNotificationsSEL + fromClass:realClass]; + NSValue *didFailToRegisterForRemoteNotificationsIMPPointer = + [NSValue valueWithPointer:didFailToRegisterForRemoteNotificationsIMP]; + + // For application:didReceiveRemoteNotification: + SEL didReceiveRemoteNotificationSEL = @selector(application:didReceiveRemoteNotification:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:didReceiveRemoteNotificationSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidReceiveRemoteNotificationIMP didReceiveRemoteNotificationIMP = + (GULRealDidReceiveRemoteNotificationIMP) + [GULAppDelegateSwizzler implementationOfMethodSelector:didReceiveRemoteNotificationSEL + fromClass:realClass]; + NSValue *didReceiveRemoteNotificationIMPPointer = + [NSValue valueWithPointer:didReceiveRemoteNotificationIMP]; + + // For application:didReceiveRemoteNotification:fetchCompletionHandler: + NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer; + SEL didReceiveRemoteNotificationWithCompletionSEL = + @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); + if ([anObject respondsToSelector:didReceiveRemoteNotificationWithCompletionSEL]) { + // Only add the application:didReceiveRemoteNotification:fetchCompletionHandler: method if + // the original AppDelegate implements it. + // This fixes a bug if an app only implements application:didReceiveRemoteNotification: + // (if we add the method with completion, iOS sees that one exists and does not call + // the method without the completion, which in this case is the only one the app implements). + + [GULAppDelegateSwizzler + addInstanceMethodWithSelector:didReceiveRemoteNotificationWithCompletionSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidReceiveRemoteNotificationWithCompletionIMP + didReceiveRemoteNotificationWithCompletionIMP = + (GULRealDidReceiveRemoteNotificationWithCompletionIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:didReceiveRemoteNotificationWithCompletionSEL + fromClass:realClass]; + didReceiveRemoteNotificationWithCompletionIMPPointer = + [NSValue valueWithPointer:didReceiveRemoteNotificationWithCompletionIMP]; + } + +#if TARGET_OS_IOS + // For application:openURL:sourceApplication:annotation: + SEL openURLSourceApplicationAnnotationSEL = @selector(application: + openURL:sourceApplication:annotation:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:openURLSourceApplicationAnnotationSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealOpenURLSourceApplicationAnnotationIMP openURLSourceApplicationAnnotationIMP = + (GULRealOpenURLSourceApplicationAnnotationIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:openURLSourceApplicationAnnotationSEL + fromClass:realClass]; + NSValue *openURLSourceAppAnnotationIMPPointer = + [NSValue valueWithPointer:openURLSourceApplicationAnnotationIMP]; +#endif // TARGET_OS_IOS + // Override the description too so the custom class name will not show up. [GULAppDelegateSwizzler addInstanceMethodWithDestinationSelector:@selector(description) withImplementationFromSourceSelector:@selector(fakeDescription) @@ -374,8 +463,24 @@ + (void)createSubclassWithObject:(id)anObject { objc_setAssociatedObject(anObject, &kGULOpenURLOptionsIMPKey, openURLOptionsIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } + + objc_setAssociatedObject(anObject, &kGULRealDidRegisterForRemoteNotificationsIMPKey, + didRegisterForRemoteNotificationsIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(anObject, &kGULRealDidFailToRegisterForRemoteNotificationsIMPKey, + didFailToRegisterForRemoteNotificationsIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationIMPKey, + didReceiveRemoteNotificationIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey, + didReceiveRemoteNotificationWithCompletionIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +#if TARGET_OS_IOS objc_setAssociatedObject(anObject, &kGULOpenURLOptionsSourceAnnotationsIMPKey, openURLSourceAppAnnotationIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +#endif // TARGET_OS_IOS + objc_setAssociatedObject(anObject, &kGULRealClassKey, realClass, OBJC_ASSOCIATION_RETAIN_NONATOMIC); @@ -557,6 +662,8 @@ - (BOOL)application:(UIApplication *)application return returnedValue; } +#if TARGET_OS_IOS + - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication @@ -587,6 +694,8 @@ - (BOOL)application:(UIApplication *)application return returnedValue; } +#endif // TARGET_OS_IOS + #pragma mark - [Donor Methods] Network overridden handler methods #pragma clang diagnostic push @@ -647,7 +756,112 @@ - (BOOL)application:(UIApplication *)application } #pragma clang diagnostic pop +#pragma mark - [Donor Methods] Remote Notifications + +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + NSValue *didRegisterForRemoteNotificationsIMPPointer = + objc_getAssociatedObject(self, &kGULRealDidRegisterForRemoteNotificationsIMPKey); + GULRealDidRegisterForRemoteNotificationsIMP didRegisterForRemoteNotificationsIMP = + [didRegisterForRemoteNotificationsIMPPointer pointerValue]; + + // Notify interceptors. + SEL methodSelector = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); + [GULAppDelegateSwizzler + notifyInterceptorsWithMethodSelector:methodSelector + callback:^(id interceptor) { + [interceptor application:application + didRegisterForRemoteNotificationsWithDeviceToken: + deviceToken]; + }]; + // Call the real implementation if the real App Delegate has any. + if (didRegisterForRemoteNotificationsIMP) { + didRegisterForRemoteNotificationsIMP(self, methodSelector, application, deviceToken); + } +} + +- (void)application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + NSValue *didFailToRegisterForRemoteNotificationsIMPPointer = + objc_getAssociatedObject(self, &kGULRealDidFailToRegisterForRemoteNotificationsIMPKey); + GULRealDidFailToRegisterForRemoteNotificationsIMP didFailToRegisterForRemoteNotificationsIMP = + [didFailToRegisterForRemoteNotificationsIMPPointer pointerValue]; + + // Notify interceptors. + SEL methodSelector = @selector(application:didFailToRegisterForRemoteNotificationsWithError:); + [GULAppDelegateSwizzler + notifyInterceptorsWithMethodSelector:methodSelector + callback:^(id interceptor) { + [interceptor application:application + didFailToRegisterForRemoteNotificationsWithError:error]; + }]; + // Call the real implementation if the real App Delegate has any. + if (didFailToRegisterForRemoteNotificationsIMP) { + didFailToRegisterForRemoteNotificationsIMP(self, methodSelector, application, error); + } +} + +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer = + objc_getAssociatedObject(self, &kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey); + GULRealDidReceiveRemoteNotificationWithCompletionIMP + didReceiveRemoteNotificationWithCompletionIMP = + [didReceiveRemoteNotificationWithCompletionIMPPointer pointerValue]; + + // Notify interceptors. + SEL methodSelector = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); + [GULAppDelegateSwizzler + notifyInterceptorsWithMethodSelector:methodSelector + callback:^(id interceptor) { + [interceptor application:application + didReceiveRemoteNotification:userInfo + fetchCompletionHandler:completionHandler]; + }]; + // Call the real implementation if the real App Delegate has any. + if (didReceiveRemoteNotificationWithCompletionIMP) { + didReceiveRemoteNotificationWithCompletionIMP(self, methodSelector, application, userInfo, + completionHandler); + } +} + +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo { + NSValue *didReceiveRemoteNotificationIMPPointer = + objc_getAssociatedObject(self, &kGULRealDidReceiveRemoteNotificationIMPKey); + GULRealDidReceiveRemoteNotificationIMP didReceiveRemoteNotificationIMP = + [didReceiveRemoteNotificationIMPPointer pointerValue]; + + // Notify interceptors. + SEL methodSelector = @selector(application:didReceiveRemoteNotification:); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [GULAppDelegateSwizzler + notifyInterceptorsWithMethodSelector:methodSelector + callback:^(id interceptor) { + [interceptor application:application + didReceiveRemoteNotification:userInfo]; + }]; +#pragma clang diagnostic pop + // Call the real implementation if the real App Delegate has any. + if (didReceiveRemoteNotificationIMP) { + didReceiveRemoteNotificationIMP(self, methodSelector, application, userInfo); + } +} + + (void)proxyAppDelegate:(id)appDelegate { + if (![appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) { + GULLogNotice( + kGULLoggerSwizzler, NO, + [NSString + stringWithFormat:@"I-SWZ%06ld", + (long)kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate], + @"App Delegate does not conform to UIApplicationDelegate protocol. %@", + [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]); + return; + } + id originalDelegate = appDelegate; // Do not create a subclass if it is not enabled. if (![GULAppDelegateSwizzler isAppDelegateProxyEnabled]) { @@ -714,4 +928,4 @@ + (void)resetProxyOriginalDelegateOnceToken { @end -#endif // TARGET_OS_IOS +#endif // TARGET_OS_IOS || TARGET_OS_TV diff --git a/GoogleUtilities/Common/GULLoggerCodes.h b/GoogleUtilities/Common/GULLoggerCodes.h index b71c03797cd..fd22ba637a2 100644 --- a/GoogleUtilities/Common/GULLoggerCodes.h +++ b/GoogleUtilities/Common/GULLoggerCodes.h @@ -16,20 +16,21 @@ typedef NS_ENUM(NSInteger, GULSwizzlerMessageCode) { // App Delegate Swizzling. - kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000, // I-SWZ001000 - kGULSwizzlerMessageCodeAppDelegateSwizzling001 = 1001, // I-SWZ001001 - kGULSwizzlerMessageCodeAppDelegateSwizzling002 = 1002, // I-SWZ001002 - kGULSwizzlerMessageCodeAppDelegateSwizzling003 = 1003, // I-SWZ001003 - kGULSwizzlerMessageCodeAppDelegateSwizzling004 = 1004, // I-SWZ001004 - kGULSwizzlerMessageCodeAppDelegateSwizzling005 = 1005, // I-SWZ001005 - kGULSwizzlerMessageCodeAppDelegateSwizzling006 = 1006, // I-SWZ001006 - kGULSwizzlerMessageCodeAppDelegateSwizzling007 = 1007, // I-SWZ001007 - kGULSwizzlerMessageCodeAppDelegateSwizzling008 = 1008, // I-SWZ001008 - kGULSwizzlerMessageCodeAppDelegateSwizzling009 = 1009, // I-SWZ001009 - kGULSwizzlerMessageCodeAppDelegateSwizzling010 = 1010, // I-SWZ001010 - kGULSwizzlerMessageCodeAppDelegateSwizzling011 = 1011, // I-SWZ001011 - kGULSwizzlerMessageCodeAppDelegateSwizzling012 = 1012, // I-SWZ001012 - kGULSwizzlerMessageCodeAppDelegateSwizzling013 = 1013, // I-SWZ001013 + kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000, // I-SWZ001000 + kGULSwizzlerMessageCodeAppDelegateSwizzling001 = 1001, // I-SWZ001001 + kGULSwizzlerMessageCodeAppDelegateSwizzling002 = 1002, // I-SWZ001002 + kGULSwizzlerMessageCodeAppDelegateSwizzling003 = 1003, // I-SWZ001003 + kGULSwizzlerMessageCodeAppDelegateSwizzling004 = 1004, // I-SWZ001004 + kGULSwizzlerMessageCodeAppDelegateSwizzling005 = 1005, // I-SWZ001005 + kGULSwizzlerMessageCodeAppDelegateSwizzling006 = 1006, // I-SWZ001006 + kGULSwizzlerMessageCodeAppDelegateSwizzling007 = 1007, // I-SWZ001007 + kGULSwizzlerMessageCodeAppDelegateSwizzling008 = 1008, // I-SWZ001008 + kGULSwizzlerMessageCodeAppDelegateSwizzling009 = 1009, // I-SWZ001009 + kGULSwizzlerMessageCodeAppDelegateSwizzling010 = 1010, // I-SWZ001010 + kGULSwizzlerMessageCodeAppDelegateSwizzling011 = 1011, // I-SWZ001011 + kGULSwizzlerMessageCodeAppDelegateSwizzling012 = 1012, // I-SWZ001012 + kGULSwizzlerMessageCodeAppDelegateSwizzling013 = 1013, // I-SWZ001013 + kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate = 1014, // I-SWZ001014 // Method Swizzling. kGULSwizzlerMessageCodeMethodSwizzling000 = 2000, // I-SWZ002000 diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj index 08778f3f85c..30940fb98ea 100644 --- a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; + 9A414672225259F900B08D77 /* GULAppDelegateSwizzlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EFBE67F92101401100E756A7 /* GULAppDelegateSwizzlerTest.m */; }; 9A7C37C2224BD9C600033B0D /* GULAppDelegateSwizzlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EFBE67F92101401100E756A7 /* GULAppDelegateSwizzlerTest.m */; }; DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */; }; DE84BBC421D7EC900048A176 /* GULUserDefaultsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */; }; @@ -93,7 +94,7 @@ 6003F5AE195388D20070C39A /* Tests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; - 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GoogleUtilities.podspec; path = ../GoogleUtilities.podspec; sourceTree = ""; }; + 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GoogleUtilities.podspec; path = ../../GoogleUtilities.podspec; sourceTree = ""; }; DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULAppEnvironmentUtilTest.m; sourceTree = ""; }; DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULUserDefaultsTests.m; sourceTree = ""; }; DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULReachabilityCheckerTest.m; sourceTree = ""; }; @@ -671,6 +672,7 @@ DEC9786A20F6D66300014E20 /* GULMutableDictionaryTest.m in Sources */, DE84BBC621D7EC900048A176 /* GULUserDefaultsTests.m in Sources */, DEC9786C20F6D66700014E20 /* GULReachabilityCheckerTest.m in Sources */, + 9A414672225259F900B08D77 /* GULAppDelegateSwizzlerTest.m in Sources */, DEC9786820F6D65B00014E20 /* GULLoggerTest.m in Sources */, DEC9786D20F6D66B00014E20 /* GULAppEnvironmentUtilTest.m in Sources */, ); @@ -1101,6 +1103,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", + "$(SRCROOT)/../AppDelegateSwizzler/Internal", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -1131,6 +1134,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", + "$(SRCROOT)/../AppDelegateSwizzler/Internal", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/GoogleUtilities/Example/Podfile b/GoogleUtilities/Example/Podfile index aa230a65d94..e114b6fa8d2 100644 --- a/GoogleUtilities/Example/Podfile +++ b/GoogleUtilities/Example/Podfile @@ -9,16 +9,6 @@ target 'Example_iOS' do inherit! :search_paths pod 'OCMock' end - - post_install do |installer_representation| - installer_representation.pods_project.targets.each do |target| - target.build_configurations.each do |config| - if config.name != 'Release' - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'GUL_UNSWIZZLING_ENABLED=1', 'GUL_APP_DELEGATE_TESTING=1'] - end - end - end - end end target 'Example_macOS' do @@ -42,3 +32,13 @@ target 'Example_tvOS' do pod 'OCMock' end end + +post_install do |installer_representation| + installer_representation.pods_project.targets.each do |target| + target.build_configurations.each do |config| + if config.name != 'Release' + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'GUL_UNSWIZZLING_ENABLED=1', 'GUL_APP_DELEGATE_TESTING=1'] + end + end + end +end \ No newline at end of file diff --git a/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m b/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m index d2f8f1fbefe..5356fff334f 100644 --- a/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m +++ b/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m @@ -60,7 +60,17 @@ @interface GULTestAppDelegate : UIResponder { /** A URL property that is set by the app delegate methods, which is then used to verify if the app * delegate methods were properly called. */ -@property(nonatomic, copy) NSString *url; +@property(nonatomic, copy) NSURL *url; + +@property(nonatomic, strong) NSData *remoteNotificationsDeviceToken; +@property(nonatomic, strong) NSError *failToRegisterForRemoteNotificationsError; +@property(nonatomic, strong) NSDictionary *remoteNotification; +@property(nonatomic, copy) void (^remoteNotificationCompletionHandler)(UIBackgroundFetchResult); + +/** + * The application is set each time a UIApplicationDelegate method is called + */ +@property(nonatomic, weak) UIApplication *application; @end @@ -120,7 +130,8 @@ - (instancetype)init { - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { - _url = [url copy]; + self.application = app; + self.url = url; _isOpenURLOptionsMethodCalled = YES; return NO; } @@ -128,9 +139,39 @@ - (BOOL)application:(UIApplication *)app - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(nonnull NSString *)identifier completionHandler:(nonnull void (^)(void))completionHandler { + self.application = application; _backgroundSessionID = [identifier copy]; } +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + self.application = application; + self.remoteNotificationsDeviceToken = deviceToken; +} + +- (void)application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + self.application = application; + self.failToRegisterForRemoteNotificationsError = error; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo { + self.application = application; + self.remoteNotification = userInfo; +} +#pragma clang diagnostic pop + +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + self.application = application; + self.remoteNotification = userInfo; + self.remoteNotificationCompletionHandler = completionHandler; +} + // These are methods to test whether changing the class still maintains behavior that the app // delegate proxy shouldn't have modified. @@ -169,6 +210,7 @@ - (BOOL)application:(UIApplication *)app return YES; } +#if TARGET_OS_IOS - (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url sourceApplication:(nullable NSString *)sourceApplication @@ -176,6 +218,7 @@ - (BOOL)application:(UIApplication *)application _URLForIOS8 = [url copy]; return YES; } +#endif // TARGET_OS_IOS #if SDK_HAS_USERACTIVITY @@ -202,6 +245,12 @@ - (void)tearDown { [super tearDown]; } +- (void)testNotAppDelegateIsNotSwizzled { + NSObject *notAppDelegate = [[NSObject alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:(id)notAppDelegate]; + XCTAssertEqualObjects(NSStringFromClass([notAppDelegate class]), @"NSObject"); +} + /** Tests proxying an object that responds to UIApplicationDelegate protocol and makes sure that * it is isa swizzled and that the object after proxying responds to the expected methods * and doesn't have its ivars modified. @@ -235,14 +284,27 @@ - (void)testProxyAppDelegate { XCTAssertEqual(sizeBefore, sizeAfter); // After being proxied, it should be able to respond to the required method selector. +#if TARGET_OS_IOS XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application:openURL:sourceApplication:annotation:)]); +#endif // TARGET_OS_IOS + XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: continueUserActivity:restorationHandler:)]); XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application:openURL:options:)]); XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: handleEventsForBackgroundURLSession:completionHandler:)]); + XCTAssertTrue([realAppDelegate + respondsToSelector:@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); + XCTAssertTrue([realAppDelegate + respondsToSelector:@selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); + XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: + didReceiveRemoteNotification:)]); + XCTAssertTrue([realAppDelegate + respondsToSelector:@selector(application: + didReceiveRemoteNotification:fetchCompletionHandler:)]); + // Make sure that the class has changed. XCTAssertNotEqualObjects([realAppDelegate class], realAppDelegateClassBefore); @@ -458,6 +520,7 @@ - (void)testResultOfApplicationOpenURLOptionsIsORed { XCTAssertTrue(shouldOpen); } +#if TARGET_OS_IOS /** Tests that application:openURL:sourceApplication:annotation: is invoked on the interceptors if * it exists. */ @@ -540,6 +603,7 @@ - (void)testApplicationOpenURLSourceApplicationAnnotationResultIsORed { // The result is YES if one of the interceptors returns YES. XCTAssertTrue(shouldOpen); } +#endif // TARGET_OS_IOS /** Tests that application:handleEventsForBackgroundURLSession:completionHandler: is invoked on the * interceptors if it exists. @@ -648,6 +712,131 @@ - (void)testApplicationContinueUserActivityRestorationHandlerResultsAreORed { XCTAssertTrue(shouldContinueUserActvitiy); } +- (void)testApplicationDidRegisterForRemoteNotificationsIsInvokedOnInterceptors { + NSData *deviceToken = [NSData data]; + UIApplication *application = [UIApplication sharedApplication]; + + id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor application:application + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]); + + id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor2 application:application + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]); + + GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; + + [testAppDelegate application:application + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; + OCMVerifyAll(interceptor); + OCMVerifyAll(interceptor2); + + XCTAssertEqual(testAppDelegate.application, application); + XCTAssertEqual(testAppDelegate.remoteNotificationsDeviceToken, deviceToken); +} + +- (void)testApplicationDidFailToRegisterForRemoteNotificationsIsInvokedOnInterceptors { + NSError *error = [NSError errorWithDomain:@"test" code:-1 userInfo:nil]; + UIApplication *application = [UIApplication sharedApplication]; + + id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor application:application + didFailToRegisterForRemoteNotificationsWithError:error]); + + id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor2 application:application + didFailToRegisterForRemoteNotificationsWithError:error]); + + GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; + + [testAppDelegate application:application didFailToRegisterForRemoteNotificationsWithError:error]; + OCMVerifyAll(interceptor); + OCMVerifyAll(interceptor2); + + XCTAssertEqual(testAppDelegate.application, application); + XCTAssertEqual(testAppDelegate.failToRegisterForRemoteNotificationsError, error); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (void)testApplicationDidReceiveRemoteNotificationIsInvokedOnInterceptors { + NSDictionary *notification = @{}; + UIApplication *application = [UIApplication sharedApplication]; + + id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor application:application didReceiveRemoteNotification:notification]); + + id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor2 application:application didReceiveRemoteNotification:notification]); + + GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; + + [testAppDelegate application:application didReceiveRemoteNotification:notification]; + OCMVerifyAll(interceptor); + OCMVerifyAll(interceptor2); + + XCTAssertEqual(testAppDelegate.application, application); + XCTAssertEqual(testAppDelegate.remoteNotification, notification); +} +#pragma clang diagnostic pop + +- (void)testApplicationDidReceiveRemoteNotificationWithCompletionIsInvokedOnInterceptors { + NSDictionary *notification = @{}; + UIApplication *application = [UIApplication sharedApplication]; + void (^completion)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) { + }; + + id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor application:application + didReceiveRemoteNotification:notification + fetchCompletionHandler:completion]); + + id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor2 application:application + didReceiveRemoteNotification:notification + fetchCompletionHandler:completion]); + + GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; + + [testAppDelegate application:application + didReceiveRemoteNotification:notification + fetchCompletionHandler:completion]; + OCMVerifyAll(interceptor); + OCMVerifyAll(interceptor2); + + XCTAssertEqual(testAppDelegate.application, application); + XCTAssertEqual(testAppDelegate.remoteNotification, notification); + XCTAssertEqual(testAppDelegate.remoteNotificationCompletionHandler, completion); +} + +- (void)testApplicationDidReceiveRemoteNotificationWithCompletionImplementationIsNotAdded { + // The delegate without application:didReceiveRemoteNotification:fetchCompletionHandler: + // implementation + GULTestInterceptorAppDelegate *legacyDelegate = [[GULTestInterceptorAppDelegate alloc] init]; + + XCTAssertFalse([legacyDelegate + respondsToSelector:@selector(application: + didReceiveRemoteNotification:fetchCompletionHandler:)]); + + [GULAppDelegateSwizzler proxyAppDelegate:legacyDelegate]; + + XCTAssertFalse([legacyDelegate + respondsToSelector:@selector(application: + didReceiveRemoteNotification:fetchCompletionHandler:)]); +} + #pragma mark - Tests to test that Plist flag is honored /** Tests that app delegate proxy is enabled when there is no Info.plist dictionary. */ From c65ecf55c82e331e2dc997a15abccc6ee7e84680 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Wed, 3 Apr 2019 11:52:20 -0700 Subject: [PATCH 131/214] Port FIRDocumentChange to C++. (#2687) * Create C++ api::DocumentChange class. * Make FIRDocumentChange a wrapper over api::DocumentChange. * Moved documentChangesForSnapshot helper from FIRDocumentChange to where it's used in QuerySnapshot. I think this is cleaner. --- .../Tests/API/FIRQuerySnapshotTests.mm | 58 ++----- .../Source/API/FIRDocumentChange+Internal.h | 16 +- Firestore/Source/API/FIRDocumentChange.mm | 152 ++++-------------- Firestore/Source/API/FIRQuerySnapshot.mm | 16 +- .../firebase/firestore/api/document_change.h | 81 ++++++++++ .../firebase/firestore/api/document_change.mm | 37 +++++ .../firestore/api/document_reference.h | 2 - .../firebase/firestore/api/query_snapshot.h | 12 +- .../firebase/firestore/api/query_snapshot.mm | 87 ++++++++++ 9 files changed, 277 insertions(+), 184 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/api/document_change.h create mode 100644 Firestore/core/src/firebase/firestore/api/document_change.mm diff --git a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm index 9a45e8c388c..10384de4022 100644 --- a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm +++ b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm @@ -42,16 +42,6 @@ NS_ASSUME_NONNULL_BEGIN -@interface FIRDocumentChange () - -// Expose initializer for testing. -- (instancetype)initWithType:(FIRDocumentChangeType)type - document:(FIRQueryDocumentSnapshot *)document - oldIndex:(NSUInteger)oldIndex - newIndex:(NSUInteger)newIndex; - -@end - @interface FIRQuerySnapshotTests : XCTestCase @end @@ -91,56 +81,40 @@ - (void)testIncludeMetadataChanges { DocumentSet oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[ doc1Old, doc2Old ]); DocumentSet newDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[ doc2New, doc2New ]); std::vector documentChanges{ - DocumentViewChange{doc1New, DocumentViewChange::Type::kMetadata}, - DocumentViewChange{doc2New, DocumentViewChange::Type::kModified}, + DocumentViewChange(doc1New, DocumentViewChange::Type::kMetadata), + DocumentViewChange(doc2New, DocumentViewChange::Type::kModified), }; Firestore *firestore = FSTTestFirestore().wrapped; FSTQuery *query = FSTTestQuery("foo"); - ViewSnapshot viewSnapshot{query, - newDocuments, - oldDocuments, - std::move(documentChanges), - /*mutated_keys=*/DocumentKeySet{}, + ViewSnapshot viewSnapshot(query, newDocuments, oldDocuments, std::move(documentChanges), + /*mutated_keys=*/DocumentKeySet(), /*from_cache=*/false, /*sync_state_changed=*/true, - /*excludes_metadata_changes=*/false}; + /*excludes_metadata_changes=*/false); SnapshotMetadata metadata(/*pending_writes=*/false, /*from_cache=*/false); FIRQuerySnapshot *snapshot = [[FIRQuerySnapshot alloc] initWithFirestore:firestore originalQuery:query snapshot:std::move(viewSnapshot) metadata:std::move(metadata)]; - FIRQueryDocumentSnapshot *doc1Snap = - [[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore - documentKey:doc1New.key - document:doc1New - fromCache:false - hasPendingWrites:false]; - FIRQueryDocumentSnapshot *doc2Snap = - [[FIRQueryDocumentSnapshot alloc] initWithFirestore:firestore - documentKey:doc2New.key - document:doc2New - fromCache:false - hasPendingWrites:false]; + DocumentSnapshot doc1Snap(firestore, doc1New.key, doc1New, SnapshotMetadata()); + DocumentSnapshot doc2Snap(firestore, doc2New.key, doc2New, SnapshotMetadata()); NSArray *changesWithoutMetadata = @[ - [[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeModified - document:doc2Snap - oldIndex:1 - newIndex:1], + [[FIRDocumentChange alloc] + initWithDocumentChange:DocumentChange(DocumentChange::Type::Modified, doc2Snap, + /*old_index=*/1, /*new_index=*/1)], ]; XCTAssertEqualObjects(snapshot.documentChanges, changesWithoutMetadata); NSArray *changesWithMetadata = @[ - [[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeModified - document:doc1Snap - oldIndex:0 - newIndex:0], - [[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeModified - document:doc2Snap - oldIndex:1 - newIndex:1], + [[FIRDocumentChange alloc] + initWithDocumentChange:DocumentChange(DocumentChange::Type::Modified, doc1Snap, + /*old_index=*/0, /*new_index=*/0)], + [[FIRDocumentChange alloc] + initWithDocumentChange:DocumentChange(DocumentChange::Type::Modified, doc2Snap, + /*old_index=*/1, /*new_index=*/1)], ]; XCTAssertEqualObjects([snapshot documentChangesWithIncludeMetadataChanges:YES], changesWithMetadata); diff --git a/Firestore/Source/API/FIRDocumentChange+Internal.h b/Firestore/Source/API/FIRDocumentChange+Internal.h index c1c26904db8..6c79f543ec1 100644 --- a/Firestore/Source/API/FIRDocumentChange+Internal.h +++ b/Firestore/Source/API/FIRDocumentChange+Internal.h @@ -16,21 +16,17 @@ #import "FIRDocumentChange.h" -#include "Firestore/core/src/firebase/firestore/api/firestore.h" -#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" +#import -@class FIRFirestore; +#include "Firestore/core/src/firebase/firestore/api/document_change.h" + +using firebase::firestore::api::DocumentChange; NS_ASSUME_NONNULL_BEGIN -/** Internal FIRDocumentChange API we don't want exposed in our public header files. */ -@interface FIRDocumentChange (Internal) +@interface FIRDocumentChange (/* Init */) -/** Calculates the array of FIRDocumentChange's based on the given FSTViewSnapshot. */ -+ (NSArray *) - documentChangesForSnapshot:(const firebase::firestore::core::ViewSnapshot &)snapshot - includeMetadataChanges:(bool)includeMetadataChanges - firestore:(firebase::firestore::api::Firestore *)firestore; +- (instancetype)initWithDocumentChange:(DocumentChange &&)documentChange NS_DESIGNATED_INITIALIZER; @end diff --git a/Firestore/Source/API/FIRDocumentChange.mm b/Firestore/Source/API/FIRDocumentChange.mm index 1a5bccd20bb..7a561c44dd3 100644 --- a/Firestore/Source/API/FIRDocumentChange.mm +++ b/Firestore/Source/API/FIRDocumentChange.mm @@ -14,128 +14,24 @@ * limitations under the License. */ -#import "FIRDocumentChange.h" +#import "Firestore/Source/API/FIRDocumentChange+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" -#import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/Core/FSTQuery.h" -#import "Firestore/Source/Model/FSTDocument.h" -#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" -#include "Firestore/core/src/firebase/firestore/model/document_set.h" +#include "Firestore/core/src/firebase/firestore/api/document_change.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" -using firebase::firestore::api::Firestore; -using firebase::firestore::core::DocumentViewChange; -using firebase::firestore::core::ViewSnapshot; -using firebase::firestore::model::DocumentSet; +using firebase::firestore::api::DocumentChange; NS_ASSUME_NONNULL_BEGIN -@interface FIRDocumentChange () - -- (instancetype)initWithType:(FIRDocumentChangeType)type - document:(FIRDocumentSnapshot *)document - oldIndex:(NSUInteger)oldIndex - newIndex:(NSUInteger)newIndex NS_DESIGNATED_INITIALIZER; - -@end - -@implementation FIRDocumentChange (Internal) - -+ (FIRDocumentChangeType)documentChangeTypeForChange:(const DocumentViewChange &)change { - switch (change.type()) { - case DocumentViewChange::Type::kAdded: - return FIRDocumentChangeTypeAdded; - case DocumentViewChange::Type::kModified: - case DocumentViewChange::Type::kMetadata: - return FIRDocumentChangeTypeModified; - case DocumentViewChange::Type::kRemoved: - return FIRDocumentChangeTypeRemoved; - } - - HARD_FAIL("Unknown DocumentViewChange::Type: %s", change.type()); +@implementation FIRDocumentChange { + DocumentChange _documentChange; } -+ (NSArray *)documentChangesForSnapshot:(const ViewSnapshot &)snapshot - includeMetadataChanges:(bool)includeMetadataChanges - firestore:(Firestore *)firestore { - if (snapshot.old_documents().empty()) { - // Special case the first snapshot because index calculation is easy and fast. Also all changes - // on the first snapshot are adds so there are also no metadata-only changes to filter out. - FSTDocument *_Nullable lastDocument = nil; - NSUInteger index = 0; - NSMutableArray *changes = [NSMutableArray array]; - for (const DocumentViewChange &change : snapshot.document_changes()) { - FIRQueryDocumentSnapshot *document = [[FIRQueryDocumentSnapshot alloc] - initWithFirestore:firestore - documentKey:change.document().key - document:change.document() - fromCache:snapshot.from_cache() - hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)]; - HARD_ASSERT(change.type() == DocumentViewChange::Type::kAdded, - "Invalid event type for first snapshot"); - HARD_ASSERT(!lastDocument || snapshot.query().comparator(lastDocument, change.document()) == - NSOrderedAscending, - "Got added events in wrong order"); - [changes addObject:[[FIRDocumentChange alloc] initWithType:FIRDocumentChangeTypeAdded - document:document - oldIndex:NSNotFound - newIndex:index++]]; - } - return changes; - } else { - // A DocumentSet that is updated incrementally as changes are applied to use to lookup the index - // of a document. - DocumentSet indexTracker = snapshot.old_documents(); - NSMutableArray *changes = [NSMutableArray array]; - for (const DocumentViewChange &change : snapshot.document_changes()) { - if (!includeMetadataChanges && change.type() == DocumentViewChange::Type::kMetadata) { - continue; - } - - FIRQueryDocumentSnapshot *document = [[FIRQueryDocumentSnapshot alloc] - initWithFirestore:firestore - documentKey:change.document().key - document:change.document() - fromCache:snapshot.from_cache() - hasPendingWrites:snapshot.mutated_keys().contains(change.document().key)]; - - size_t oldIndex = DocumentSet::npos; - size_t newIndex = DocumentSet::npos; - if (change.type() != DocumentViewChange::Type::kAdded) { - oldIndex = indexTracker.IndexOf(change.document().key); - HARD_ASSERT(oldIndex != DocumentSet::npos, "Index for document not found"); - indexTracker = indexTracker.erase(change.document().key); - } - if (change.type() != DocumentViewChange::Type::kRemoved) { - indexTracker = indexTracker.insert(change.document()); - newIndex = indexTracker.IndexOf(change.document().key); - } - [FIRDocumentChange documentChangeTypeForChange:change]; - FIRDocumentChangeType type = [FIRDocumentChange documentChangeTypeForChange:change]; - [changes addObject:[[FIRDocumentChange alloc] initWithType:type - document:document - oldIndex:oldIndex - newIndex:newIndex]]; - } - return changes; - } -} - -@end - -@implementation FIRDocumentChange - -- (instancetype)initWithType:(FIRDocumentChangeType)type - document:(FIRQueryDocumentSnapshot *)document - oldIndex:(NSUInteger)oldIndex - newIndex:(NSUInteger)newIndex { +- (instancetype)initWithDocumentChange:(DocumentChange &&)documentChange { if (self = [super init]) { - _type = type; - _document = document; - _oldIndex = oldIndex; - _newIndex = newIndex; + _documentChange = std::move(documentChange); } return self; } @@ -145,16 +41,36 @@ - (BOOL)isEqual:(nullable id)other { if (![other isKindOfClass:[FIRDocumentChange class]]) return NO; FIRDocumentChange *change = (FIRDocumentChange *)other; - return self.type == change.type && [self.document isEqual:change.document] && - self.oldIndex == change.oldIndex && self.newIndex == change.newIndex; + return _documentChange == change->_documentChange; } - (NSUInteger)hash { - NSUInteger result = (NSUInteger)self.type; - result = result * 31u + [self.document hash]; - result = result * 31u + (NSUInteger)self.oldIndex; - result = result * 31u + (NSUInteger)self.newIndex; - return result; + return _documentChange.Hash(); +} + +- (FIRDocumentChangeType)type { + switch (_documentChange.type()) { + case DocumentChange::Type::Added: + return FIRDocumentChangeTypeAdded; + case DocumentChange::Type::Modified: + return FIRDocumentChangeTypeModified; + case DocumentChange::Type::Removed: + return FIRDocumentChangeTypeRemoved; + } + + HARD_FAIL("Unknown DocumentChange::Type: %s", _documentChange.type()); +} + +- (FIRQueryDocumentSnapshot *)document { + return [[FIRQueryDocumentSnapshot alloc] initWithSnapshot:_documentChange.document()]; +} + +- (NSUInteger)oldIndex { + return _documentChange.old_index(); +} + +- (NSUInteger)newIndex { + return _documentChange.new_index(); } @end diff --git a/Firestore/Source/API/FIRQuerySnapshot.mm b/Firestore/Source/API/FIRQuerySnapshot.mm index 05a9ad0c29f..7ff14860f3c 100644 --- a/Firestore/Source/API/FIRQuerySnapshot.mm +++ b/Firestore/Source/API/FIRQuerySnapshot.mm @@ -123,15 +123,15 @@ - (NSInteger)count { - (NSArray *)documentChangesWithIncludeMetadataChanges: (BOOL)includeMetadataChanges { - if (includeMetadataChanges && _snapshot->view_snapshot().excludes_metadata_changes()) { - ThrowInvalidArgument("To include metadata changes with your document changes, you must call " - "addSnapshotListener(includeMetadataChanges: true)."); - } - if (!_documentChanges || _documentChangesIncludeMetadataChanges != includeMetadataChanges) { - _documentChanges = [FIRDocumentChange documentChangesForSnapshot:_snapshot->view_snapshot() - includeMetadataChanges:includeMetadataChanges - firestore:_snapshot->firestore()]; + NSMutableArray *documentChanges = [NSMutableArray array]; + _snapshot->ForEachChange( + static_cast(includeMetadataChanges), [&documentChanges](DocumentChange change) { + [documentChanges + addObject:[[FIRDocumentChange alloc] initWithDocumentChange:std::move(change)]]; + }); + + _documentChanges = documentChanges; _documentChangesIncludeMetadataChanges = includeMetadataChanges; } return _documentChanges; diff --git a/Firestore/core/src/firebase/firestore/api/document_change.h b/Firestore/core/src/firebase/firestore/api/document_change.h new file mode 100644 index 00000000000..abc1d0d54a1 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/document_change.h @@ -0,0 +1,81 @@ +/* + * Copyright 2019 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_API_DOCUMENT_CHANGE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_DOCUMENT_CHANGE_H_ + +#include + +#include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" + +namespace firebase { +namespace firestore { +namespace api { + +class DocumentChange { + public: + enum class Type { Added, Modified, Removed }; + + DocumentChange() = default; + DocumentChange(Type type, + DocumentSnapshot document, + size_t old_index, + size_t new_index) + : type_(type), + document_(std::move(document)), + old_index_(old_index), + new_index_(new_index) { + } + + size_t Hash() const; + + Type type() const { + return type_; + } + + DocumentSnapshot document() const { + return document_; + } + + size_t old_index() const { + return old_index_; + } + + size_t new_index() const { + return new_index_; + } + + /** + * A sentinel return value for old_index() and new_index() indicating that + * there's no relevant index to return because the document was newly added + * or removed respectively. + */ + static constexpr size_t npos = static_cast(-1); + + private: + Type type_; + DocumentSnapshot document_; + size_t old_index_; + size_t new_index_; +}; + +bool operator==(const DocumentChange& lhs, const DocumentChange& rhs); + +} // namespace api +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_DOCUMENT_CHANGE_H_ diff --git a/Firestore/core/src/firebase/firestore/api/document_change.mm b/Firestore/core/src/firebase/firestore/api/document_change.mm new file mode 100644 index 00000000000..e6c2c485041 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/document_change.mm @@ -0,0 +1,37 @@ +/* + * Copyright 2019 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/api/document_change.h" + +#include "Firestore/core/src/firebase/firestore/util/hashing.h" + +namespace firebase { +namespace firestore { +namespace api { + +size_t DocumentChange::Hash() const { + return util::Hash(static_cast(type_), document_, old_index_, new_index_); +} + +bool operator==(const DocumentChange& lhs, const DocumentChange& rhs) { + return lhs.type() == rhs.type() && lhs.document() == rhs.document() && + lhs.old_index() == rhs.old_index() && + lhs.new_index() == rhs.new_index(); +} + +} // namespace api +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.h b/Firestore/core/src/firebase/firestore/api/document_reference.h index d107d452315..f9da6004cf6 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.h +++ b/Firestore/core/src/firebase/firestore/api/document_reference.h @@ -27,7 +27,6 @@ #include #include -#import "FIRDocumentReference.h" #import "FIRFirestoreSource.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" @@ -39,7 +38,6 @@ NS_ASSUME_NONNULL_BEGIN -@class FIRFirestore; @class FSTMutation; namespace firebase { diff --git a/Firestore/core/src/firebase/firestore/api/query_snapshot.h b/Firestore/core/src/firebase/firestore/api/query_snapshot.h index 7c5f49dde79..7495636c45c 100644 --- a/Firestore/core/src/firebase/firestore/api/query_snapshot.h +++ b/Firestore/core/src/firebase/firestore/api/query_snapshot.h @@ -26,6 +26,7 @@ #include #include +#include "Firestore/core/src/firebase/firestore/api/document_change.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/api/snapshot_metadata.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" @@ -76,10 +77,6 @@ class QuerySnapshot { return internal_query_; } - const core::ViewSnapshot& view_snapshot() const { - return snapshot_; - } - /** * Metadata about this snapshot, concerning its source and if it has local * modifications. @@ -92,6 +89,13 @@ class QuerySnapshot { void ForEachDocument( const std::function& callback) const; + /** + * Iterates over the `DocumentChanges` representing the changes between + * the prior snapshot and this one. + */ + void ForEachChange(bool include_metadata_changes, + const std::function& callback) const; + friend bool operator==(const QuerySnapshot& lhs, const QuerySnapshot& rhs); private: diff --git a/Firestore/core/src/firebase/firestore/api/query_snapshot.mm b/Firestore/core/src/firebase/firestore/api/query_snapshot.mm index c0269809a59..f14cbb148c0 100644 --- a/Firestore/core/src/firebase/firestore/api/query_snapshot.mm +++ b/Firestore/core/src/firebase/firestore/api/query_snapshot.mm @@ -25,9 +25,12 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocument.h" +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/objc_compatibility.h" +#include "absl/types/optional.h" NS_ASSUME_NONNULL_BEGIN @@ -37,6 +40,7 @@ namespace objc = util::objc; using api::Firestore; +using core::DocumentViewChange; using core::ViewSnapshot; using model::DocumentSet; @@ -63,6 +67,89 @@ DocumentSnapshot snap(firestore_, document.key, document, from_cache, } } +static DocumentChange::Type DocumentChangeTypeForChange( + const DocumentViewChange& change) { + switch (change.type()) { + case DocumentViewChange::Type::kAdded: + return DocumentChange::Type::Added; + case DocumentViewChange::Type::kModified: + case DocumentViewChange::Type::kMetadata: + return DocumentChange::Type::Modified; + case DocumentViewChange::Type::kRemoved: + return DocumentChange::Type::Removed; + } + + HARD_FAIL("Unknown DocumentViewChange::Type: %s", change.type()); +} + +void QuerySnapshot::ForEachChange( + bool include_metadata_changes, + const std::function& callback) const { + if (include_metadata_changes && snapshot_.excludes_metadata_changes()) { + ThrowInvalidArgument("To include metadata changes with your document " + "changes, you must call " + "addSnapshotListener(includeMetadataChanges:true)."); + } + + if (snapshot_.old_documents().empty()) { + // Special case the first snapshot because index calculation is easy and + // fast. Also all changes on the first snapshot are adds so there are also + // no metadata-only changes to filter out. + FSTDocument* last_document = nil; + size_t index = 0; + for (const DocumentViewChange& change : snapshot_.document_changes()) { + FSTDocument* doc = change.document(); + SnapshotMetadata metadata( + /*pending_writes=*/snapshot_.mutated_keys().contains(doc.key), + /*from_cache=*/snapshot_.from_cache()); + DocumentSnapshot document(firestore_, doc.key, doc, metadata); + + HARD_ASSERT(change.type() == DocumentViewChange::Type::kAdded, + "Invalid event type for first snapshot"); + HARD_ASSERT(!last_document || snapshot_.query().comparator( + last_document, change.document()) == + NSOrderedAscending, + "Got added events in wrong order"); + + callback(DocumentChange(DocumentChange::Type::Added, std::move(document), + DocumentChange::npos, index++)); + } + + } else { + // A DocumentSet that is updated incrementally as changes are applied to use + // to lookup the index of a document. + DocumentSet index_tracker = snapshot_.old_documents(); + for (const DocumentViewChange& change : snapshot_.document_changes()) { + if (!include_metadata_changes && + change.type() == DocumentViewChange::Type::kMetadata) { + continue; + } + + FSTDocument* doc = change.document(); + SnapshotMetadata metadata( + /*pending_writes=*/snapshot_.mutated_keys().contains(doc.key), + /*from_cache=*/snapshot_.from_cache()); + DocumentSnapshot document(firestore_, doc.key, doc, metadata); + + size_t old_index = DocumentChange::npos; + size_t new_index = DocumentChange::npos; + if (change.type() != DocumentViewChange::Type::kAdded) { + old_index = index_tracker.IndexOf(change.document().key); + HARD_ASSERT(old_index != DocumentSet::npos, + "Index for document not found"); + index_tracker = index_tracker.erase(change.document().key); + } + if (change.type() != DocumentViewChange::Type::kRemoved) { + index_tracker = index_tracker.insert(change.document()); + new_index = index_tracker.IndexOf(change.document().key); + } + + DocumentChange::Type type = DocumentChangeTypeForChange(change); + callback(DocumentChange(type, std::move(document), old_index, new_index)); + } + } +} + } // namespace api } // namespace firestore } // namespace firebase From b2cd981f23564eb7bc2f4bb2dd10504cabae005b Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Wed, 3 Apr 2019 15:05:07 -0400 Subject: [PATCH 132/214] Revert "GULLogger - count errors and warnigns" (#2714) * Revert "GULLogger: logged issue counting concurrency issue fix (#2627)" This reverts commit 961e9b0bfbbc84e3e1d942fa84e1a9b6ad0ec00c. * Revert "GULLogger - issue count synchronous getters (#2610)" This reverts commit c6da7d616149e171715ac85403095853a10bbcfc. * Revert "GULLogger - count errors and warnigns (#2601)" This reverts commit 1e08a55afd7295cd851ffac95a40d2b5ab55e306. --- .../Example/Tests/Logger/GULLoggerTest.m | 82 ++--------------- GoogleUtilities/Logger/GULLogger.m | 88 ------------------- GoogleUtilities/Logger/Private/GULLogger.h | 27 ------ 3 files changed, 9 insertions(+), 188 deletions(-) diff --git a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m index 62abd5aa9e1..9483bbcb5f9 100644 --- a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m +++ b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m @@ -32,15 +32,13 @@ extern BOOL getGULLoggerDebugMode(void); -extern CFStringRef getGULLoggerUserDefaultsSuiteName(void); -extern dispatch_queue_t getGULLoggerCounterQueue(void); - static NSString *const kMessageCode = @"I-COR000001"; @interface GULLoggerTest : XCTestCase @property(nonatomic) NSString *randomLogString; -@property(nonatomic) NSUserDefaults *loggerDefaults; + +@property(nonatomic, strong) NSUserDefaults *defaults; @end @@ -50,18 +48,14 @@ - (void)setUp { [super setUp]; GULResetLogger(); - self.loggerDefaults = [[NSUserDefaults alloc] - initWithSuiteName:CFBridgingRelease(getGULLoggerUserDefaultsSuiteName())]; + // Stub NSUserDefaults for cleaner testing. + _defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.google.logger_test"]; } - (void)tearDown { - // Make sure all async operations have finished before starting a new test. - [self drainQueue:getGULClientQueue()]; - [self drainQueue:getGULLoggerCounterQueue()]; - - self.loggerDefaults = nil; - [super tearDown]; + + _defaults = nil; } - (void)testMessageCodeFormat { @@ -159,61 +153,17 @@ - (void)testGULLoggerLevelValues { XCTAssertEqual(GULLoggerLevelDebug, ASL_LEVEL_DEBUG); } -- (void)testGetErrorWarningNumberBeforeLogDontCrash { - GULResetLogger(); - - XCTAssertNoThrow(GULNumberOfErrorsLogged()); - XCTAssertNoThrow(GULNumberOfWarningsLogged()); -} - -- (void)testErrorNumberIncrement { - [self.loggerDefaults setInteger:10 forKey:kGULLoggerErrorCountKey]; - - GULLogError(@"my service", NO, kMessageCode, @"Message."); - - [self drainQueue:getGULLoggerCounterQueue()]; - XCTAssertEqual(GULNumberOfErrorsLogged(), 11); -} - -- (void)testWarningNumberIncrement { - [self.loggerDefaults setInteger:5 forKey:kGULLoggerWarningCountKey]; - - GULLogWarning(@"my service", NO, kMessageCode, @"Message."); - - [self drainQueue:getGULLoggerCounterQueue()]; - XCTAssertEqual(GULNumberOfWarningsLogged(), 6); -} - -- (void)testResetIssuesCount { - [self.loggerDefaults setInteger:3 forKey:kGULLoggerErrorCountKey]; - [self.loggerDefaults setInteger:4 forKey:kGULLoggerWarningCountKey]; - - GULResetNumberOfIssuesLogged(); - - XCTAssertEqual(GULNumberOfErrorsLogged(), 0); - XCTAssertEqual(GULNumberOfWarningsLogged(), 0); -} - -- (void)testNumberOfIssuesLoggedNoDeadlock { - [self dispatchSyncNestedDispatchCount:100 - queue:getGULLoggerCounterQueue() - block:^{ - XCTAssertNoThrow(GULNumberOfErrorsLogged()); - XCTAssertNoThrow(GULNumberOfWarningsLogged()); - }]; -} - // Helper functions. - (BOOL)logExists { - [self drainQueue:getGULClientQueue()]; + [self drainGULClientQueue]; NSString *correctMsg = [NSString stringWithFormat:@"%@[%@] %@", @"my service", kMessageCode, self.randomLogString]; return [self messageWasLogged:correctMsg]; } -- (void)drainQueue:(dispatch_queue_t)queue { +- (void)drainGULClientQueue { dispatch_semaphore_t workerSemaphore = dispatch_semaphore_create(0); - dispatch_barrier_async(queue, ^{ + dispatch_async(getGULClientQueue(), ^{ dispatch_semaphore_signal(workerSemaphore); }); dispatch_semaphore_wait(workerSemaphore, DISPATCH_TIME_FOREVER); @@ -241,19 +191,5 @@ - (BOOL)messageWasLogged:(NSString *)message { #pragma clang pop } -- (void)dispatchSyncNestedDispatchCount:(NSInteger)count - queue:(dispatch_queue_t)queue - block:(dispatch_block_t)block { - if (count < 0) { - return; - } - - dispatch_sync(queue, ^{ - [self dispatchSyncNestedDispatchCount:count - 1 queue:queue block:block]; - block(); - NSLog(@"%@, depth: %ld", NSStringFromSelector(_cmd), (long)count); - }); -} - @end #endif diff --git a/GoogleUtilities/Logger/GULLogger.m b/GoogleUtilities/Logger/GULLogger.m index d92ec673372..495e5830bb0 100644 --- a/GoogleUtilities/Logger/GULLogger.m +++ b/GoogleUtilities/Logger/GULLogger.m @@ -19,9 +19,6 @@ #import #import "Public/GULLoggerLevel.h" -NSString *const kGULLoggerErrorCountKey = @"kGULLoggerErrorCountKey"; -NSString *const kGULLoggerWarningCountKey = @"kGULLoggerWarningCountKey"; - /// ASL client facility name used by GULLogger. const char *kGULLoggerASLClientFacilityName = "com.google.utilities.logger"; @@ -46,8 +43,6 @@ static NSRegularExpression *sMessageCodeRegex; #endif -void GULIncrementLogCountForLevel(GULLoggerLevel level); - void GULLoggerInitializeASL(void) { dispatch_once(&sGULLoggerOnceToken, ^{ NSInteger majorOSVersion = [[GULAppEnvironmentUtil systemVersion] integerValue]; @@ -154,10 +149,6 @@ void GULLogBasic(GULLoggerLevel level, NSString *message, va_list args_ptr) { GULLoggerInitializeASL(); - - // Keep count of how many errors and warnings are triggered. - GULIncrementLogCountForLevel(level); - if (!(level <= sGULLoggerMaximumLevel || sGULLoggerDebugMode || forceLog)) { return; } @@ -203,85 +194,6 @@ void GULLogBasic(GULLoggerLevel level, #undef GUL_MAKE_LOGGER -#pragma mark - User defaults - -// NSUserDefaults cannot be used due to a bug described in GULUserDefaults -// GULUserDefaults cannot be used because GULLogger is a dependency for GULUserDefaults -// We have to use C API direclty here - -CFStringRef getGULLoggerUserDefaultsSuiteName(void) { - return (__bridge CFStringRef) @"GoogleUtilities.Logger.GULLogger"; -} - -NSInteger GULGetUserDefaultsIntegerForKey(NSString *key) { - id value = (__bridge_transfer id)CFPreferencesCopyAppValue((__bridge CFStringRef)key, - getGULLoggerUserDefaultsSuiteName()); - if (![value isKindOfClass:[NSNumber class]]) { - return 0; - } - - return [(NSNumber *)value integerValue]; -} - -void GULLoggerUserDefaultsSetIntegerForKey(NSInteger count, NSString *key) { - NSNumber *countNumber = @(count); - CFPreferencesSetAppValue((__bridge CFStringRef)key, (__bridge CFNumberRef)countNumber, - getGULLoggerUserDefaultsSuiteName()); - CFPreferencesAppSynchronize(getGULLoggerUserDefaultsSuiteName()); -} - -#pragma mark - Number of errors and warnings - -dispatch_queue_t getGULLoggerCounterQueue(void) { - static dispatch_queue_t queue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = - dispatch_queue_create("GoogleUtilities.GULLogger.counterQueue", DISPATCH_QUEUE_CONCURRENT); - }); - - return queue; -} - -NSInteger GULSyncGetUserDefaultsIntegerForKey(NSString *key) { - __block NSInteger integerValue = 0; - dispatch_sync(getGULLoggerCounterQueue(), ^{ - integerValue = GULGetUserDefaultsIntegerForKey(key); - }); - - return integerValue; -} - -NSInteger GULNumberOfErrorsLogged(void) { - return GULSyncGetUserDefaultsIntegerForKey(kGULLoggerErrorCountKey); -} - -NSInteger GULNumberOfWarningsLogged(void) { - return GULSyncGetUserDefaultsIntegerForKey(kGULLoggerWarningCountKey); -} - -void GULResetNumberOfIssuesLogged(void) { - dispatch_barrier_async(getGULLoggerCounterQueue(), ^{ - GULLoggerUserDefaultsSetIntegerForKey(0, kGULLoggerErrorCountKey); - GULLoggerUserDefaultsSetIntegerForKey(0, kGULLoggerWarningCountKey); - }); -} - -void GULIncrementUserDefaultsIntegerForKey(NSString *key) { - NSInteger value = GULGetUserDefaultsIntegerForKey(key); - GULLoggerUserDefaultsSetIntegerForKey(value + 1, key); -} - -void GULIncrementLogCountForLevel(GULLoggerLevel level) { - dispatch_barrier_async(getGULLoggerCounterQueue(), ^{ - if (level == GULLoggerLevelError) { - GULIncrementUserDefaultsIntegerForKey(kGULLoggerErrorCountKey); - } else if (level == GULLoggerLevelWarning) { - GULIncrementUserDefaultsIntegerForKey(kGULLoggerWarningCountKey); - } - }); -} - #pragma mark - GULLoggerWrapper @implementation GULLoggerWrapper diff --git a/GoogleUtilities/Logger/Private/GULLogger.h b/GoogleUtilities/Logger/Private/GULLogger.h index 167f24c4caa..ff425768681 100644 --- a/GoogleUtilities/Logger/Private/GULLogger.h +++ b/GoogleUtilities/Logger/Private/GULLogger.h @@ -25,16 +25,6 @@ NS_ASSUME_NONNULL_BEGIN */ typedef NSString *const GULLoggerService; -/** - * The key used to store the logger's error count. - */ -extern NSString *const kGULLoggerErrorCountKey; - -/** - * The key used to store the logger's warning count. - */ -extern NSString *const kGULLoggerWarningCountKey; - #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -139,23 +129,6 @@ extern void GULLogDebug(GULLoggerService service, NSString *message, ...) NS_FORMAT_FUNCTION(4, 5); -/** - * Retrieve the number of errors that have been logged since the stat was last reset. - * Calling this method can be comparably expensive, so it should not be called from main thread. - */ -extern NSInteger GULNumberOfErrorsLogged(void); - -/** - * Retrieve the number of warnings that have been logged since the stat was last reset. - * Calling this method can be comparably expensive, so it should not be called from main thread. - */ -extern NSInteger GULNumberOfWarningsLogged(void); - -/** - * Reset number of errors and warnings that have been logged to 0. - */ -extern void GULResetNumberOfIssuesLogged(void); - #ifdef __cplusplus } // extern "C" #endif // __cplusplus From deac5de20b3780647096522c69a5bad524d0ed77 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Wed, 3 Apr 2019 13:06:58 -0700 Subject: [PATCH 133/214] Project restruct (#2712) --- Example/Firebase.xcodeproj/project.pbxproj | 28 ++++++------------- .../Email}/FIREmailAuthProvider.m | 0 .../Email}/FIREmailPasswordAuthCredential.h | 0 .../Email}/FIREmailPasswordAuthCredential.m | 0 .../{ => Auth Provider}/FIRAuthCredential.m | 0 .../FIRAuthCredential_Internal.h | 0 .../{ => Auth Provider}/FIRAuthProvider.m | 0 .../Facebook/FIRFacebookAuthCredential.h | 0 .../Facebook/FIRFacebookAuthCredential.m | 0 .../Facebook/FIRFacebookAuthProvider.m | 0 .../GameCenter/FIRGameCenterAuthCredential.h | 0 .../GameCenter/FIRGameCenterAuthCredential.m | 0 .../GameCenter/FIRGameCenterAuthProvider.m | 0 .../GitHub/FIRGitHubAuthCredential.h | 0 .../GitHub/FIRGitHubAuthCredential.m | 0 .../GitHub/FIRGitHubAuthProvider.m | 0 .../Google/FIRGoogleAuthCredential.h | 0 .../Google/FIRGoogleAuthCredential.m | 0 .../Google/FIRGoogleAuthProvider.m | 0 .../OAuth/FIROAuthCredential.m | 0 .../OAuth/FIROAuthCredential_Internal.h | 0 .../OAuth/FIROAuthProvider.m | 0 .../Phone/FIRPhoneAuthCredential.m | 0 .../Phone/FIRPhoneAuthCredential_Internal.h | 0 .../Phone/FIRPhoneAuthProvider.m | 0 .../Twitter/FIRTwitterAuthCredential.h | 0 .../Twitter/FIRTwitterAuthCredential.m | 0 .../Twitter/FIRTwitterAuthProvider.m | 0 .../Source/{ => Auth}/FIRActionCodeSettings.m | 0 Firebase/Auth/Source/{ => Auth}/FIRAuth.m | 4 +-- .../Source/{ => Auth}/FIRAuthDataResult.m | 0 .../{ => Auth}/FIRAuthDataResult_Internal.h | 0 .../Source/{ => Auth}/FIRAuthDispatcher.h | 0 .../Source/{ => Auth}/FIRAuthDispatcher.m | 0 .../{ => Auth}/FIRAuthGlobalWorkQueue.h | 0 .../{ => Auth}/FIRAuthGlobalWorkQueue.m | 0 .../Source/{ => Auth}/FIRAuthOperationType.h | 0 .../{ => Auth}/FIRAuthSerialTaskQueue.h | 0 .../{ => Auth}/FIRAuthSerialTaskQueue.m | 0 .../Auth/Source/{ => Auth}/FIRAuthSettings.m | 0 .../Source/{ => Auth}/FIRAuthTokenResult.m | 0 .../{ => Auth}/FIRAuthTokenResult_Internal.h | 0 .../Auth/Source/{ => Auth}/FIRAuth_Internal.h | 0 .../Source/{RPCs => Backend}/FIRAuthBackend.h | 0 .../Source/{RPCs => Backend}/FIRAuthBackend.m | 4 +-- .../{RPCs => Backend}/FIRAuthRPCRequest.h | 0 .../{RPCs => Backend}/FIRAuthRPCResponse.h | 0 .../FIRAuthRequestConfiguration.h | 0 .../FIRAuthRequestConfiguration.m | 0 .../FIRIdentityToolkitRequest.h | 0 .../FIRIdentityToolkitRequest.m | 0 .../RPC}/FIRCreateAuthURIRequest.h | 0 .../RPC}/FIRCreateAuthURIRequest.m | 0 .../RPC}/FIRCreateAuthURIResponse.h | 0 .../RPC}/FIRCreateAuthURIResponse.m | 0 .../RPC}/FIRDeleteAccountRequest.h | 0 .../RPC}/FIRDeleteAccountRequest.m | 0 .../RPC}/FIRDeleteAccountResponse.h | 0 .../RPC}/FIRDeleteAccountResponse.m | 0 .../RPC}/FIREmailLinkSignInRequest.h | 0 .../RPC}/FIREmailLinkSignInRequest.m | 0 .../RPC}/FIREmailLinkSignInResponse.h | 0 .../RPC}/FIREmailLinkSignInResponse.m | 0 .../RPC}/FIRGetAccountInfoRequest.h | 0 .../RPC}/FIRGetAccountInfoRequest.m | 0 .../RPC}/FIRGetAccountInfoResponse.h | 0 .../RPC}/FIRGetAccountInfoResponse.m | 0 .../RPC}/FIRGetOOBConfirmationCodeRequest.h | 0 .../RPC}/FIRGetOOBConfirmationCodeRequest.m | 0 .../RPC}/FIRGetOOBConfirmationCodeResponse.h | 0 .../RPC}/FIRGetOOBConfirmationCodeResponse.m | 0 .../RPC}/FIRGetProjectConfigRequest.h | 0 .../RPC}/FIRGetProjectConfigRequest.m | 0 .../RPC}/FIRGetProjectConfigResponse.h | 0 .../RPC}/FIRGetProjectConfigResponse.m | 0 .../RPC}/FIRResetPasswordRequest.h | 0 .../RPC}/FIRResetPasswordRequest.m | 0 .../RPC}/FIRResetPasswordResponse.h | 0 .../RPC}/FIRResetPasswordResponse.m | 0 .../RPC}/FIRSecureTokenRequest.h | 0 .../RPC}/FIRSecureTokenRequest.m | 0 .../RPC}/FIRSecureTokenResponse.h | 0 .../RPC}/FIRSecureTokenResponse.m | 0 .../RPC}/FIRSendVerificationCodeRequest.h | 0 .../RPC}/FIRSendVerificationCodeRequest.m | 0 .../RPC}/FIRSendVerificationCodeResponse.h | 0 .../RPC}/FIRSendVerificationCodeResponse.m | 0 .../RPC}/FIRSetAccountInfoRequest.h | 0 .../RPC}/FIRSetAccountInfoRequest.m | 0 .../RPC}/FIRSetAccountInfoResponse.h | 0 .../RPC}/FIRSetAccountInfoResponse.m | 0 .../RPC}/FIRSignInWithGameCenterRequest.h | 0 .../RPC}/FIRSignInWithGameCenterRequest.m | 0 .../RPC}/FIRSignInWithGameCenterResponse.h | 0 .../RPC}/FIRSignInWithGameCenterResponse.m | 0 .../RPC}/FIRSignUpNewUserRequest.h | 0 .../RPC}/FIRSignUpNewUserRequest.m | 0 .../RPC}/FIRSignUpNewUserResponse.h | 0 .../RPC}/FIRSignUpNewUserResponse.m | 0 .../RPC}/FIRVerifyAssertionRequest.h | 0 .../RPC}/FIRVerifyAssertionRequest.m | 0 .../RPC}/FIRVerifyAssertionResponse.h | 0 .../RPC}/FIRVerifyAssertionResponse.m | 0 .../RPC}/FIRVerifyClientRequest.h | 0 .../RPC}/FIRVerifyClientRequest.m | 0 .../RPC}/FIRVerifyClientResponse.h | 0 .../RPC}/FIRVerifyClientResponse.m | 0 .../RPC}/FIRVerifyCustomTokenRequest.h | 0 .../RPC}/FIRVerifyCustomTokenRequest.m | 0 .../RPC}/FIRVerifyCustomTokenResponse.h | 0 .../RPC}/FIRVerifyCustomTokenResponse.m | 0 .../RPC}/FIRVerifyPasswordRequest.h | 0 .../RPC}/FIRVerifyPasswordRequest.m | 0 .../RPC}/FIRVerifyPasswordResponse.h | 0 .../RPC}/FIRVerifyPasswordResponse.m | 0 .../RPC}/FIRVerifyPhoneNumberRequest.h | 0 .../RPC}/FIRVerifyPhoneNumberRequest.m | 0 .../RPC}/FIRVerifyPhoneNumberResponse.h | 0 .../RPC}/FIRVerifyPhoneNumberResponse.m | 0 .../Source/{ => Storage}/FIRAuthKeychain.h | 0 .../Source/{ => Storage}/FIRAuthKeychain.m | 0 .../FIRAuthUserDefaultsStorage.h | 0 .../FIRAuthUserDefaultsStorage.m | 0 .../{ => System Services}/FIRAuthAPNSToken.h | 0 .../{ => System Services}/FIRAuthAPNSToken.m | 0 .../FIRAuthAPNSTokenManager.h | 0 .../FIRAuthAPNSTokenManager.m | 0 .../FIRAuthAppCredential.h | 0 .../FIRAuthAppCredential.m | 0 .../FIRAuthAppCredentialManager.h | 0 .../FIRAuthAppCredentialManager.m | 0 .../FIRAuthNotificationManager.h | 0 .../FIRAuthNotificationManager.m | 0 .../FIRAuthStoredUserManager.h | 0 .../FIRAuthStoredUserManager.m | 0 .../FIRSecureTokenService.h | 0 .../FIRSecureTokenService.m | 0 .../Source/{ => User}/FIRAdditionalUserInfo.m | 0 .../FIRAdditionalUserInfo_Internal.h | 0 Firebase/Auth/Source/{ => User}/FIRUser.m | 2 +- .../Auth/Source/{ => User}/FIRUserInfoImpl.h | 0 .../Auth/Source/{ => User}/FIRUserInfoImpl.m | 0 .../Auth/Source/{ => User}/FIRUserMetadata.m | 0 .../{ => User}/FIRUserMetadata_Internal.h | 0 .../Auth/Source/{ => User}/FIRUser_Internal.h | 0 .../{ => Utilities}/FIRAuthAppDelegateProxy.h | 0 .../{ => Utilities}/FIRAuthAppDelegateProxy.m | 0 .../FIRAuthDefaultUIDelegate.h | 0 .../FIRAuthDefaultUIDelegate.m | 0 .../{ => Utilities}/FIRAuthErrorUtils.h | 0 .../{ => Utilities}/FIRAuthErrorUtils.m | 0 .../{ => Utilities}/FIRAuthExceptionUtils.h | 0 .../{ => Utilities}/FIRAuthExceptionUtils.m | 0 .../{ => Utilities}/FIRAuthInternalErrors.h | 0 .../{ => Utilities}/FIRAuthURLPresenter.h | 0 .../{ => Utilities}/FIRAuthURLPresenter.m | 0 .../Source/{ => Utilities}/FIRAuthWebUtils.h | 0 .../Source/{ => Utilities}/FIRAuthWebUtils.m | 0 .../Source/{ => Utilities}/FIRAuthWebView.h | 0 .../Source/{ => Utilities}/FIRAuthWebView.m | 0 .../FIRAuthWebViewController.h | 0 .../FIRAuthWebViewController.m | 0 .../Source/{ => Utilities}/NSData+FIRBase64.h | 0 .../Source/{ => Utilities}/NSData+FIRBase64.m | 0 164 files changed, 13 insertions(+), 25 deletions(-) rename Firebase/Auth/Source/{AuthProviders/EmailPassword => Auth Provider/Email}/FIREmailAuthProvider.m (100%) rename Firebase/Auth/Source/{AuthProviders/EmailPassword => Auth Provider/Email}/FIREmailPasswordAuthCredential.h (100%) rename Firebase/Auth/Source/{AuthProviders/EmailPassword => Auth Provider/Email}/FIREmailPasswordAuthCredential.m (100%) rename Firebase/Auth/Source/{ => Auth Provider}/FIRAuthCredential.m (100%) rename Firebase/Auth/Source/{ => Auth Provider}/FIRAuthCredential_Internal.h (100%) rename Firebase/Auth/Source/{ => Auth Provider}/FIRAuthProvider.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Facebook/FIRFacebookAuthCredential.h (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Facebook/FIRFacebookAuthCredential.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Facebook/FIRFacebookAuthProvider.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/GameCenter/FIRGameCenterAuthCredential.h (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/GameCenter/FIRGameCenterAuthCredential.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/GameCenter/FIRGameCenterAuthProvider.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/GitHub/FIRGitHubAuthCredential.h (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/GitHub/FIRGitHubAuthCredential.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/GitHub/FIRGitHubAuthProvider.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Google/FIRGoogleAuthCredential.h (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Google/FIRGoogleAuthCredential.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Google/FIRGoogleAuthProvider.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/OAuth/FIROAuthCredential.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/OAuth/FIROAuthCredential_Internal.h (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/OAuth/FIROAuthProvider.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Phone/FIRPhoneAuthCredential.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Phone/FIRPhoneAuthCredential_Internal.h (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Phone/FIRPhoneAuthProvider.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Twitter/FIRTwitterAuthCredential.h (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Twitter/FIRTwitterAuthCredential.m (100%) rename Firebase/Auth/Source/{AuthProviders => Auth Provider}/Twitter/FIRTwitterAuthProvider.m (100%) rename Firebase/Auth/Source/{ => Auth}/FIRActionCodeSettings.m (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuth.m (99%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthDataResult.m (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthDataResult_Internal.h (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthDispatcher.h (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthDispatcher.m (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthGlobalWorkQueue.h (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthGlobalWorkQueue.m (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthOperationType.h (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthSerialTaskQueue.h (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthSerialTaskQueue.m (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthSettings.m (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthTokenResult.m (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuthTokenResult_Internal.h (100%) rename Firebase/Auth/Source/{ => Auth}/FIRAuth_Internal.h (100%) rename Firebase/Auth/Source/{RPCs => Backend}/FIRAuthBackend.h (100%) rename Firebase/Auth/Source/{RPCs => Backend}/FIRAuthBackend.m (99%) rename Firebase/Auth/Source/{RPCs => Backend}/FIRAuthRPCRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend}/FIRAuthRPCResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend}/FIRAuthRequestConfiguration.h (100%) rename Firebase/Auth/Source/{RPCs => Backend}/FIRAuthRequestConfiguration.m (100%) rename Firebase/Auth/Source/{RPCs => Backend}/FIRIdentityToolkitRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend}/FIRIdentityToolkitRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRCreateAuthURIRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRCreateAuthURIRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRCreateAuthURIResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRCreateAuthURIResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRDeleteAccountRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRDeleteAccountRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRDeleteAccountResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRDeleteAccountResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIREmailLinkSignInRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIREmailLinkSignInRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIREmailLinkSignInResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIREmailLinkSignInResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetAccountInfoRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetAccountInfoRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetAccountInfoResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetAccountInfoResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetOOBConfirmationCodeRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetOOBConfirmationCodeRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetOOBConfirmationCodeResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetOOBConfirmationCodeResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetProjectConfigRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetProjectConfigRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetProjectConfigResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRGetProjectConfigResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRResetPasswordRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRResetPasswordRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRResetPasswordResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRResetPasswordResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSecureTokenRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSecureTokenRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSecureTokenResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSecureTokenResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSendVerificationCodeRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSendVerificationCodeRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSendVerificationCodeResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSendVerificationCodeResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSetAccountInfoRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSetAccountInfoRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSetAccountInfoResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSetAccountInfoResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSignInWithGameCenterRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSignInWithGameCenterRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSignInWithGameCenterResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSignInWithGameCenterResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSignUpNewUserRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSignUpNewUserRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSignUpNewUserResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRSignUpNewUserResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyAssertionRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyAssertionRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyAssertionResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyAssertionResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyClientRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyClientRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyClientResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyClientResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyCustomTokenRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyCustomTokenRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyCustomTokenResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyCustomTokenResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyPasswordRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyPasswordRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyPasswordResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyPasswordResponse.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyPhoneNumberRequest.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyPhoneNumberRequest.m (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyPhoneNumberResponse.h (100%) rename Firebase/Auth/Source/{RPCs => Backend/RPC}/FIRVerifyPhoneNumberResponse.m (100%) rename Firebase/Auth/Source/{ => Storage}/FIRAuthKeychain.h (100%) rename Firebase/Auth/Source/{ => Storage}/FIRAuthKeychain.m (100%) rename Firebase/Auth/Source/{ => Storage}/FIRAuthUserDefaultsStorage.h (100%) rename Firebase/Auth/Source/{ => Storage}/FIRAuthUserDefaultsStorage.m (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthAPNSToken.h (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthAPNSToken.m (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthAPNSTokenManager.h (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthAPNSTokenManager.m (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthAppCredential.h (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthAppCredential.m (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthAppCredentialManager.h (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthAppCredentialManager.m (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthNotificationManager.h (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthNotificationManager.m (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthStoredUserManager.h (100%) rename Firebase/Auth/Source/{ => System Services}/FIRAuthStoredUserManager.m (100%) rename Firebase/Auth/Source/{ => System Services}/FIRSecureTokenService.h (100%) rename Firebase/Auth/Source/{ => System Services}/FIRSecureTokenService.m (100%) rename Firebase/Auth/Source/{ => User}/FIRAdditionalUserInfo.m (100%) rename Firebase/Auth/Source/{ => User}/FIRAdditionalUserInfo_Internal.h (100%) rename Firebase/Auth/Source/{ => User}/FIRUser.m (99%) rename Firebase/Auth/Source/{ => User}/FIRUserInfoImpl.h (100%) rename Firebase/Auth/Source/{ => User}/FIRUserInfoImpl.m (100%) rename Firebase/Auth/Source/{ => User}/FIRUserMetadata.m (100%) rename Firebase/Auth/Source/{ => User}/FIRUserMetadata_Internal.h (100%) rename Firebase/Auth/Source/{ => User}/FIRUser_Internal.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthAppDelegateProxy.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthAppDelegateProxy.m (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthDefaultUIDelegate.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthDefaultUIDelegate.m (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthErrorUtils.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthErrorUtils.m (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthExceptionUtils.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthExceptionUtils.m (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthInternalErrors.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthURLPresenter.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthURLPresenter.m (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthWebUtils.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthWebUtils.m (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthWebView.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthWebView.m (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthWebViewController.h (100%) rename Firebase/Auth/Source/{ => Utilities}/FIRAuthWebViewController.m (100%) rename Firebase/Auth/Source/{ => Utilities}/NSData+FIRBase64.h (100%) rename Firebase/Auth/Source/{ => Utilities}/NSData+FIRBase64.m (100%) diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index d0b9a4c0ba5..3f3966a38a6 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -6137,9 +6137,7 @@ DEVELOPMENT_TEAM = ""; HEADER_SEARCH_PATHS = ( "$(inherited)", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/RPCs\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/AuthProviders\"", + "\"${PODS_ROOT}/../../Firebase/Auth/Source\"/**", ); INFOPLIST_FILE = "Auth/Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -6162,9 +6160,7 @@ DEVELOPMENT_TEAM = ""; HEADER_SEARCH_PATHS = ( "$(inherited)", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/RPCs\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/AuthProviders\"", + "\"${PODS_ROOT}/../../Firebase/Auth/Source\"/**", ); INFOPLIST_FILE = "Auth/Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -6669,7 +6665,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 4ANB9W7R3P; GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = "$SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist"; + INFOPLIST_FILE = $SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.4; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; @@ -6703,7 +6699,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 4ANB9W7R3P; GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = "$SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist"; + INFOPLIST_FILE = $SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.4; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; @@ -7308,9 +7304,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = ( "$(inherited)", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/RPCs\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/AuthProviders\"", + "\"${PODS_ROOT}/../../Firebase/Auth/Source\"/**", ); INFOPLIST_FILE = "$(SRCROOT)/Auth/App/iOS/Auth-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -7348,9 +7342,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = ( "$(inherited)", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/RPCs\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/AuthProviders\"", + "\"${PODS_ROOT}/../../Firebase/Auth/Source\"/**", ); INFOPLIST_FILE = "$(SRCROOT)/Auth/App/iOS/Auth-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -7560,9 +7552,7 @@ DEVELOPMENT_TEAM = ""; HEADER_SEARCH_PATHS = ( "$(inherited)", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/RPCs\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/AuthProviders\"", + "\"${PODS_ROOT}/../../Firebase/Auth/Source\"/**", "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = "Auth/Tests/Tests-Info.plist"; @@ -7586,9 +7576,7 @@ DEVELOPMENT_TEAM = ""; HEADER_SEARCH_PATHS = ( "$(inherited)", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/RPCs\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", - "\"${PODS_ROOT}/../../Firebase/Auth/Source/AuthProviders\"", + "\"${PODS_ROOT}/../../Firebase/Auth/Source\"/**", "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = "Auth/Tests/Tests-Info.plist"; diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m b/Firebase/Auth/Source/Auth Provider/Email/FIREmailAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/Email/FIREmailAuthProvider.m diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h b/Firebase/Auth/Source/Auth Provider/Email/FIREmailPasswordAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h rename to Firebase/Auth/Source/Auth Provider/Email/FIREmailPasswordAuthCredential.h diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m b/Firebase/Auth/Source/Auth Provider/Email/FIREmailPasswordAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/Email/FIREmailPasswordAuthCredential.m diff --git a/Firebase/Auth/Source/FIRAuthCredential.m b/Firebase/Auth/Source/Auth Provider/FIRAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/FIRAuthCredential.m diff --git a/Firebase/Auth/Source/FIRAuthCredential_Internal.h b/Firebase/Auth/Source/Auth Provider/FIRAuthCredential_Internal.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthCredential_Internal.h rename to Firebase/Auth/Source/Auth Provider/FIRAuthCredential_Internal.h diff --git a/Firebase/Auth/Source/FIRAuthProvider.m b/Firebase/Auth/Source/Auth Provider/FIRAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/FIRAuthProvider.m diff --git a/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.h b/Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.h rename to Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthCredential.h diff --git a/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.m b/Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthCredential.m diff --git a/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthProvider.m b/Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthProvider.m diff --git a/Firebase/Auth/Source/AuthProviders/GameCenter/FIRGameCenterAuthCredential.h b/Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/AuthProviders/GameCenter/FIRGameCenterAuthCredential.h rename to Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthCredential.h diff --git a/Firebase/Auth/Source/AuthProviders/GameCenter/FIRGameCenterAuthCredential.m b/Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/GameCenter/FIRGameCenterAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthCredential.m diff --git a/Firebase/Auth/Source/AuthProviders/GameCenter/FIRGameCenterAuthProvider.m b/Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/GameCenter/FIRGameCenterAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthProvider.m diff --git a/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.h b/Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.h rename to Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthCredential.h diff --git a/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.m b/Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthCredential.m diff --git a/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthProvider.m b/Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthProvider.m diff --git a/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.h b/Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.h rename to Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthCredential.h diff --git a/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.m b/Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthCredential.m diff --git a/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthProvider.m b/Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthProvider.m diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential.m b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential.m diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential_Internal.h b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential_Internal.h similarity index 100% rename from Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential_Internal.h rename to Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential_Internal.h diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential.m b/Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthCredential.m diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h b/Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthCredential_Internal.h similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h rename to Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthCredential_Internal.h diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m b/Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthProvider.m diff --git a/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.h b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.h rename to Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h diff --git a/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.m b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.m rename to Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m diff --git a/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthProvider.m b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthProvider.m rename to Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m diff --git a/Firebase/Auth/Source/FIRActionCodeSettings.m b/Firebase/Auth/Source/Auth/FIRActionCodeSettings.m similarity index 100% rename from Firebase/Auth/Source/FIRActionCodeSettings.m rename to Firebase/Auth/Source/Auth/FIRActionCodeSettings.m diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/Auth/FIRAuth.m similarity index 99% rename from Firebase/Auth/Source/FIRAuth.m rename to Firebase/Auth/Source/Auth/FIRAuth.m index 6a8151afb24..9c74a55f141 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/Auth/FIRAuth.m @@ -31,7 +31,7 @@ #import #import -#import "AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h" +#import "FIREmailPasswordAuthCredential.h" #import "FIRAdditionalUserInfo_Internal.h" #import "FIRAuthCredential_Internal.h" #import "FIRAuthDataResult_Internal.h" @@ -79,7 +79,7 @@ #import "FIRAuthAPNSTokenManager.h" #import "FIRAuthAppCredentialManager.h" #import "FIRAuthAppDelegateProxy.h" -#import "AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h" +#import "FIRPhoneAuthCredential_Internal.h" #import "FIRAuthNotificationManager.h" #import "FIRAuthURLPresenter.h" #endif diff --git a/Firebase/Auth/Source/FIRAuthDataResult.m b/Firebase/Auth/Source/Auth/FIRAuthDataResult.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthDataResult.m rename to Firebase/Auth/Source/Auth/FIRAuthDataResult.m diff --git a/Firebase/Auth/Source/FIRAuthDataResult_Internal.h b/Firebase/Auth/Source/Auth/FIRAuthDataResult_Internal.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthDataResult_Internal.h rename to Firebase/Auth/Source/Auth/FIRAuthDataResult_Internal.h diff --git a/Firebase/Auth/Source/FIRAuthDispatcher.h b/Firebase/Auth/Source/Auth/FIRAuthDispatcher.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthDispatcher.h rename to Firebase/Auth/Source/Auth/FIRAuthDispatcher.h diff --git a/Firebase/Auth/Source/FIRAuthDispatcher.m b/Firebase/Auth/Source/Auth/FIRAuthDispatcher.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthDispatcher.m rename to Firebase/Auth/Source/Auth/FIRAuthDispatcher.m diff --git a/Firebase/Auth/Source/FIRAuthGlobalWorkQueue.h b/Firebase/Auth/Source/Auth/FIRAuthGlobalWorkQueue.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthGlobalWorkQueue.h rename to Firebase/Auth/Source/Auth/FIRAuthGlobalWorkQueue.h diff --git a/Firebase/Auth/Source/FIRAuthGlobalWorkQueue.m b/Firebase/Auth/Source/Auth/FIRAuthGlobalWorkQueue.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthGlobalWorkQueue.m rename to Firebase/Auth/Source/Auth/FIRAuthGlobalWorkQueue.m diff --git a/Firebase/Auth/Source/FIRAuthOperationType.h b/Firebase/Auth/Source/Auth/FIRAuthOperationType.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthOperationType.h rename to Firebase/Auth/Source/Auth/FIRAuthOperationType.h diff --git a/Firebase/Auth/Source/FIRAuthSerialTaskQueue.h b/Firebase/Auth/Source/Auth/FIRAuthSerialTaskQueue.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthSerialTaskQueue.h rename to Firebase/Auth/Source/Auth/FIRAuthSerialTaskQueue.h diff --git a/Firebase/Auth/Source/FIRAuthSerialTaskQueue.m b/Firebase/Auth/Source/Auth/FIRAuthSerialTaskQueue.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthSerialTaskQueue.m rename to Firebase/Auth/Source/Auth/FIRAuthSerialTaskQueue.m diff --git a/Firebase/Auth/Source/FIRAuthSettings.m b/Firebase/Auth/Source/Auth/FIRAuthSettings.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthSettings.m rename to Firebase/Auth/Source/Auth/FIRAuthSettings.m diff --git a/Firebase/Auth/Source/FIRAuthTokenResult.m b/Firebase/Auth/Source/Auth/FIRAuthTokenResult.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthTokenResult.m rename to Firebase/Auth/Source/Auth/FIRAuthTokenResult.m diff --git a/Firebase/Auth/Source/FIRAuthTokenResult_Internal.h b/Firebase/Auth/Source/Auth/FIRAuthTokenResult_Internal.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthTokenResult_Internal.h rename to Firebase/Auth/Source/Auth/FIRAuthTokenResult_Internal.h diff --git a/Firebase/Auth/Source/FIRAuth_Internal.h b/Firebase/Auth/Source/Auth/FIRAuth_Internal.h similarity index 100% rename from Firebase/Auth/Source/FIRAuth_Internal.h rename to Firebase/Auth/Source/Auth/FIRAuth_Internal.h diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.h b/Firebase/Auth/Source/Backend/FIRAuthBackend.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRAuthBackend.h rename to Firebase/Auth/Source/Backend/FIRAuthBackend.h diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/Backend/FIRAuthBackend.m similarity index 99% rename from Firebase/Auth/Source/RPCs/FIRAuthBackend.m rename to Firebase/Auth/Source/Backend/FIRAuthBackend.m index e862b5cd9f7..16c8c94420d 100644 --- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m +++ b/Firebase/Auth/Source/Backend/FIRAuthBackend.m @@ -59,9 +59,9 @@ #import "FIRVerifyPhoneNumberRequest.h" #import "FIRVerifyPhoneNumberResponse.h" -#import "../AuthProviders/OAuth/FIROAuthCredential_Internal.h" +#import "FIROAuthCredential_Internal.h" #if TARGET_OS_IOS -#import "../AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h" +#import "FIRPhoneAuthCredential_Internal.h" #import "FIRPhoneAuthProvider.h" #endif diff --git a/Firebase/Auth/Source/RPCs/FIRAuthRPCRequest.h b/Firebase/Auth/Source/Backend/FIRAuthRPCRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRAuthRPCRequest.h rename to Firebase/Auth/Source/Backend/FIRAuthRPCRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRAuthRPCResponse.h b/Firebase/Auth/Source/Backend/FIRAuthRPCResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRAuthRPCResponse.h rename to Firebase/Auth/Source/Backend/FIRAuthRPCResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRAuthRequestConfiguration.h b/Firebase/Auth/Source/Backend/FIRAuthRequestConfiguration.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRAuthRequestConfiguration.h rename to Firebase/Auth/Source/Backend/FIRAuthRequestConfiguration.h diff --git a/Firebase/Auth/Source/RPCs/FIRAuthRequestConfiguration.m b/Firebase/Auth/Source/Backend/FIRAuthRequestConfiguration.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRAuthRequestConfiguration.m rename to Firebase/Auth/Source/Backend/FIRAuthRequestConfiguration.m diff --git a/Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.h b/Firebase/Auth/Source/Backend/FIRIdentityToolkitRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.h rename to Firebase/Auth/Source/Backend/FIRIdentityToolkitRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.m b/Firebase/Auth/Source/Backend/FIRIdentityToolkitRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.m rename to Firebase/Auth/Source/Backend/FIRIdentityToolkitRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRCreateAuthURIRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRCreateAuthURIRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRCreateAuthURIRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRCreateAuthURIRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRCreateAuthURIResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRCreateAuthURIResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRCreateAuthURIResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRCreateAuthURIResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRDeleteAccountRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRDeleteAccountRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRDeleteAccountRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRDeleteAccountRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRDeleteAccountResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRDeleteAccountResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRDeleteAccountResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRDeleteAccountResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h b/Firebase/Auth/Source/Backend/RPC/FIREmailLinkSignInRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIREmailLinkSignInRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m b/Firebase/Auth/Source/Backend/RPC/FIREmailLinkSignInRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIREmailLinkSignInRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h b/Firebase/Auth/Source/Backend/RPC/FIREmailLinkSignInResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIREmailLinkSignInResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m b/Firebase/Auth/Source/Backend/RPC/FIREmailLinkSignInResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIREmailLinkSignInResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRGetAccountInfoRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRGetAccountInfoRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRGetAccountInfoRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRGetAccountInfoRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRGetAccountInfoResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRGetAccountInfoResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRGetAccountInfoResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRGetAccountInfoResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRGetOOBConfirmationCodeRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRGetOOBConfirmationCodeRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRGetOOBConfirmationCodeRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRGetOOBConfirmationCodeRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRGetOOBConfirmationCodeResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRGetOOBConfirmationCodeResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRGetOOBConfirmationCodeResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRGetOOBConfirmationCodeResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRGetProjectConfigRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRGetProjectConfigRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetProjectConfigRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRGetProjectConfigRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRGetProjectConfigRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRGetProjectConfigRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetProjectConfigRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRGetProjectConfigRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRGetProjectConfigResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRGetProjectConfigResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetProjectConfigResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRGetProjectConfigResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRGetProjectConfigResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRGetProjectConfigResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRGetProjectConfigResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRGetProjectConfigResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRResetPasswordRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRResetPasswordRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRResetPasswordRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRResetPasswordRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRResetPasswordResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRResetPasswordResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRResetPasswordResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRResetPasswordResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRSecureTokenRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRSecureTokenRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRSecureTokenRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRSecureTokenRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRSecureTokenResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRSecureTokenResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRSecureTokenResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRSecureTokenResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRSendVerificationCodeRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRSendVerificationCodeRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRSendVerificationCodeRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRSendVerificationCodeRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRSendVerificationCodeResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRSendVerificationCodeResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRSendVerificationCodeResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRSendVerificationCodeResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRSetAccountInfoRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRSetAccountInfoRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRSetAccountInfoRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRSetAccountInfoRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRSetAccountInfoResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRSetAccountInfoResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRSetAccountInfoResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRSetAccountInfoResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRSignInWithGameCenterRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRSignInWithGameCenterRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSignInWithGameCenterRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRSignInWithGameCenterRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRSignInWithGameCenterRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRSignInWithGameCenterRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSignInWithGameCenterRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRSignInWithGameCenterRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRSignInWithGameCenterResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRSignInWithGameCenterResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSignInWithGameCenterResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRSignInWithGameCenterResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRSignInWithGameCenterResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRSignInWithGameCenterResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSignInWithGameCenterResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRSignInWithGameCenterResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRSignUpNewUserRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRSignUpNewUserRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRSignUpNewUserRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRSignUpNewUserRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRSignUpNewUserResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRSignUpNewUserResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRSignUpNewUserResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRSignUpNewUserResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyClientRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyClientRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyClientRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyClientRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyClientResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyClientResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyClientResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyClientResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyCustomTokenRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyCustomTokenRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyCustomTokenRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyCustomTokenRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyCustomTokenResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyCustomTokenResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyCustomTokenResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyCustomTokenResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyPasswordRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyPasswordRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyPasswordRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyPasswordRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyPasswordResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyPasswordResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyPasswordResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyPasswordResponse.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyPhoneNumberRequest.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyPhoneNumberRequest.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyPhoneNumberRequest.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyPhoneNumberRequest.m diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyPhoneNumberResponse.h similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.h rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyPhoneNumberResponse.h diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyPhoneNumberResponse.m similarity index 100% rename from Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.m rename to Firebase/Auth/Source/Backend/RPC/FIRVerifyPhoneNumberResponse.m diff --git a/Firebase/Auth/Source/FIRAuthKeychain.h b/Firebase/Auth/Source/Storage/FIRAuthKeychain.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthKeychain.h rename to Firebase/Auth/Source/Storage/FIRAuthKeychain.h diff --git a/Firebase/Auth/Source/FIRAuthKeychain.m b/Firebase/Auth/Source/Storage/FIRAuthKeychain.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthKeychain.m rename to Firebase/Auth/Source/Storage/FIRAuthKeychain.m diff --git a/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.h b/Firebase/Auth/Source/Storage/FIRAuthUserDefaultsStorage.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthUserDefaultsStorage.h rename to Firebase/Auth/Source/Storage/FIRAuthUserDefaultsStorage.h diff --git a/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m b/Firebase/Auth/Source/Storage/FIRAuthUserDefaultsStorage.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m rename to Firebase/Auth/Source/Storage/FIRAuthUserDefaultsStorage.m diff --git a/Firebase/Auth/Source/FIRAuthAPNSToken.h b/Firebase/Auth/Source/System Services/FIRAuthAPNSToken.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthAPNSToken.h rename to Firebase/Auth/Source/System Services/FIRAuthAPNSToken.h diff --git a/Firebase/Auth/Source/FIRAuthAPNSToken.m b/Firebase/Auth/Source/System Services/FIRAuthAPNSToken.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthAPNSToken.m rename to Firebase/Auth/Source/System Services/FIRAuthAPNSToken.m diff --git a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.h b/Firebase/Auth/Source/System Services/FIRAuthAPNSTokenManager.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthAPNSTokenManager.h rename to Firebase/Auth/Source/System Services/FIRAuthAPNSTokenManager.h diff --git a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m b/Firebase/Auth/Source/System Services/FIRAuthAPNSTokenManager.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthAPNSTokenManager.m rename to Firebase/Auth/Source/System Services/FIRAuthAPNSTokenManager.m diff --git a/Firebase/Auth/Source/FIRAuthAppCredential.h b/Firebase/Auth/Source/System Services/FIRAuthAppCredential.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthAppCredential.h rename to Firebase/Auth/Source/System Services/FIRAuthAppCredential.h diff --git a/Firebase/Auth/Source/FIRAuthAppCredential.m b/Firebase/Auth/Source/System Services/FIRAuthAppCredential.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthAppCredential.m rename to Firebase/Auth/Source/System Services/FIRAuthAppCredential.m diff --git a/Firebase/Auth/Source/FIRAuthAppCredentialManager.h b/Firebase/Auth/Source/System Services/FIRAuthAppCredentialManager.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthAppCredentialManager.h rename to Firebase/Auth/Source/System Services/FIRAuthAppCredentialManager.h diff --git a/Firebase/Auth/Source/FIRAuthAppCredentialManager.m b/Firebase/Auth/Source/System Services/FIRAuthAppCredentialManager.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthAppCredentialManager.m rename to Firebase/Auth/Source/System Services/FIRAuthAppCredentialManager.m diff --git a/Firebase/Auth/Source/FIRAuthNotificationManager.h b/Firebase/Auth/Source/System Services/FIRAuthNotificationManager.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthNotificationManager.h rename to Firebase/Auth/Source/System Services/FIRAuthNotificationManager.h diff --git a/Firebase/Auth/Source/FIRAuthNotificationManager.m b/Firebase/Auth/Source/System Services/FIRAuthNotificationManager.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthNotificationManager.m rename to Firebase/Auth/Source/System Services/FIRAuthNotificationManager.m diff --git a/Firebase/Auth/Source/FIRAuthStoredUserManager.h b/Firebase/Auth/Source/System Services/FIRAuthStoredUserManager.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthStoredUserManager.h rename to Firebase/Auth/Source/System Services/FIRAuthStoredUserManager.h diff --git a/Firebase/Auth/Source/FIRAuthStoredUserManager.m b/Firebase/Auth/Source/System Services/FIRAuthStoredUserManager.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthStoredUserManager.m rename to Firebase/Auth/Source/System Services/FIRAuthStoredUserManager.m diff --git a/Firebase/Auth/Source/FIRSecureTokenService.h b/Firebase/Auth/Source/System Services/FIRSecureTokenService.h similarity index 100% rename from Firebase/Auth/Source/FIRSecureTokenService.h rename to Firebase/Auth/Source/System Services/FIRSecureTokenService.h diff --git a/Firebase/Auth/Source/FIRSecureTokenService.m b/Firebase/Auth/Source/System Services/FIRSecureTokenService.m similarity index 100% rename from Firebase/Auth/Source/FIRSecureTokenService.m rename to Firebase/Auth/Source/System Services/FIRSecureTokenService.m diff --git a/Firebase/Auth/Source/FIRAdditionalUserInfo.m b/Firebase/Auth/Source/User/FIRAdditionalUserInfo.m similarity index 100% rename from Firebase/Auth/Source/FIRAdditionalUserInfo.m rename to Firebase/Auth/Source/User/FIRAdditionalUserInfo.m diff --git a/Firebase/Auth/Source/FIRAdditionalUserInfo_Internal.h b/Firebase/Auth/Source/User/FIRAdditionalUserInfo_Internal.h similarity index 100% rename from Firebase/Auth/Source/FIRAdditionalUserInfo_Internal.h rename to Firebase/Auth/Source/User/FIRAdditionalUserInfo_Internal.h diff --git a/Firebase/Auth/Source/FIRUser.m b/Firebase/Auth/Source/User/FIRUser.m similarity index 99% rename from Firebase/Auth/Source/FIRUser.m rename to Firebase/Auth/Source/User/FIRUser.m index 9989614437f..c822595e87c 100644 --- a/Firebase/Auth/Source/FIRUser.m +++ b/Firebase/Auth/Source/User/FIRUser.m @@ -57,7 +57,7 @@ #if TARGET_OS_IOS #import "FIRPhoneAuthProvider.h" -#import "AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h" +#import "FIRPhoneAuthCredential_Internal.h" #endif NS_ASSUME_NONNULL_BEGIN diff --git a/Firebase/Auth/Source/FIRUserInfoImpl.h b/Firebase/Auth/Source/User/FIRUserInfoImpl.h similarity index 100% rename from Firebase/Auth/Source/FIRUserInfoImpl.h rename to Firebase/Auth/Source/User/FIRUserInfoImpl.h diff --git a/Firebase/Auth/Source/FIRUserInfoImpl.m b/Firebase/Auth/Source/User/FIRUserInfoImpl.m similarity index 100% rename from Firebase/Auth/Source/FIRUserInfoImpl.m rename to Firebase/Auth/Source/User/FIRUserInfoImpl.m diff --git a/Firebase/Auth/Source/FIRUserMetadata.m b/Firebase/Auth/Source/User/FIRUserMetadata.m similarity index 100% rename from Firebase/Auth/Source/FIRUserMetadata.m rename to Firebase/Auth/Source/User/FIRUserMetadata.m diff --git a/Firebase/Auth/Source/FIRUserMetadata_Internal.h b/Firebase/Auth/Source/User/FIRUserMetadata_Internal.h similarity index 100% rename from Firebase/Auth/Source/FIRUserMetadata_Internal.h rename to Firebase/Auth/Source/User/FIRUserMetadata_Internal.h diff --git a/Firebase/Auth/Source/FIRUser_Internal.h b/Firebase/Auth/Source/User/FIRUser_Internal.h similarity index 100% rename from Firebase/Auth/Source/FIRUser_Internal.h rename to Firebase/Auth/Source/User/FIRUser_Internal.h diff --git a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h b/Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthAppDelegateProxy.h rename to Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.h diff --git a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m b/Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthAppDelegateProxy.m rename to Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.m diff --git a/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.h b/Firebase/Auth/Source/Utilities/FIRAuthDefaultUIDelegate.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthDefaultUIDelegate.h rename to Firebase/Auth/Source/Utilities/FIRAuthDefaultUIDelegate.h diff --git a/Firebase/Auth/Source/FIRAuthDefaultUIDelegate.m b/Firebase/Auth/Source/Utilities/FIRAuthDefaultUIDelegate.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthDefaultUIDelegate.m rename to Firebase/Auth/Source/Utilities/FIRAuthDefaultUIDelegate.m diff --git a/Firebase/Auth/Source/FIRAuthErrorUtils.h b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthErrorUtils.h rename to Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h diff --git a/Firebase/Auth/Source/FIRAuthErrorUtils.m b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthErrorUtils.m rename to Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m diff --git a/Firebase/Auth/Source/FIRAuthExceptionUtils.h b/Firebase/Auth/Source/Utilities/FIRAuthExceptionUtils.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthExceptionUtils.h rename to Firebase/Auth/Source/Utilities/FIRAuthExceptionUtils.h diff --git a/Firebase/Auth/Source/FIRAuthExceptionUtils.m b/Firebase/Auth/Source/Utilities/FIRAuthExceptionUtils.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthExceptionUtils.m rename to Firebase/Auth/Source/Utilities/FIRAuthExceptionUtils.m diff --git a/Firebase/Auth/Source/FIRAuthInternalErrors.h b/Firebase/Auth/Source/Utilities/FIRAuthInternalErrors.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthInternalErrors.h rename to Firebase/Auth/Source/Utilities/FIRAuthInternalErrors.h diff --git a/Firebase/Auth/Source/FIRAuthURLPresenter.h b/Firebase/Auth/Source/Utilities/FIRAuthURLPresenter.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthURLPresenter.h rename to Firebase/Auth/Source/Utilities/FIRAuthURLPresenter.h diff --git a/Firebase/Auth/Source/FIRAuthURLPresenter.m b/Firebase/Auth/Source/Utilities/FIRAuthURLPresenter.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthURLPresenter.m rename to Firebase/Auth/Source/Utilities/FIRAuthURLPresenter.m diff --git a/Firebase/Auth/Source/FIRAuthWebUtils.h b/Firebase/Auth/Source/Utilities/FIRAuthWebUtils.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthWebUtils.h rename to Firebase/Auth/Source/Utilities/FIRAuthWebUtils.h diff --git a/Firebase/Auth/Source/FIRAuthWebUtils.m b/Firebase/Auth/Source/Utilities/FIRAuthWebUtils.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthWebUtils.m rename to Firebase/Auth/Source/Utilities/FIRAuthWebUtils.m diff --git a/Firebase/Auth/Source/FIRAuthWebView.h b/Firebase/Auth/Source/Utilities/FIRAuthWebView.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthWebView.h rename to Firebase/Auth/Source/Utilities/FIRAuthWebView.h diff --git a/Firebase/Auth/Source/FIRAuthWebView.m b/Firebase/Auth/Source/Utilities/FIRAuthWebView.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthWebView.m rename to Firebase/Auth/Source/Utilities/FIRAuthWebView.m diff --git a/Firebase/Auth/Source/FIRAuthWebViewController.h b/Firebase/Auth/Source/Utilities/FIRAuthWebViewController.h similarity index 100% rename from Firebase/Auth/Source/FIRAuthWebViewController.h rename to Firebase/Auth/Source/Utilities/FIRAuthWebViewController.h diff --git a/Firebase/Auth/Source/FIRAuthWebViewController.m b/Firebase/Auth/Source/Utilities/FIRAuthWebViewController.m similarity index 100% rename from Firebase/Auth/Source/FIRAuthWebViewController.m rename to Firebase/Auth/Source/Utilities/FIRAuthWebViewController.m diff --git a/Firebase/Auth/Source/NSData+FIRBase64.h b/Firebase/Auth/Source/Utilities/NSData+FIRBase64.h similarity index 100% rename from Firebase/Auth/Source/NSData+FIRBase64.h rename to Firebase/Auth/Source/Utilities/NSData+FIRBase64.h diff --git a/Firebase/Auth/Source/NSData+FIRBase64.m b/Firebase/Auth/Source/Utilities/NSData+FIRBase64.m similarity index 100% rename from Firebase/Auth/Source/NSData+FIRBase64.m rename to Firebase/Auth/Source/Utilities/NSData+FIRBase64.m From aa6077963356116aaa978c08eb06ad5f3c5674d0 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Wed, 3 Apr 2019 16:55:55 -0400 Subject: [PATCH 134/214] Bump GoogleUtilities version to 5.6.0 (#2715) --- GoogleUtilities.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoogleUtilities.podspec b/GoogleUtilities.podspec index 813ebe7590d..4ac8a4324c9 100644 --- a/GoogleUtilities.podspec +++ b/GoogleUtilities.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleUtilities' - s.version = '5.5.0' + s.version = '5.6.0' s.summary = 'Google Utilities for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC From 267e9a6ece89887e29ecfe3352bd98f44ddeef51 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Thu, 4 Apr 2019 09:26:12 -0400 Subject: [PATCH 135/214] FIRMessagingRemoteNotificationsProxy updated to use GULAppDelegateSwizzler (#2683) --- Example/Firebase.xcodeproj/project.pbxproj | 1 + ...FIRMessagingRemoteNotificationsProxyTest.m | 57 +++++- Example/Podfile | 22 +-- Firebase/Messaging/FIRMessaging.m | 1 + .../FIRMessagingRemoteNotificationsProxy.m | 187 ++++-------------- FirebaseMessaging.podspec | 1 + .../Internal/GULAppDelegateSwizzler_Private.h | 6 - .../Private/GULAppDelegateSwizzler.h | 7 + 8 files changed, 109 insertions(+), 173 deletions(-) diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 3f3966a38a6..2eee5fabe4e 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -7601,6 +7601,7 @@ "$(inherited)", "COCOAPODS=1", "GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1", + "GUL_APP_DELEGATE_TESTING=1", ); HEADER_SEARCH_PATHS = ( "$(inherited)", diff --git a/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m b/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m index a46efffc979..dae5297fa6a 100644 --- a/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m +++ b/Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m @@ -24,6 +24,8 @@ #import "FIRMessaging.h" #import "FIRMessagingRemoteNotificationsProxy.h" +#import + #pragma mark - Invalid App Delegate or UNNotificationCenter @interface RandomObject : NSObject @@ -62,6 +64,8 @@ @implementation IncompleteAppDelegate @interface FakeAppDelegate : NSObject @property(nonatomic) BOOL remoteNotificationMethodWasCalled; @property(nonatomic) BOOL remoteNotificationWithFetchHandlerWasCalled; +@property(nonatomic, strong) NSData *deviceToken; +@property(nonatomic, strong) NSError *registerForRemoteNotificationsError; @end @implementation FakeAppDelegate #if TARGET_OS_IOS @@ -75,6 +79,17 @@ - (void)application:(UIApplication *)application fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { self.remoteNotificationWithFetchHandlerWasCalled = YES; } + +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + self.deviceToken = deviceToken; +} + +- (void)application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + self.registerForRemoteNotificationsError = error; +} + @end #pragma mark - Incompete UNUserNotificationCenterDelegate @@ -107,6 +122,10 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center @end #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +@interface GULAppDelegateSwizzler (FIRMessagingRemoteNotificationsProxyTest) ++ (void)resetProxyOriginalDelegateOnceToken; +@end + #pragma mark - Local, Per-Test Properties @interface FIRMessagingRemoteNotificationsProxyTest : XCTestCase @@ -123,6 +142,9 @@ @implementation FIRMessagingRemoteNotificationsProxyTest - (void)setUp { [super setUp]; + + [GULAppDelegateSwizzler resetProxyOriginalDelegateOnceToken]; + _mockSharedApplication = OCMPartialMock([UIApplication sharedApplication]); _mockMessaging = OCMClassMock([FIRMessaging class]); @@ -134,8 +156,7 @@ - (void)setUp { OCMStub([_mockProxyClass sharedProxy]).andReturn(self.proxy); #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - _mockUserNotificationCenter = OCMClassMock([UNUserNotificationCenter class]); - OCMStub([_mockUserNotificationCenter currentNotificationCenter]).andReturn(_mockUserNotificationCenter); + _mockUserNotificationCenter = OCMPartialMock([UNUserNotificationCenter currentNotificationCenter]); #endif } @@ -149,6 +170,9 @@ - (void)tearDown { [_mockSharedApplication stopMocking]; _mockSharedApplication = nil; + [_mockUserNotificationCenter stopMocking]; + _mockUserNotificationCenter = nil; + _proxy = nil; [super tearDown]; } @@ -168,6 +192,7 @@ - (void)testSwizzlingNonAppDelegate { } - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod { +#if TARGET_OS_IOS IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init]; [OCMStub([self.mockSharedApplication delegate]) andReturn:incompleteAppDelegate]; [self.proxy swizzleMethodsIfPossible]; @@ -179,6 +204,7 @@ - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod { didReceiveRemoteNotification:notification]; [self.mockMessaging verify]; +#endif // TARGET_OS_IOS } - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod { @@ -187,10 +213,13 @@ - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod { [self.proxy swizzleMethodsIfPossible]; SEL remoteNotificationWithFetchHandler = - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); + @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); XCTAssertFalse([incompleteAppDelegate respondsToSelector:remoteNotificationWithFetchHandler]); + +#if TARGET_OS_IOS SEL remoteNotification = @selector(application:didReceiveRemoteNotification:); XCTAssertTrue([incompleteAppDelegate respondsToSelector:remoteNotification]); +#endif // TARGET_OS_IOS } - (void)testSwizzledAppDelegateRemoteNotificationMethods { @@ -219,7 +248,7 @@ - (void)testSwizzledAppDelegateRemoteNotificationMethods { // Verify our swizzled method was called OCMExpect([self.mockMessaging appDidReceiveMessage:notification]); - [appDelegate application:OCMClassMock([UIApplication class]) + [appDelegate application:self.mockSharedApplication didReceiveRemoteNotification:notification fetchCompletionHandler:^(UIBackgroundFetchResult result) {}]; @@ -227,6 +256,26 @@ - (void)testSwizzledAppDelegateRemoteNotificationMethods { XCTAssertTrue(appDelegate.remoteNotificationWithFetchHandlerWasCalled); [self.mockMessaging verify]; + + // Verify application:didRegisterForRemoteNotificationsWithDeviceToken: + NSData *deviceToken = [NSData data]; + + OCMExpect([self.mockMessaging setAPNSToken:deviceToken]); + + [appDelegate application:self.mockSharedApplication + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; + + XCTAssertEqual(appDelegate.deviceToken, deviceToken); + [self.mockMessaging verify]; + + // Verify application:didFailToRegisterForRemoteNotificationsWithError: + NSError *error = [NSError errorWithDomain:@"tests" code:-1 userInfo:nil]; + + [appDelegate application:self.mockSharedApplication + didFailToRegisterForRemoteNotificationsWithError:error]; + + XCTAssertEqual(appDelegate.registerForRemoteNotificationsError, error); + #endif } diff --git a/Example/Podfile b/Example/Podfile index 2af8a35c371..b18cf150ca2 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -62,17 +62,6 @@ target 'DynamicLinks_Example_iOS' do pod 'OCMock' pod 'GoogleUtilities/MethodSwizzler', :path => '../' pod 'GoogleUtilities/SwizzlerTestHelpers', :path => '../' - - # Set define to turn on the unswizzler for the unit tests - post_install do |installer_representation| - installer_representation.pods_project.targets.each do |target| - target.build_configurations.each do |config| - if config.name != 'Release' - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'GUL_UNSWIZZLING_ENABLED=1'] - end - end - end - end end end @@ -272,3 +261,14 @@ target 'InstanceID_Example_tvOS' do end end +# Set define to turn on GUL_UNSWIZZLING and GUL_APP_DELEGATE_TESTING for the unit tests +post_install do |installer_representation| + installer_representation.pods_project.targets.each do |target| + target.build_configurations.each do |config| + if config.name != 'Release' + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'GUL_UNSWIZZLING_ENABLED=1', 'GUL_APP_DELEGATE_TESTING=1'] + end + end + end +end + diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 6a16840eb61..997a2e68201 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -880,6 +880,7 @@ + (NSString *)FIRMessagingSDKCurrentLocale { - (void)receiver:(FIRMessagingReceiver *)receiver receivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage { if ([self.delegate respondsToSelector:@selector(messaging:didReceiveMessage:)]) { + [self appDidReceiveMessage:remoteMessage.appData]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunguarded-availability" [self.delegate messaging:self didReceiveMessage:remoteMessage]; diff --git a/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m b/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m index a7f6efbe743..c202e092bc5 100644 --- a/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m +++ b/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m @@ -23,23 +23,21 @@ #import "FIRMessagingLogger.h" #import "FIRMessagingUtilities.h" #import "FIRMessaging_Private.h" +#import -static const BOOL kDefaultAutoRegisterEnabledValue = YES; static void * UserNotificationObserverContext = &UserNotificationObserverContext; static NSString *kUserNotificationWillPresentSelectorString = @"userNotificationCenter:willPresentNotification:withCompletionHandler:"; static NSString *kUserNotificationDidReceiveResponseSelectorString = @"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:"; -static NSString *kReceiveDataMessageSelectorString = @"messaging:didReceiveMessage:"; -@interface FIRMessagingRemoteNotificationsProxy () +@interface FIRMessagingRemoteNotificationsProxy () @property(strong, nonatomic) NSMutableDictionary *originalAppDelegateImps; @property(strong, nonatomic) NSMutableDictionary *swizzledSelectorsByClass; @property(nonatomic) BOOL didSwizzleMethods; -@property(nonatomic) BOOL didSwizzleAppDelegateMethods; @property(nonatomic) BOOL hasSwizzledUserNotificationDelegate; @property(nonatomic) BOOL isObservingUserNotificationDelegateChanges; @@ -47,20 +45,14 @@ @interface FIRMessagingRemoteNotificationsProxy () @property(strong, nonatomic) id userNotificationCenter; @property(strong, nonatomic) id currentUserNotificationCenterDelegate; +@property(strong, nonatomic) GULAppDelegateInterceptorID appDelegateInterceptorID; + @end @implementation FIRMessagingRemoteNotificationsProxy + (BOOL)canSwizzleMethods { - id canSwizzleValue = - [[NSBundle mainBundle] - objectForInfoDictionaryKey: kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey]; - if (canSwizzleValue && [canSwizzleValue isKindOfClass:[NSNumber class]]) { - NSNumber *canSwizzleNumberValue = (NSNumber *)canSwizzleValue; - return canSwizzleNumberValue.boolValue; - } else { - return kDefaultAutoRegisterEnabledValue; - } + return [GULAppDelegateSwizzler isAppDelegateProxyEnabled]; } + (instancetype)sharedProxy { @@ -95,12 +87,8 @@ - (void)swizzleMethodsIfPossible { return; } - UIApplication *application = FIRMessagingUIApplication(); - if (!application) { - return; - } - NSObject *appDelegate = [application delegate]; - [self swizzleAppDelegateMethods:appDelegate]; + [GULAppDelegateSwizzler proxyOriginalDelegate]; + self.appDelegateInterceptorID = [GULAppDelegateSwizzler registerAppDelegateInterceptor:self]; // Add KVO listener on [UNUserNotificationCenter currentNotificationCenter]'s delegate property Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter"); @@ -118,6 +106,10 @@ - (void)swizzleMethodsIfPossible { } - (void)unswizzleAllMethods { + if (self.appDelegateInterceptorID) { + [GULAppDelegateSwizzler unregisterAppDelegateInterceptorWithID:self.appDelegateInterceptorID]; + } + for (NSString *className in self.swizzledSelectorsByClass) { Class klass = NSClassFromString(className); NSArray *selectorStrings = self.swizzledSelectorsByClass[className]; @@ -129,72 +121,6 @@ - (void)unswizzleAllMethods { [self.swizzledSelectorsByClass removeAllObjects]; } -- (void)swizzleAppDelegateMethods:(id)appDelegate { - if (![appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) { - return; - } - Class appDelegateClass = [appDelegate class]; - - BOOL didSwizzleAppDelegate = NO; - // Message receiving handler for iOS 9, 8, 7 devices (both display notification and data message). - SEL remoteNotificationSelector = - @selector(application:didReceiveRemoteNotification:); - - SEL remoteNotificationWithFetchHandlerSelector = - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); - - // For recording when APNS tokens are registered (or fail to register) - SEL registerForAPNSFailSelector = - @selector(application:didFailToRegisterForRemoteNotificationsWithError:); - - SEL registerForAPNSSuccessSelector = - @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); - - - // Receive Remote Notifications. - BOOL selectorWithFetchHandlerImplemented = NO; - if ([appDelegate respondsToSelector:remoteNotificationWithFetchHandlerSelector]) { - selectorWithFetchHandlerImplemented = YES; - [self swizzleSelector:remoteNotificationWithFetchHandlerSelector - inClass:appDelegateClass - withImplementation:(IMP)FCM_swizzle_appDidReceiveRemoteNotificationWithHandler - inProtocol:@protocol(UIApplicationDelegate)]; - didSwizzleAppDelegate = YES; - } - - if ([appDelegate respondsToSelector:remoteNotificationSelector] || - !selectorWithFetchHandlerImplemented) { - [self swizzleSelector:remoteNotificationSelector - inClass:appDelegateClass - withImplementation:(IMP)FCM_swizzle_appDidReceiveRemoteNotification - inProtocol:@protocol(UIApplicationDelegate)]; - didSwizzleAppDelegate = YES; - } - - // For data message from MCS. - SEL receiveDataMessageSelector = NSSelectorFromString(kReceiveDataMessageSelectorString); - if ([appDelegate respondsToSelector:receiveDataMessageSelector]) { - [self swizzleSelector:receiveDataMessageSelector - inClass:appDelegateClass - withImplementation:(IMP)FCM_swizzle_messagingDidReceiveMessage - inProtocol:@protocol(UIApplicationDelegate)]; - didSwizzleAppDelegate = YES; - } - - // Receive APNS token - [self swizzleSelector:registerForAPNSSuccessSelector - inClass:appDelegateClass - withImplementation:(IMP)FCM_swizzle_appDidRegisterForRemoteNotifications - inProtocol:@protocol(UIApplicationDelegate)]; - - [self swizzleSelector:registerForAPNSFailSelector - inClass:appDelegateClass - withImplementation:(IMP)FCM_swizzle_appDidFailToRegisterForRemoteNotifications - inProtocol:@protocol(UIApplicationDelegate)]; - - self.didSwizzleAppDelegateMethods = didSwizzleAppDelegate; -} - - (void)listenForDelegateChangesInUserNotificationCenter:(id)notificationCenter { Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter"); if (![notificationCenter isKindOfClass:notificationCenterClass]) { @@ -466,39 +392,38 @@ id getNamedPropertyFromObject(id object, NSString *propertyName, Class klass) { return property; } -#pragma mark - Swizzled Methods +#pragma mark - UIApplicationDelegate -void FCM_swizzle_appDidReceiveRemoteNotification(id self, - SEL _cmd, - UIApplication *app, - NSDictionary *userInfo) { +#if TARGET_OS_IOS +- (void)application:(UIApplication *)application +didReceiveRemoteNotification:(NSDictionary *)userInfo { [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; - - IMP original_imp = - [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd]; - if (original_imp) { - ((void (*)(id, SEL, UIApplication *, NSDictionary *))original_imp)(self, - _cmd, - app, - userInfo); - } } +#endif // TARGET_OS_IOS -void FCM_swizzle_appDidReceiveRemoteNotificationWithHandler( - id self, SEL _cmd, UIApplication *app, NSDictionary *userInfo, - void (^handler)(UIBackgroundFetchResult)) { - +- (void)application:(UIApplication *)application +didReceiveRemoteNotification:(NSDictionary *)userInfo +fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; +} - IMP original_imp = - [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd]; - if (original_imp) { - ((void (*)(id, SEL, UIApplication *, NSDictionary *, - void (^)(UIBackgroundFetchResult)))original_imp)( - self, _cmd, app, userInfo, handler); - } +- (void)application:(UIApplication *)application +didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + // Pass the APNSToken along to FIRMessaging (and auto-detect the token type) + [FIRMessaging messaging].APNSToken = deviceToken; +} + +- (void)application:(UIApplication *)application +didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + // Log the fact that we failed to register for remote notifications + FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxyAPNSFailed, + @"Error in " + @"application:didFailToRegisterForRemoteNotificationsWithError: %@", + error.localizedDescription); } +#pragma mark - Swizzled Methods + /** * Swizzle the notification handler for iOS 10+ devices. * Signature of original handler is as below: @@ -674,46 +599,4 @@ id userInfoFromNotification(id notification) { return notificationUserInfo; } -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, FIRMessaging *, FIRMessagingRemoteMessage *))original_imp)( - self, _cmd, message, remoteMessage); - } -} - -void FCM_swizzle_appDidFailToRegisterForRemoteNotifications(id self, - SEL _cmd, - UIApplication *app, - NSError *error) { - // Log the fact that we failed to register for remote notifications - FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxyAPNSFailed, - @"Error in " - @"application:didFailToRegisterForRemoteNotificationsWithError: %@", - error.localizedDescription); - IMP original_imp = - [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd]; - if (original_imp) { - ((void (*)(id, SEL, UIApplication *, NSError *))original_imp)(self, _cmd, app, error); - } -} - -void FCM_swizzle_appDidRegisterForRemoteNotifications(id self, - SEL _cmd, - UIApplication *app, - NSData *deviceToken) { - // Pass the APNSToken along to FIRMessaging (and auto-detect the token type) - [FIRMessaging messaging].APNSToken = deviceToken; - - IMP original_imp = - [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd]; - if (original_imp) { - ((void (*)(id, SEL, UIApplication *, NSData *))original_imp)(self, _cmd, app, deviceToken); - } -} - @end diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index 535a575c049..e1d36c8e70c 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -41,6 +41,7 @@ device, and it is completely free. s.dependency 'FirebaseAnalyticsInterop', '~> 1.1' s.dependency 'FirebaseCore', '~> 5.2' s.dependency 'FirebaseInstanceID', '~> 3.6' + s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.3' s.dependency 'GoogleUtilities/Reachability', '~> 5.3' s.dependency 'GoogleUtilities/Environment', '~> 5.3' s.dependency 'GoogleUtilities/UserDefaults', '~> 5.3' diff --git a/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h b/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h index 219b220cfd9..6e6b556e7ef 100644 --- a/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h +++ b/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h @@ -23,12 +23,6 @@ NS_ASSUME_NONNULL_BEGIN @interface GULAppDelegateSwizzler () -/** Returns the current sharedApplication. - * - * @return the current UIApplication if in an app, or nil if in extension or if it doesn't exist. - */ -+ (nullable UIApplication *)sharedApplication; - /** ISA Swizzles the given appDelegate as the original app delegate would be. * * @param appDelegate The object that needs to be isa swizzled. This should conform to the diff --git a/GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h b/GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h index 31fc4b0ab04..4421766458b 100644 --- a/GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h +++ b/GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h @@ -16,6 +16,7 @@ #import +@class UIApplication; @protocol UIApplicationDelegate; NS_ASSUME_NONNULL_BEGIN @@ -55,6 +56,12 @@ typedef NSString *const GULAppDelegateInterceptorID; */ + (BOOL)isAppDelegateProxyEnabled; +/** Returns the current sharedApplication. + * + * @return the current UIApplication if in an app, or nil if in extension or if it doesn't exist. + */ ++ (nullable UIApplication *)sharedApplication; + /** Do not initialize this class. */ - (instancetype)init NS_UNAVAILABLE; From 54c95ee622a3da675d46af084a95524f338db0da Mon Sep 17 00:00:00 2001 From: rsgowman Date: Thu, 4 Apr 2019 11:01:46 -0400 Subject: [PATCH 136/214] Minor bugfix wrt converting strings with embedded 0s (#2716) --- Firestore/core/src/firebase/firestore/util/string_apple.h | 8 ++++---- .../test/firebase/firestore/util/string_apple_test.mm | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/util/string_apple.h b/Firestore/core/src/firebase/firestore/util/string_apple.h index 436d2ab2cb8..b6eb6f636ee 100644 --- a/Firestore/core/src/firebase/firestore/util/string_apple.h +++ b/Firestore/core/src/firebase/firestore/util/string_apple.h @@ -46,17 +46,17 @@ inline CFStringRef MakeCFString(absl::string_view contents) { #if defined(__OBJC__) // Translates a C string to the equivalent NSString without making a copy. -inline NSString* WrapNSStringNoCopy(const char* c_str) { +inline NSString* WrapNSStringNoCopy(const char* c_str, size_t size) { return [[NSString alloc] initWithBytesNoCopy:const_cast(static_cast(c_str)) - length:strlen(c_str) + length:size encoding:NSUTF8StringEncoding - freeWhenDone:NO]; + freeWhenDone:false]; } // Translates a string_view to the equivalent NSString without making a copy. inline NSString* WrapNSStringNoCopy(const absl::string_view str) { - return WrapNSStringNoCopy(str.data()); + return WrapNSStringNoCopy(str.data(), str.size()); } // Translates a string_view string to the equivalent NSString by making a copy. diff --git a/Firestore/core/test/firebase/firestore/util/string_apple_test.mm b/Firestore/core/test/firebase/firestore/util/string_apple_test.mm index 1cc247e1c96..f2a52095de9 100644 --- a/Firestore/core/test/firebase/firestore/util/string_apple_test.mm +++ b/Firestore/core/test/firebase/firestore/util/string_apple_test.mm @@ -64,6 +64,14 @@ } } +TEST_F(StringAppleTest, MakeStringFromNSStringNoCopy) { + for (const std::string& string_value : StringTestCases()) { + NSString* ns_string = WrapNSStringNoCopy(string_value); + std::string actual = MakeString(ns_string); + EXPECT_EQ(string_value, actual); + } +} + } // namespace util } // namespace firestore } // namespace firebase From ec2399683ba8d60e6b78846eec0c20657b54db53 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 4 Apr 2019 09:46:29 -0700 Subject: [PATCH 137/214] Remove useMessagingDelegateForDirectChannel property (#2711) --- Example/Firebase.xcodeproj/project.pbxproj | 6 - .../Tests/FIRMessagingReceiverTest.m | 75 ------ Firebase/Messaging/FIRMessaging.m | 11 +- Firebase/Messaging/FIRMessagingReceiver.h | 8 - Firebase/Messaging/FIRMessagingReceiver.m | 95 +------ Firebase/Messaging/Public/FIRMessaging.h | 26 +- .../GULAppDelegateSwizzler.m | 244 ++---------------- GoogleUtilities/Common/GULLoggerCodes.h | 29 +-- .../GoogleUtilities.xcodeproj/project.pbxproj | 6 +- GoogleUtilities/Example/Podfile | 20 +- .../Swizzler/GULAppDelegateSwizzlerTest.m | 193 +------------- ZipBuilder/README.md | 69 +---- 12 files changed, 48 insertions(+), 734 deletions(-) delete mode 100644 Example/Messaging/Tests/FIRMessagingReceiverTest.m diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 2eee5fabe4e..c9226363f9a 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -137,7 +137,6 @@ 511DD29D2225C8C40094D78D /* FIRMessagingLinkHandlingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315CC1E8738B70083EDBF /* FIRMessagingLinkHandlingTest.m */; }; 511DD29E2225C8C40094D78D /* FIRMessagingPendingTopicsListTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315CD1E8738B70083EDBF /* FIRMessagingPendingTopicsListTest.m */; }; 511DD29F2225C8C40094D78D /* FIRMessagingPubSubTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315CE1E8738B70083EDBF /* FIRMessagingPubSubTest.m */; }; - 511DD2A02225C8C40094D78D /* FIRMessagingReceiverTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEF61BFC216E8B1000A738D4 /* FIRMessagingReceiverTest.m */; }; 511DD2A12225C8C40094D78D /* FIRMessagingRegistrarTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315CF1E8738B70083EDBF /* FIRMessagingRegistrarTest.m */; }; 511DD2A22225C8C40094D78D /* FIRMessagingRemoteNotificationsProxyTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D01E8738B70083EDBF /* FIRMessagingRemoteNotificationsProxyTest.m */; }; 511DD2A32225C8C40094D78D /* FIRMessagingRmqManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D11E8738B70083EDBF /* FIRMessagingRmqManagerTest.m */; }; @@ -635,7 +634,6 @@ DEE14D931E84468D006FA992 /* FIROptionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE14D7A1E844677006FA992 /* FIROptionsTest.m */; }; DEE14D941E84468D006FA992 /* FIRTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE14D7C1E844677006FA992 /* FIRTestCase.m */; }; DEF288411F9AB6E100D480CF /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DEF288401F9AB6E100D480CF /* Default-568h@2x.png */; }; - DEF61BFD216E8B1100A738D4 /* FIRMessagingReceiverTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEF61BFC216E8B1000A738D4 /* FIRMessagingReceiverTest.m */; }; DEF6C30D1FBCE72F005D0740 /* FIRAuthDispatcherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9314FF1E86C6FF0083EDBF /* FIRAuthDispatcherTests.m */; }; DEF6C30F1FBCE775005D0740 /* FIRAdditionalUserInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9314FA1E86C6FF0083EDBF /* FIRAdditionalUserInfoTests.m */; }; DEF6C3101FBCE775005D0740 /* FIRApp+FIRAuthUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9314FC1E86C6FF0083EDBF /* FIRApp+FIRAuthUnitTests.m */; }; @@ -1478,7 +1476,6 @@ DEE14D7C1E844677006FA992 /* FIRTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRTestCase.m; sourceTree = ""; }; DEE14D7D1E844677006FA992 /* Tests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; DEF288401F9AB6E100D480CF /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; - DEF61BFC216E8B1000A738D4 /* FIRMessagingReceiverTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRMessagingReceiverTest.m; sourceTree = ""; }; E2C2834C90DBAB56D568189F /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; ED34CF4A20DC16DC000EA5D1 /* FIRComponentContainerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRComponentContainerTest.m; sourceTree = ""; }; ED34CF4B20DC16DC000EA5D1 /* FIRTestComponents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRTestComponents.m; sourceTree = ""; }; @@ -2557,7 +2554,6 @@ DE9315CC1E8738B70083EDBF /* FIRMessagingLinkHandlingTest.m */, DE9315CD1E8738B70083EDBF /* FIRMessagingPendingTopicsListTest.m */, DE9315CE1E8738B70083EDBF /* FIRMessagingPubSubTest.m */, - DEF61BFC216E8B1000A738D4 /* FIRMessagingReceiverTest.m */, DE9315CF1E8738B70083EDBF /* FIRMessagingRegistrarTest.m */, DE9315D01E8738B70083EDBF /* FIRMessagingRemoteNotificationsProxyTest.m */, DE9315D11E8738B70083EDBF /* FIRMessagingRmqManagerTest.m */, @@ -4319,7 +4315,6 @@ 511DD29D2225C8C40094D78D /* FIRMessagingLinkHandlingTest.m in Sources */, 511DD29E2225C8C40094D78D /* FIRMessagingPendingTopicsListTest.m in Sources */, 511DD29F2225C8C40094D78D /* FIRMessagingPubSubTest.m in Sources */, - 511DD2A02225C8C40094D78D /* FIRMessagingReceiverTest.m in Sources */, 511DD2A12225C8C40094D78D /* FIRMessagingRegistrarTest.m in Sources */, 511DD2A22225C8C40094D78D /* FIRMessagingRemoteNotificationsProxyTest.m in Sources */, 511DD2A32225C8C40094D78D /* FIRMessagingRmqManagerTest.m in Sources */, @@ -4941,7 +4936,6 @@ EDF5242C21EA37AA00BB24C6 /* FIRMessagingTestUtilities.m in Sources */, DE9316031E8738E60083EDBF /* FIRMessagingSyncMessageManagerTest.m in Sources */, DE9315FF1E8738E60083EDBF /* FIRMessagingRemoteNotificationsProxyTest.m in Sources */, - DEF61BFD216E8B1100A738D4 /* FIRMessagingReceiverTest.m in Sources */, DE9315F81E8738E60083EDBF /* FIRMessagingDataMessageManagerTest.m in Sources */, 51284D16224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m in Sources */, DE9316051E8738E60083EDBF /* FIRMessagingTestNotificationUtilities.m in Sources */, diff --git a/Example/Messaging/Tests/FIRMessagingReceiverTest.m b/Example/Messaging/Tests/FIRMessagingReceiverTest.m deleted file mode 100644 index e0fd7e78f73..00000000000 --- a/Example/Messaging/Tests/FIRMessagingReceiverTest.m +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 - -#import - -#import - -#import "FIRMessaging.h" -#import "FIRMessaging_Private.h" -#import "FIRMessagingTestUtilities.h" - -NSString *const kFIRMessagingTestsReceiverSuiteName = @"com.messaging.test_receiverTest"; - -@interface FIRMessagingReceiverTest : XCTestCase -@property(nonatomic, readonly, strong) FIRMessaging *messaging; - -@end - -@implementation FIRMessagingReceiverTest -- (void)setUp { - [super setUp]; - - NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:kFIRMessagingTestsReceiverSuiteName]; - _messaging = [FIRMessagingTestUtilities messagingForTestsWithUserDefaults:defaults]; -} - -- (void)tearDown { - [self.messaging.messagingUserDefaults removePersistentDomainForName:kFIRMessagingTestsReceiverSuiteName]; - _messaging = nil; - - [super tearDown]; -} - -- (void)testUseMessagingDelegate { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - XCTAssertFalse(_messaging.useMessagingDelegateForDirectChannel); - - _messaging.useMessagingDelegateForDirectChannel = YES; - XCTAssertTrue(_messaging.useMessagingDelegateForDirectChannel); -} - -- (void)testUseMessagingDelegateFlagOverridedByPlistWithFalseValue { - id bundleMock = OCMPartialMock([NSBundle mainBundle]); - OCMStub([bundleMock objectForInfoDictionaryKey:kFIRMessagingPlistUseMessagingDelegate]) - .andReturn(nil); - XCTAssertFalse(_messaging.useMessagingDelegateForDirectChannel); - - [bundleMock stopMocking]; -} - -- (void)testUseMessagingDelegateFlagOverridedByPlistWithTrueValue { - id bundleMock = OCMPartialMock([NSBundle mainBundle]); - OCMStub([bundleMock objectForInfoDictionaryKey:kFIRMessagingPlistUseMessagingDelegate]) - .andReturn(@YES); - XCTAssertTrue(_messaging.useMessagingDelegateForDirectChannel); -#pragma clang diagnostic pop - [bundleMock stopMocking]; -} -@end diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 997a2e68201..46ec4427a6c 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -319,7 +319,7 @@ - (void)setupNotificationListeners { } - (void)setupReceiver { - self.receiver = [[FIRMessagingReceiver alloc] initWithUserDefaults:self.messagingUserDefaults]; + self.receiver = [[FIRMessagingReceiver alloc] init]; self.receiver.delegate = self; } @@ -659,15 +659,6 @@ - (void)notifyDelegateOfFCMTokenAvailability { } } - -- (void)setUseMessagingDelegateForDirectChannel:(BOOL)useMessagingDelegateForDirectChannel { - self.receiver.useDirectChannel = useMessagingDelegateForDirectChannel; -} - -- (BOOL)useMessagingDelegateForDirectChannel { - return self.receiver.useDirectChannel; -} - #pragma mark - Application State Changes - (void)applicationStateChanged { diff --git a/Firebase/Messaging/FIRMessagingReceiver.h b/Firebase/Messaging/FIRMessagingReceiver.h index 416ee3b6d69..676c7f1aef2 100644 --- a/Firebase/Messaging/FIRMessagingReceiver.h +++ b/Firebase/Messaging/FIRMessagingReceiver.h @@ -30,15 +30,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FIRMessagingReceiver : NSObject -/// Default initializer for creating the messaging receiver. -- (instancetype)initWithUserDefaults:(GULUserDefaults *)defaults NS_DESIGNATED_INITIALIZER; - -/// Use `initWithUserDefaults:` instead. -- (instancetype)init NS_UNAVAILABLE; - @property(nonatomic, weak, nullable) id delegate; -/// Whether to use direct channel for direct channel message callback handler in all iOS versions. -@property(nonatomic, assign) BOOL useDirectChannel; @end diff --git a/Firebase/Messaging/FIRMessagingReceiver.m b/Firebase/Messaging/FIRMessagingReceiver.m index 0d947c5f082..4d8ec13f4b2 100644 --- a/Firebase/Messaging/FIRMessagingReceiver.m +++ b/Firebase/Messaging/FIRMessagingReceiver.m @@ -18,9 +18,6 @@ #import -#import -#import - #import "FIRMessaging.h" #import "FIRMessagingLogger.h" #import "FIRMessagingUtilities.h" @@ -28,12 +25,6 @@ static NSString *const kUpstreamMessageIDUserInfoKey = @"messageID"; static NSString *const kUpstreamErrorUserInfoKey = @"error"; -/// "Should use Messaging delegate" key stored in NSUserDefaults -NSString *const kFIRMessagingUserDefaultsKeyUseMessagingDelegate = - @"com.firebase.messaging.useMessagingDelegate"; -/// "Should use Messaging Delegate" key stored in Info.plist -NSString *const kFIRMessagingPlistUseMessagingDelegate = - @"FirebaseMessagingUseMessagingDelegateForDirectChannel"; static int downstreamMessageID = 0; @@ -43,16 +34,6 @@ @interface FIRMessagingReceiver () @implementation FIRMessagingReceiver -#pragma mark - Initializer - -- (instancetype)initWithUserDefaults:(GULUserDefaults *)defaults { - self = [super init]; - if (self != nil) { - _defaults = defaults; - } - return self; -} - #pragma mark - FIRMessagingDataMessageManager protocol - (void)didReceiveMessage:(NSDictionary *)message withIdentifier:(nullable NSString *)messageID { @@ -60,14 +41,7 @@ - (void)didReceiveMessage:(NSDictionary *)message withIdentifier:(nullable NSStr messageID = [[self class] nextMessageID]; } - NSInteger majorOSVersion = [[GULAppEnvironmentUtil systemVersion] integerValue]; - if (majorOSVersion >= 10 || self.useDirectChannel) { - // iOS 10 and above or use direct channel is enabled. - [self scheduleIos10NotificationForMessage:message withIdentifier:messageID]; - } else { - // Post notification directly to AppDelegate handlers. This is valid pre-iOS 10. - [self scheduleNotificationForMessage:message]; - } + [self handleDirectChannelMessage:message withIdentifier:messageID]; } - (void)willSendDataMessageWithID:(NSString *)messageID error:(NSError *)error { @@ -112,52 +86,13 @@ - (void)didDeleteMessagesOnServer { } #pragma mark - Private Helpers -// As the new UserNotifications framework in iOS 10 doesn't support constructor/mutation for -// UNNotification object, FCM can't inject the message to the app with UserNotifications framework. -// Define our own protocol, which means app developers need to implement two interfaces to receive -// display notifications and data messages respectively for devices running iOS 10 or above. Devices -// running iOS 9 or below are not affected. -- (void)scheduleIos10NotificationForMessage:(NSDictionary *)message - withIdentifier:(NSString *)messageID { +- (void)handleDirectChannelMessage:(NSDictionary *)message withIdentifier:(NSString *)messageID { FIRMessagingRemoteMessage *wrappedMessage = [[FIRMessagingRemoteMessage alloc] init]; - // TODO: wrap title, body, badge and other fields wrappedMessage.appData = [message copy]; wrappedMessage.messageID = messageID; [self.delegate receiver:self receivedRemoteMessage:wrappedMessage]; } -- (void)scheduleNotificationForMessage:(NSDictionary *)message { - SEL newNotificationSelector = - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); - SEL oldNotificationSelector = @selector(application:didReceiveRemoteNotification:); - - dispatch_async(dispatch_get_main_queue(), ^{ - UIApplication *application = FIRMessagingUIApplication(); - if (!application) { - return; - } - id appDelegate = [application delegate]; - if ([appDelegate respondsToSelector:newNotificationSelector]) { - // Try the new remote notification callback - [appDelegate application:application - didReceiveRemoteNotification:message - fetchCompletionHandler:^(UIBackgroundFetchResult result) { - }]; - - } else if ([appDelegate respondsToSelector:oldNotificationSelector]) { - // Try the old remote notification callback -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [appDelegate application:application didReceiveRemoteNotification:message]; -#pragma clang diagnostic pop - } else { - FIRMessagingLoggerError(kFIRMessagingMessageCodeReceiver005, - @"None of the remote notification callbacks implemented by " - @"UIApplicationDelegate"); - } - }); -} - + (NSString *)nextMessageID { @synchronized (self) { ++downstreamMessageID; @@ -165,30 +100,4 @@ + (NSString *)nextMessageID { } } -- (BOOL)useDirectChannel { - // Check storage - id shouldUseMessagingDelegate = - [_defaults objectForKey:kFIRMessagingUserDefaultsKeyUseMessagingDelegate]; - if (shouldUseMessagingDelegate) { - return [shouldUseMessagingDelegate boolValue]; - } - - // Check Info.plist - shouldUseMessagingDelegate = - [[NSBundle mainBundle] objectForInfoDictionaryKey:kFIRMessagingPlistUseMessagingDelegate]; - if (shouldUseMessagingDelegate) { - return [shouldUseMessagingDelegate boolValue]; - } - // If none of above exists, we go back to default behavior which is NO. - return NO; -} - -- (void)setUseDirectChannel:(BOOL)useDirectChannel { - BOOL shouldUseMessagingDelegate = [self useDirectChannel]; - if (useDirectChannel != shouldUseMessagingDelegate) { - [_defaults setBool:useDirectChannel forKey:kFIRMessagingUserDefaultsKeyUseMessagingDelegate]; - [_defaults synchronize]; - } -} - @end diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h index 10b84dd4d33..e1942f5c291 100644 --- a/Firebase/Messaging/Public/FIRMessaging.h +++ b/Firebase/Messaging/Public/FIRMessaging.h @@ -266,15 +266,10 @@ NS_SWIFT_NAME(MessagingDelegate) didReceiveRegistrationToken:(NSString *)fcmToken NS_SWIFT_NAME(messaging(_:didReceiveRegistrationToken:)); -/// This method is called on iOS 10+ devices to handle data messages received via FCM -/// direct channel (not via APNS). For iOS 9 and below, the direct channel data message -/// is handled by the UIApplicationDelegate's -application:didReceiveRemoteNotification: method. -/// You can enable all direct channel data messages to be delivered in FIRMessagingDelegate -/// by setting the flag `useMessagingDelegateForDirectMessages` to true. +/// Handle data messages received via FCM direct channel (not via APNS). - (void)messaging:(FIRMessaging *)messaging didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage - NS_SWIFT_NAME(messaging(_:didReceive:)) - __IOS_AVAILABLE(10.0); +NS_SWIFT_NAME(messaging(_:didReceive:)); @end @@ -309,23 +304,6 @@ NS_SWIFT_NAME(Messaging) */ @property(nonatomic, readonly) BOOL isDirectChannelEstablished; -/* - * Whether direct channel message should only use FIRMessagingDelegate messaging(_:didReceive:) - * for message delivery callback. The default value is false. If you need to change - * the default, set FirebaseMessagingUseMessagingDelegateForDirectChannel to true in - * your application’s Info.plist. - * - * If false, the message via direct channel for iOS 9 and below is still delivered in - * `-UIApplicationDelegate application(_:didReceiveRemoteNotification:fetchCompletionHandler:)`, - * and the FIRMessagingRemoteMessage object and its associated data will be unavailable. - * For iOS 10 and above, it is still delivered in `FIRMessagingDelegate messaging(_:didReceive:)`. - * - * If true, the data message sent by direct channel will be delivered via - * `FIRMessagingDelegate messaging(_:didReceive:)` and across all iOS versions. - */ -@property(nonatomic, assign) BOOL useMessagingDelegateForDirectChannel - __deprecated_msg("This is soon to be deprecated. All direct messages will by default delivered in `FIRMessagingDelegate messaging(_:didReceive:)` across all iOS versions"); - /** * FIRMessaging * diff --git a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m index 368e7b71dbc..8bbabad3778 100644 --- a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m +++ b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m @@ -14,7 +14,7 @@ #import "TargetConditionals.h" -#if TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_IOS #import #import @@ -47,39 +47,14 @@ typedef BOOL (*GULRealContinueUserActivityIMP)( id, SEL, UIApplication *, NSUserActivity *, void (^)(NSArray *restorableObjects)); #pragma clang diagnostic pop -typedef void (*GULRealDidRegisterForRemoteNotificationsIMP)(id, SEL, UIApplication *, NSData *); - -typedef void (*GULRealDidFailToRegisterForRemoteNotificationsIMP)(id, - SEL, - UIApplication *, - NSError *); - -typedef void (*GULRealDidReceiveRemoteNotificationIMP)(id, SEL, UIApplication *, NSDictionary *); - -typedef void (*GULRealDidReceiveRemoteNotificationWithCompletionIMP)( - id, SEL, UIApplication *, NSDictionary *, void (^)(UIBackgroundFetchResult)); - typedef void (^GULAppDelegateInterceptorCallback)(id); // The strings below are the keys for associated objects. static char const *const kGULContinueUserActivityIMPKey = "GUL_continueUserActivityIMP"; static char const *const kGULHandleBackgroundSessionIMPKey = "GUL_handleBackgroundSessionIMP"; static char const *const kGULOpenURLOptionsIMPKey = "GUL_openURLOptionsIMP"; - -static char const *const kGULRealDidRegisterForRemoteNotificationsIMPKey = - "GUL_didRegisterForRemoteNotificationsIMP"; -static char const *const kGULRealDidFailToRegisterForRemoteNotificationsIMPKey = - "GUL_didFailToRegisterForRemoteNotificationsIMP"; -static char const *const kGULRealDidReceiveRemoteNotificationIMPKey = - "GUL_didReceiveRemoteNotificationIMP"; -static char const *const kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey = - "GUL_didReceiveRemoteNotificationWithCompletionIMP"; -#if TARGET_OS_IOS -// The method application:openURL:sourceApplication:annotation: is not available on tvOS static char const *const kGULOpenURLOptionsSourceAnnotationsIMPKey = "GUL_openURLSourceApplicationAnnotationIMP"; -#endif // TARGET_OS_IOS - static char const *const kGULRealClassKey = "GUL_realClass"; static NSString *const kGULAppDelegateKeyPath = @"delegate"; @@ -358,82 +333,6 @@ + (void)createSubclassWithObject:(id)anObject { fromClass:realClass]; NSValue *continueUserActivityIMPPointer = [NSValue valueWithPointer:continueUserActivityIMP]; - // For application:handleEventsForBackgroundURLSession:completionHandler: - SEL handleEventsForBackgroundURLSessionSEL = @selector(application: - handleEventsForBackgroundURLSession:completionHandler:); - [GULAppDelegateSwizzler addInstanceMethodWithSelector:handleEventsForBackgroundURLSessionSEL - fromClass:[GULAppDelegateSwizzler class] - toClass:appDelegateSubClass]; - GULRealHandleEventsForBackgroundURLSessionIMP handleBackgroundSessionIMP = - (GULRealHandleEventsForBackgroundURLSessionIMP)[GULAppDelegateSwizzler - implementationOfMethodSelector:handleEventsForBackgroundURLSessionSEL - fromClass:realClass]; - NSValue *handleBackgroundSessionIMPPointer = - [NSValue valueWithPointer:handleBackgroundSessionIMP]; - - // For application:didRegisterForRemoteNotificationsWithDeviceToken: - SEL didRegisterForRemoteNotificationsSEL = @selector(application: - didRegisterForRemoteNotificationsWithDeviceToken:); - [GULAppDelegateSwizzler addInstanceMethodWithSelector:didRegisterForRemoteNotificationsSEL - fromClass:[GULAppDelegateSwizzler class] - toClass:appDelegateSubClass]; - GULRealDidRegisterForRemoteNotificationsIMP didRegisterForRemoteNotificationsIMP = - (GULRealDidRegisterForRemoteNotificationsIMP)[GULAppDelegateSwizzler - implementationOfMethodSelector:didRegisterForRemoteNotificationsSEL - fromClass:realClass]; - NSValue *didRegisterForRemoteNotificationsIMPPointer = - [NSValue valueWithPointer:didRegisterForRemoteNotificationsIMP]; - - // For application:didFailToRegisterForRemoteNotificationsWithError: - SEL didFailToRegisterForRemoteNotificationsSEL = @selector(application: - didFailToRegisterForRemoteNotificationsWithError:); - [GULAppDelegateSwizzler addInstanceMethodWithSelector:didFailToRegisterForRemoteNotificationsSEL - fromClass:[GULAppDelegateSwizzler class] - toClass:appDelegateSubClass]; - GULRealDidFailToRegisterForRemoteNotificationsIMP didFailToRegisterForRemoteNotificationsIMP = - (GULRealDidFailToRegisterForRemoteNotificationsIMP)[GULAppDelegateSwizzler - implementationOfMethodSelector:didFailToRegisterForRemoteNotificationsSEL - fromClass:realClass]; - NSValue *didFailToRegisterForRemoteNotificationsIMPPointer = - [NSValue valueWithPointer:didFailToRegisterForRemoteNotificationsIMP]; - - // For application:didReceiveRemoteNotification: - SEL didReceiveRemoteNotificationSEL = @selector(application:didReceiveRemoteNotification:); - [GULAppDelegateSwizzler addInstanceMethodWithSelector:didReceiveRemoteNotificationSEL - fromClass:[GULAppDelegateSwizzler class] - toClass:appDelegateSubClass]; - GULRealDidReceiveRemoteNotificationIMP didReceiveRemoteNotificationIMP = - (GULRealDidReceiveRemoteNotificationIMP) - [GULAppDelegateSwizzler implementationOfMethodSelector:didReceiveRemoteNotificationSEL - fromClass:realClass]; - NSValue *didReceiveRemoteNotificationIMPPointer = - [NSValue valueWithPointer:didReceiveRemoteNotificationIMP]; - - // For application:didReceiveRemoteNotification:fetchCompletionHandler: - NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer; - SEL didReceiveRemoteNotificationWithCompletionSEL = - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); - if ([anObject respondsToSelector:didReceiveRemoteNotificationWithCompletionSEL]) { - // Only add the application:didReceiveRemoteNotification:fetchCompletionHandler: method if - // the original AppDelegate implements it. - // This fixes a bug if an app only implements application:didReceiveRemoteNotification: - // (if we add the method with completion, iOS sees that one exists and does not call - // the method without the completion, which in this case is the only one the app implements). - - [GULAppDelegateSwizzler - addInstanceMethodWithSelector:didReceiveRemoteNotificationWithCompletionSEL - fromClass:[GULAppDelegateSwizzler class] - toClass:appDelegateSubClass]; - GULRealDidReceiveRemoteNotificationWithCompletionIMP - didReceiveRemoteNotificationWithCompletionIMP = - (GULRealDidReceiveRemoteNotificationWithCompletionIMP)[GULAppDelegateSwizzler - implementationOfMethodSelector:didReceiveRemoteNotificationWithCompletionSEL - fromClass:realClass]; - didReceiveRemoteNotificationWithCompletionIMPPointer = - [NSValue valueWithPointer:didReceiveRemoteNotificationWithCompletionIMP]; - } - -#if TARGET_OS_IOS // For application:openURL:sourceApplication:annotation: SEL openURLSourceApplicationAnnotationSEL = @selector(application: openURL:sourceApplication:annotation:); @@ -446,7 +345,19 @@ + (void)createSubclassWithObject:(id)anObject { fromClass:realClass]; NSValue *openURLSourceAppAnnotationIMPPointer = [NSValue valueWithPointer:openURLSourceApplicationAnnotationIMP]; -#endif // TARGET_OS_IOS + + // For application:handleEventsForBackgroundURLSession:completionHandler: + SEL handleEventsForBackgroundURLSessionSEL = @selector(application: + handleEventsForBackgroundURLSession:completionHandler:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:handleEventsForBackgroundURLSessionSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealHandleEventsForBackgroundURLSessionIMP handleBackgroundSessionIMP = + (GULRealHandleEventsForBackgroundURLSessionIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:handleEventsForBackgroundURLSessionSEL + fromClass:realClass]; + NSValue *handleBackgroundSessionIMPPointer = + [NSValue valueWithPointer:handleBackgroundSessionIMP]; // Override the description too so the custom class name will not show up. [GULAppDelegateSwizzler addInstanceMethodWithDestinationSelector:@selector(description) @@ -463,24 +374,8 @@ + (void)createSubclassWithObject:(id)anObject { objc_setAssociatedObject(anObject, &kGULOpenURLOptionsIMPKey, openURLOptionsIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - - objc_setAssociatedObject(anObject, &kGULRealDidRegisterForRemoteNotificationsIMPKey, - didRegisterForRemoteNotificationsIMPPointer, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); - objc_setAssociatedObject(anObject, &kGULRealDidFailToRegisterForRemoteNotificationsIMPKey, - didFailToRegisterForRemoteNotificationsIMPPointer, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); - objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationIMPKey, - didReceiveRemoteNotificationIMPPointer, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); - objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey, - didReceiveRemoteNotificationWithCompletionIMPPointer, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); -#if TARGET_OS_IOS objc_setAssociatedObject(anObject, &kGULOpenURLOptionsSourceAnnotationsIMPKey, openURLSourceAppAnnotationIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -#endif // TARGET_OS_IOS - objc_setAssociatedObject(anObject, &kGULRealClassKey, realClass, OBJC_ASSOCIATION_RETAIN_NONATOMIC); @@ -662,8 +557,6 @@ - (BOOL)application:(UIApplication *)application return returnedValue; } -#if TARGET_OS_IOS - - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication @@ -694,8 +587,6 @@ - (BOOL)application:(UIApplication *)application return returnedValue; } -#endif // TARGET_OS_IOS - #pragma mark - [Donor Methods] Network overridden handler methods #pragma clang diagnostic push @@ -756,112 +647,7 @@ - (BOOL)application:(UIApplication *)application } #pragma clang diagnostic pop -#pragma mark - [Donor Methods] Remote Notifications - -- (void)application:(UIApplication *)application - didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { - NSValue *didRegisterForRemoteNotificationsIMPPointer = - objc_getAssociatedObject(self, &kGULRealDidRegisterForRemoteNotificationsIMPKey); - GULRealDidRegisterForRemoteNotificationsIMP didRegisterForRemoteNotificationsIMP = - [didRegisterForRemoteNotificationsIMPPointer pointerValue]; - - // Notify interceptors. - SEL methodSelector = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); - [GULAppDelegateSwizzler - notifyInterceptorsWithMethodSelector:methodSelector - callback:^(id interceptor) { - [interceptor application:application - didRegisterForRemoteNotificationsWithDeviceToken: - deviceToken]; - }]; - // Call the real implementation if the real App Delegate has any. - if (didRegisterForRemoteNotificationsIMP) { - didRegisterForRemoteNotificationsIMP(self, methodSelector, application, deviceToken); - } -} - -- (void)application:(UIApplication *)application - didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - NSValue *didFailToRegisterForRemoteNotificationsIMPPointer = - objc_getAssociatedObject(self, &kGULRealDidFailToRegisterForRemoteNotificationsIMPKey); - GULRealDidFailToRegisterForRemoteNotificationsIMP didFailToRegisterForRemoteNotificationsIMP = - [didFailToRegisterForRemoteNotificationsIMPPointer pointerValue]; - - // Notify interceptors. - SEL methodSelector = @selector(application:didFailToRegisterForRemoteNotificationsWithError:); - [GULAppDelegateSwizzler - notifyInterceptorsWithMethodSelector:methodSelector - callback:^(id interceptor) { - [interceptor application:application - didFailToRegisterForRemoteNotificationsWithError:error]; - }]; - // Call the real implementation if the real App Delegate has any. - if (didFailToRegisterForRemoteNotificationsIMP) { - didFailToRegisterForRemoteNotificationsIMP(self, methodSelector, application, error); - } -} - -- (void)application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)userInfo - fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { - NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer = - objc_getAssociatedObject(self, &kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey); - GULRealDidReceiveRemoteNotificationWithCompletionIMP - didReceiveRemoteNotificationWithCompletionIMP = - [didReceiveRemoteNotificationWithCompletionIMPPointer pointerValue]; - - // Notify interceptors. - SEL methodSelector = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); - [GULAppDelegateSwizzler - notifyInterceptorsWithMethodSelector:methodSelector - callback:^(id interceptor) { - [interceptor application:application - didReceiveRemoteNotification:userInfo - fetchCompletionHandler:completionHandler]; - }]; - // Call the real implementation if the real App Delegate has any. - if (didReceiveRemoteNotificationWithCompletionIMP) { - didReceiveRemoteNotificationWithCompletionIMP(self, methodSelector, application, userInfo, - completionHandler); - } -} - -- (void)application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)userInfo { - NSValue *didReceiveRemoteNotificationIMPPointer = - objc_getAssociatedObject(self, &kGULRealDidReceiveRemoteNotificationIMPKey); - GULRealDidReceiveRemoteNotificationIMP didReceiveRemoteNotificationIMP = - [didReceiveRemoteNotificationIMPPointer pointerValue]; - - // Notify interceptors. - SEL methodSelector = @selector(application:didReceiveRemoteNotification:); -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [GULAppDelegateSwizzler - notifyInterceptorsWithMethodSelector:methodSelector - callback:^(id interceptor) { - [interceptor application:application - didReceiveRemoteNotification:userInfo]; - }]; -#pragma clang diagnostic pop - // Call the real implementation if the real App Delegate has any. - if (didReceiveRemoteNotificationIMP) { - didReceiveRemoteNotificationIMP(self, methodSelector, application, userInfo); - } -} - + (void)proxyAppDelegate:(id)appDelegate { - if (![appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) { - GULLogNotice( - kGULLoggerSwizzler, NO, - [NSString - stringWithFormat:@"I-SWZ%06ld", - (long)kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate], - @"App Delegate does not conform to UIApplicationDelegate protocol. %@", - [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]); - return; - } - id originalDelegate = appDelegate; // Do not create a subclass if it is not enabled. if (![GULAppDelegateSwizzler isAppDelegateProxyEnabled]) { @@ -928,4 +714,4 @@ + (void)resetProxyOriginalDelegateOnceToken { @end -#endif // TARGET_OS_IOS || TARGET_OS_TV +#endif // TARGET_OS_IOS diff --git a/GoogleUtilities/Common/GULLoggerCodes.h b/GoogleUtilities/Common/GULLoggerCodes.h index fd22ba637a2..b71c03797cd 100644 --- a/GoogleUtilities/Common/GULLoggerCodes.h +++ b/GoogleUtilities/Common/GULLoggerCodes.h @@ -16,21 +16,20 @@ typedef NS_ENUM(NSInteger, GULSwizzlerMessageCode) { // App Delegate Swizzling. - kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000, // I-SWZ001000 - kGULSwizzlerMessageCodeAppDelegateSwizzling001 = 1001, // I-SWZ001001 - kGULSwizzlerMessageCodeAppDelegateSwizzling002 = 1002, // I-SWZ001002 - kGULSwizzlerMessageCodeAppDelegateSwizzling003 = 1003, // I-SWZ001003 - kGULSwizzlerMessageCodeAppDelegateSwizzling004 = 1004, // I-SWZ001004 - kGULSwizzlerMessageCodeAppDelegateSwizzling005 = 1005, // I-SWZ001005 - kGULSwizzlerMessageCodeAppDelegateSwizzling006 = 1006, // I-SWZ001006 - kGULSwizzlerMessageCodeAppDelegateSwizzling007 = 1007, // I-SWZ001007 - kGULSwizzlerMessageCodeAppDelegateSwizzling008 = 1008, // I-SWZ001008 - kGULSwizzlerMessageCodeAppDelegateSwizzling009 = 1009, // I-SWZ001009 - kGULSwizzlerMessageCodeAppDelegateSwizzling010 = 1010, // I-SWZ001010 - kGULSwizzlerMessageCodeAppDelegateSwizzling011 = 1011, // I-SWZ001011 - kGULSwizzlerMessageCodeAppDelegateSwizzling012 = 1012, // I-SWZ001012 - kGULSwizzlerMessageCodeAppDelegateSwizzling013 = 1013, // I-SWZ001013 - kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate = 1014, // I-SWZ001014 + kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000, // I-SWZ001000 + kGULSwizzlerMessageCodeAppDelegateSwizzling001 = 1001, // I-SWZ001001 + kGULSwizzlerMessageCodeAppDelegateSwizzling002 = 1002, // I-SWZ001002 + kGULSwizzlerMessageCodeAppDelegateSwizzling003 = 1003, // I-SWZ001003 + kGULSwizzlerMessageCodeAppDelegateSwizzling004 = 1004, // I-SWZ001004 + kGULSwizzlerMessageCodeAppDelegateSwizzling005 = 1005, // I-SWZ001005 + kGULSwizzlerMessageCodeAppDelegateSwizzling006 = 1006, // I-SWZ001006 + kGULSwizzlerMessageCodeAppDelegateSwizzling007 = 1007, // I-SWZ001007 + kGULSwizzlerMessageCodeAppDelegateSwizzling008 = 1008, // I-SWZ001008 + kGULSwizzlerMessageCodeAppDelegateSwizzling009 = 1009, // I-SWZ001009 + kGULSwizzlerMessageCodeAppDelegateSwizzling010 = 1010, // I-SWZ001010 + kGULSwizzlerMessageCodeAppDelegateSwizzling011 = 1011, // I-SWZ001011 + kGULSwizzlerMessageCodeAppDelegateSwizzling012 = 1012, // I-SWZ001012 + kGULSwizzlerMessageCodeAppDelegateSwizzling013 = 1013, // I-SWZ001013 // Method Swizzling. kGULSwizzlerMessageCodeMethodSwizzling000 = 2000, // I-SWZ002000 diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj index 30940fb98ea..08778f3f85c 100644 --- a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; - 9A414672225259F900B08D77 /* GULAppDelegateSwizzlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EFBE67F92101401100E756A7 /* GULAppDelegateSwizzlerTest.m */; }; 9A7C37C2224BD9C600033B0D /* GULAppDelegateSwizzlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EFBE67F92101401100E756A7 /* GULAppDelegateSwizzlerTest.m */; }; DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */; }; DE84BBC421D7EC900048A176 /* GULUserDefaultsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */; }; @@ -94,7 +93,7 @@ 6003F5AE195388D20070C39A /* Tests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; - 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GoogleUtilities.podspec; path = ../../GoogleUtilities.podspec; sourceTree = ""; }; + 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GoogleUtilities.podspec; path = ../GoogleUtilities.podspec; sourceTree = ""; }; DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULAppEnvironmentUtilTest.m; sourceTree = ""; }; DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULUserDefaultsTests.m; sourceTree = ""; }; DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULReachabilityCheckerTest.m; sourceTree = ""; }; @@ -672,7 +671,6 @@ DEC9786A20F6D66300014E20 /* GULMutableDictionaryTest.m in Sources */, DE84BBC621D7EC900048A176 /* GULUserDefaultsTests.m in Sources */, DEC9786C20F6D66700014E20 /* GULReachabilityCheckerTest.m in Sources */, - 9A414672225259F900B08D77 /* GULAppDelegateSwizzlerTest.m in Sources */, DEC9786820F6D65B00014E20 /* GULLoggerTest.m in Sources */, DEC9786D20F6D66B00014E20 /* GULAppEnvironmentUtilTest.m in Sources */, ); @@ -1103,7 +1101,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", - "$(SRCROOT)/../AppDelegateSwizzler/Internal", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -1134,7 +1131,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", - "$(SRCROOT)/../AppDelegateSwizzler/Internal", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/GoogleUtilities/Example/Podfile b/GoogleUtilities/Example/Podfile index e114b6fa8d2..aa230a65d94 100644 --- a/GoogleUtilities/Example/Podfile +++ b/GoogleUtilities/Example/Podfile @@ -9,6 +9,16 @@ target 'Example_iOS' do inherit! :search_paths pod 'OCMock' end + + post_install do |installer_representation| + installer_representation.pods_project.targets.each do |target| + target.build_configurations.each do |config| + if config.name != 'Release' + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'GUL_UNSWIZZLING_ENABLED=1', 'GUL_APP_DELEGATE_TESTING=1'] + end + end + end + end end target 'Example_macOS' do @@ -32,13 +42,3 @@ target 'Example_tvOS' do pod 'OCMock' end end - -post_install do |installer_representation| - installer_representation.pods_project.targets.each do |target| - target.build_configurations.each do |config| - if config.name != 'Release' - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'GUL_UNSWIZZLING_ENABLED=1', 'GUL_APP_DELEGATE_TESTING=1'] - end - end - end -end \ No newline at end of file diff --git a/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m b/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m index 5356fff334f..d2f8f1fbefe 100644 --- a/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m +++ b/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m @@ -60,17 +60,7 @@ @interface GULTestAppDelegate : UIResponder { /** A URL property that is set by the app delegate methods, which is then used to verify if the app * delegate methods were properly called. */ -@property(nonatomic, copy) NSURL *url; - -@property(nonatomic, strong) NSData *remoteNotificationsDeviceToken; -@property(nonatomic, strong) NSError *failToRegisterForRemoteNotificationsError; -@property(nonatomic, strong) NSDictionary *remoteNotification; -@property(nonatomic, copy) void (^remoteNotificationCompletionHandler)(UIBackgroundFetchResult); - -/** - * The application is set each time a UIApplicationDelegate method is called - */ -@property(nonatomic, weak) UIApplication *application; +@property(nonatomic, copy) NSString *url; @end @@ -130,8 +120,7 @@ - (instancetype)init { - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { - self.application = app; - self.url = url; + _url = [url copy]; _isOpenURLOptionsMethodCalled = YES; return NO; } @@ -139,39 +128,9 @@ - (BOOL)application:(UIApplication *)app - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(nonnull NSString *)identifier completionHandler:(nonnull void (^)(void))completionHandler { - self.application = application; _backgroundSessionID = [identifier copy]; } -- (void)application:(UIApplication *)application - didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { - self.application = application; - self.remoteNotificationsDeviceToken = deviceToken; -} - -- (void)application:(UIApplication *)application - didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - self.application = application; - self.failToRegisterForRemoteNotificationsError = error; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -- (void)application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)userInfo { - self.application = application; - self.remoteNotification = userInfo; -} -#pragma clang diagnostic pop - -- (void)application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)userInfo - fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { - self.application = application; - self.remoteNotification = userInfo; - self.remoteNotificationCompletionHandler = completionHandler; -} - // These are methods to test whether changing the class still maintains behavior that the app // delegate proxy shouldn't have modified. @@ -210,7 +169,6 @@ - (BOOL)application:(UIApplication *)app return YES; } -#if TARGET_OS_IOS - (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url sourceApplication:(nullable NSString *)sourceApplication @@ -218,7 +176,6 @@ - (BOOL)application:(UIApplication *)application _URLForIOS8 = [url copy]; return YES; } -#endif // TARGET_OS_IOS #if SDK_HAS_USERACTIVITY @@ -245,12 +202,6 @@ - (void)tearDown { [super tearDown]; } -- (void)testNotAppDelegateIsNotSwizzled { - NSObject *notAppDelegate = [[NSObject alloc] init]; - [GULAppDelegateSwizzler proxyAppDelegate:(id)notAppDelegate]; - XCTAssertEqualObjects(NSStringFromClass([notAppDelegate class]), @"NSObject"); -} - /** Tests proxying an object that responds to UIApplicationDelegate protocol and makes sure that * it is isa swizzled and that the object after proxying responds to the expected methods * and doesn't have its ivars modified. @@ -284,27 +235,14 @@ - (void)testProxyAppDelegate { XCTAssertEqual(sizeBefore, sizeAfter); // After being proxied, it should be able to respond to the required method selector. -#if TARGET_OS_IOS XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application:openURL:sourceApplication:annotation:)]); -#endif // TARGET_OS_IOS - XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: continueUserActivity:restorationHandler:)]); XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application:openURL:options:)]); XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: handleEventsForBackgroundURLSession:completionHandler:)]); - XCTAssertTrue([realAppDelegate - respondsToSelector:@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); - XCTAssertTrue([realAppDelegate - respondsToSelector:@selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); - XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: - didReceiveRemoteNotification:)]); - XCTAssertTrue([realAppDelegate - respondsToSelector:@selector(application: - didReceiveRemoteNotification:fetchCompletionHandler:)]); - // Make sure that the class has changed. XCTAssertNotEqualObjects([realAppDelegate class], realAppDelegateClassBefore); @@ -520,7 +458,6 @@ - (void)testResultOfApplicationOpenURLOptionsIsORed { XCTAssertTrue(shouldOpen); } -#if TARGET_OS_IOS /** Tests that application:openURL:sourceApplication:annotation: is invoked on the interceptors if * it exists. */ @@ -603,7 +540,6 @@ - (void)testApplicationOpenURLSourceApplicationAnnotationResultIsORed { // The result is YES if one of the interceptors returns YES. XCTAssertTrue(shouldOpen); } -#endif // TARGET_OS_IOS /** Tests that application:handleEventsForBackgroundURLSession:completionHandler: is invoked on the * interceptors if it exists. @@ -712,131 +648,6 @@ - (void)testApplicationContinueUserActivityRestorationHandlerResultsAreORed { XCTAssertTrue(shouldContinueUserActvitiy); } -- (void)testApplicationDidRegisterForRemoteNotificationsIsInvokedOnInterceptors { - NSData *deviceToken = [NSData data]; - UIApplication *application = [UIApplication sharedApplication]; - - id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); - OCMExpect([interceptor application:application - didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]); - - id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); - OCMExpect([interceptor2 application:application - didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]); - - GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; - [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; - [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; - - [testAppDelegate application:application - didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); - - XCTAssertEqual(testAppDelegate.application, application); - XCTAssertEqual(testAppDelegate.remoteNotificationsDeviceToken, deviceToken); -} - -- (void)testApplicationDidFailToRegisterForRemoteNotificationsIsInvokedOnInterceptors { - NSError *error = [NSError errorWithDomain:@"test" code:-1 userInfo:nil]; - UIApplication *application = [UIApplication sharedApplication]; - - id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); - OCMExpect([interceptor application:application - didFailToRegisterForRemoteNotificationsWithError:error]); - - id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); - OCMExpect([interceptor2 application:application - didFailToRegisterForRemoteNotificationsWithError:error]); - - GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; - [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; - [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; - - [testAppDelegate application:application didFailToRegisterForRemoteNotificationsWithError:error]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); - - XCTAssertEqual(testAppDelegate.application, application); - XCTAssertEqual(testAppDelegate.failToRegisterForRemoteNotificationsError, error); -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -- (void)testApplicationDidReceiveRemoteNotificationIsInvokedOnInterceptors { - NSDictionary *notification = @{}; - UIApplication *application = [UIApplication sharedApplication]; - - id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); - OCMExpect([interceptor application:application didReceiveRemoteNotification:notification]); - - id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); - OCMExpect([interceptor2 application:application didReceiveRemoteNotification:notification]); - - GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; - [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; - [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; - - [testAppDelegate application:application didReceiveRemoteNotification:notification]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); - - XCTAssertEqual(testAppDelegate.application, application); - XCTAssertEqual(testAppDelegate.remoteNotification, notification); -} -#pragma clang diagnostic pop - -- (void)testApplicationDidReceiveRemoteNotificationWithCompletionIsInvokedOnInterceptors { - NSDictionary *notification = @{}; - UIApplication *application = [UIApplication sharedApplication]; - void (^completion)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) { - }; - - id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); - OCMExpect([interceptor application:application - didReceiveRemoteNotification:notification - fetchCompletionHandler:completion]); - - id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); - OCMExpect([interceptor2 application:application - didReceiveRemoteNotification:notification - fetchCompletionHandler:completion]); - - GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; - [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; - [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; - [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; - - [testAppDelegate application:application - didReceiveRemoteNotification:notification - fetchCompletionHandler:completion]; - OCMVerifyAll(interceptor); - OCMVerifyAll(interceptor2); - - XCTAssertEqual(testAppDelegate.application, application); - XCTAssertEqual(testAppDelegate.remoteNotification, notification); - XCTAssertEqual(testAppDelegate.remoteNotificationCompletionHandler, completion); -} - -- (void)testApplicationDidReceiveRemoteNotificationWithCompletionImplementationIsNotAdded { - // The delegate without application:didReceiveRemoteNotification:fetchCompletionHandler: - // implementation - GULTestInterceptorAppDelegate *legacyDelegate = [[GULTestInterceptorAppDelegate alloc] init]; - - XCTAssertFalse([legacyDelegate - respondsToSelector:@selector(application: - didReceiveRemoteNotification:fetchCompletionHandler:)]); - - [GULAppDelegateSwizzler proxyAppDelegate:legacyDelegate]; - - XCTAssertFalse([legacyDelegate - respondsToSelector:@selector(application: - didReceiveRemoteNotification:fetchCompletionHandler:)]); -} - #pragma mark - Tests to test that Plist flag is honored /** Tests that app delegate proxy is enabled when there is no Info.plist dictionary. */ diff --git a/ZipBuilder/README.md b/ZipBuilder/README.md index 54586f73e46..8759b101857 100644 --- a/ZipBuilder/README.md +++ b/ZipBuilder/README.md @@ -1,74 +1,7 @@ # Firebase Zip File Builder This project builds the Firebase iOS Zip file for distribution. - -## Overview - -This is a small Swift Package Manager project that allows users to package a Firebase iOS Zip file. With no launch -arguments, it will use the most recent public versions of all SDKs included in the zip file. - -It was designed to fail fast with an explanation of what went wrong, so you can fix issues or dig in without having to dig -too deep into the code. - -## Requirements - -In order to build the Zip file, you will need: - -- Xcode 10.1 -- CocoaPods -- An internet connection to fetch CocoaPods - -## Running the Tool - -You can run the tool with `swift run ZipBuilder [ARGS]` or generate an Xcode project with -`swift package generate-xcodeproj` and run within Xcode. - -In the near future, releases will be built via a builder server instead of on the release engineer's machine, making these -instructions more of a reference to understand what's going on instead of how to build it yourself. - -## Launch Arguments - -See `main.swift` and the `LaunchArgs` struct for information on specific launch arguments. - -You can pass in launch arguments with Xcode by clicking "ZipBuilder" beside the Run/Stop buttons, clicking "Edit -Scheme" and adding them in the "Arguments Passed On Launch" section. - -### Common Arguments - -These arguments assume you're running the command from the `ZipBuilder` directory. - -**Required** arguments: -- `-templateDir $(pwd)/Template` - - This should always be the same. -- `-coreDiagnosticsDir ` - - Needed to overwrite the existing Core Diagnostics framework. - -Optional comon arguments: -- `-updatePodRepo false` - - This is for speedups when `pod repo update` has already been run recently. - -For release engineers (Googlers packaging an upcoming Firebase release) these commands should also be used: -- `-customSpecRepos sso://cpdc-internal/firebase` - - This pulls the latest podspecs from the CocoaPods staging area. -- `-releasingSDKs ` and -- `-existingVersions ` - - Validates the version numbers fetched from CocoaPods staging against the expected released versions from these - textprotos. - -Putting them all together, here's a common command to build a releaseable Zip file: - -``` -swift run ZipBuilder -templateDir $(pwd)/Template -updatePodRepo false \ --coreDiagnosticsDir /private/tmp/tmpUqBxKN/FirebaseCoreDiagnostics.framework \ --releasingSDKs \ --existingVersions \ --customSpecRepos sso://cpdc-internal/firebase -``` - -## Debugging - -You can generate an Xcode project for the tool by running `swift package generate-xcodeproj` in this directory. -See the above instructions for adding Launch Arguments to the Xcode build. +More instructions to come. ## Priorities From b30d00515b6ac00c3b2cf92179e3fdfb3fb5dce1 Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Thu, 4 Apr 2019 10:47:52 -0700 Subject: [PATCH 138/214] Implement oauth secret token in headful-lite. (#2663) * Implement oauth secret token in headful-lite. This is useful for Twitter sign in. * Deprecate TwitterAuth{Provider,Credential}. OAuth sign in flow is now the recommended way to sign in with Twitter. * Add update OAuth credential to AuthDataResult. Developer can access the secret token from that. --- Example/Auth/Tests/FIRUserTests.m | 3 ++- Example/Firebase.xcodeproj/project.pbxproj | 4 +-- .../Auth Provider/OAuth/FIROAuthCredential.m | 26 ++++++++++++++++++- .../OAuth/FIROAuthCredential_Internal.h | 12 ++++++++- .../Auth Provider/OAuth/FIROAuthProvider.m | 2 ++ .../Twitter/FIRTwitterAuthCredential.h | 1 + .../Twitter/FIRTwitterAuthCredential.m | 5 ++++ .../Twitter/FIRTwitterAuthProvider.m | 5 ++++ Firebase/Auth/Source/Auth/FIRAuth.m | 16 ++++++------ Firebase/Auth/Source/Auth/FIRAuthDataResult.m | 20 ++++++++++++-- .../Source/Auth/FIRAuthDataResult_Internal.h | 10 +++++++ Firebase/Auth/Source/Backend/FIRAuthBackend.m | 9 ++----- .../Backend/RPC/FIRVerifyAssertionResponse.h | 5 ++++ .../Backend/RPC/FIRVerifyAssertionResponse.m | 1 + .../Auth/Source/Public/FIRAuthDataResult.h | 10 ++++++- .../Auth/Source/Public/FIROAuthCredential.h | 7 +++++ .../Source/Public/FIRTwitterAuthProvider.h | 7 +++-- Firebase/Auth/Source/User/FIRUser.m | 7 ++++- .../Auth/Source/Utilities/FIRAuthErrorUtils.h | 6 +++-- .../Auth/Source/Utilities/FIRAuthErrorUtils.m | 14 +++++----- 20 files changed, 136 insertions(+), 34 deletions(-) diff --git a/Example/Auth/Tests/FIRUserTests.m b/Example/Auth/Tests/FIRUserTests.m index e05e9a012df..263f32a68c9 100644 --- a/Example/Auth/Tests/FIRUserTests.m +++ b/Example/Auth/Tests/FIRUserTests.m @@ -1513,7 +1513,8 @@ - (void)testlinkAndRetrieveDataError { FIRVerifyAssertionResponseCallback callback) { dispatch_async(FIRAuthGlobalWorkQueue(), ^() { callback(nil, - [FIRAuthErrorUtils accountExistsWithDifferentCredentialErrorWithEmail:kEmail]); + [FIRAuthErrorUtils accountExistsWithDifferentCredentialErrorWithEmail:kEmail + updatedCredential:nil]); }); }); diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index c9226363f9a..6a0d9c9054a 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -6659,7 +6659,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 4ANB9W7R3P; GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = $SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist; + INFOPLIST_FILE = "$SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.4; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; @@ -6693,7 +6693,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 4ANB9W7R3P; GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = $SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist; + INFOPLIST_FILE = "$SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.4; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; diff --git a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential.m b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential.m index ebea7be8fa3..069d4f36608 100644 --- a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential.m +++ b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential.m @@ -20,6 +20,7 @@ #import "FIRAuthExceptionUtils.h" #import "FIROAuthCredential_Internal.h" #import "FIRVerifyAssertionRequest.h" +#import "FIRVerifyAssertionResponse.h" NS_ASSUME_NONNULL_BEGIN @@ -40,12 +41,14 @@ - (nullable instancetype)initWithProvider:(NSString *)provider { - (instancetype)initWithProviderID:(NSString *)providerID IDToken:(nullable NSString *)IDToken accessToken:(nullable NSString *)accessToken + secret:(nullable NSString *)secret pendingToken:(nullable NSString *)pendingToken { self = [super initWithProvider:providerID]; if (self) { _IDToken = IDToken; _accessToken = accessToken; _pendingToken = pendingToken; + _secret = secret; } return self; } @@ -53,7 +56,8 @@ - (instancetype)initWithProviderID:(NSString *)providerID - (instancetype)initWithProviderID:(NSString *)providerID sessionID:(NSString *)sessionID OAuthResponseURLString:(NSString *)OAuthResponseURLString { - self = [self initWithProviderID:providerID IDToken:nil accessToken:nil pendingToken:nil]; + self = + [self initWithProviderID:providerID IDToken:nil accessToken:nil secret:nil pendingToken:nil]; if (self) { _OAuthResponseURLString = OAuthResponseURLString; _sessionID = sessionID; @@ -61,9 +65,26 @@ - (instancetype)initWithProviderID:(NSString *)providerID return self; } + +- (nullable instancetype)initWithVerifyAssertionResponse:(FIRVerifyAssertionResponse *)response { + if (response.oauthIDToken.length || response.oauthAccessToken.length || + response.oauthSecretToken.length) { + return [self initWithProviderID:response.providerID + IDToken:response.oauthIDToken + accessToken:response.oauthAccessToken + secret:response.oauthSecretToken + pendingToken:response.pendingToken]; + } + return nil; +} + - (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { request.providerIDToken = _IDToken; request.providerAccessToken = _accessToken; + request.requestURI = _OAuthResponseURLString; + request.sessionID = _sessionID; + request.providerOAuthTokenSecret = _secret; + request.pendingToken = _pendingToken; } #pragma mark - NSSecureCoding @@ -76,9 +97,11 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { NSString *IDToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"IDToken"]; NSString *accessToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"accessToken"]; NSString *pendingToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"pendingToken"]; + NSString *secret = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"secret"]; self = [self initWithProviderID:self.provider IDToken:IDToken accessToken:accessToken + secret:secret pendingToken:pendingToken]; return self; } @@ -87,6 +110,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.IDToken forKey:@"IDToken"]; [aCoder encodeObject:self.accessToken forKey:@"accessToken"]; [aCoder encodeObject:self.pendingToken forKey:@"pendingToken"]; + [aCoder encodeObject:self.secret forKey:@"secret"]; } @end diff --git a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential_Internal.h b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential_Internal.h index 623d5fd6041..6bc7f4e854b 100644 --- a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential_Internal.h +++ b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential_Internal.h @@ -18,6 +18,8 @@ #import "FIROAuthCredential.h" +@class FIRVerifyAssertionResponse; + NS_ASSUME_NONNULL_BEGIN /** @extension FIROAuthCredential @@ -40,16 +42,18 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, readonly, nullable) NSString *pendingToken; -/** @fn initWithProviderId:IDToken:accessToken:pendingToken +/** @fn initWithProviderId:IDToken:accessToken:secret:pendingToken @brief Designated initializer. @param providerID The provider ID associated with the credential being created. @param IDToken The ID Token associated with the credential being created. @param accessToken The access token associated with the credential being created. + @param secret The secret associated with the credential being created. @param pendingToken The pending token associated with the credential being created. */ - (instancetype)initWithProviderID:(NSString *)providerID IDToken:(nullable NSString *)IDToken accessToken:(nullable NSString *)accessToken + secret:(nullable NSString *)secret pendingToken:(nullable NSString *)pendingToken NS_DESIGNATED_INITIALIZER; /** @fn initWithProviderId:sessionID:OAuthResponseURLString: @@ -62,6 +66,12 @@ NS_ASSUME_NONNULL_BEGIN sessionID:(NSString *)sessionID OAuthResponseURLString:(NSString *)OAuthResponseURLString; +/** @fn initWithVerifyAssertionResponse + @brief Intitializer which takes an verifyAssertion response. + @param response The verifyAssertion Response to create the credential instance. + */ +- (nullable instancetype)initWithVerifyAssertionResponse:(FIRVerifyAssertionResponse *)response; + @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m index 81e4fc919ca..dd322d6bcc5 100644 --- a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m +++ b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m @@ -70,6 +70,7 @@ + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID return [[FIROAuthCredential alloc] initWithProviderID:providerID IDToken:IDToken accessToken:accessToken + secret:nil pendingToken:nil]; } @@ -78,6 +79,7 @@ + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID return [[FIROAuthCredential alloc] initWithProviderID:providerID IDToken:nil accessToken:accessToken + secret:nil pendingToken:nil]; } diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h index 423d595a4e5..a8fdfbe8e8e 100644 --- a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h +++ b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h @@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN /** @class FIRTwitterAuthCredential @brief Internal implementation of FIRAuthCredential for Twitter credentials. */ +DEPRECATED_MSG_ATTRIBUTE("Please use FIROAuthCredential instead of FIRTwitterAuthCredential.") @interface FIRTwitterAuthCredential : FIRAuthCredential /** @property token diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m index cb466155850..6d7ce66be05 100644 --- a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m +++ b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m @@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + @interface FIRTwitterAuthCredential () - (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; @@ -70,4 +73,6 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { @end +#pragma clang diagnostic pop + NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m index 33771b723ca..004242b8ea2 100644 --- a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m +++ b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m @@ -23,6 +23,9 @@ NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + @implementation FIRTwitterAuthProvider - (instancetype)init { @@ -37,4 +40,6 @@ + (FIRAuthCredential *)credentialWithToken:(NSString *)token secret:(NSString *) @end +#pragma clang diagnostic pop + NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Auth/FIRAuth.m b/Firebase/Auth/Source/Auth/FIRAuth.m index 9c74a55f141..372cef61025 100644 --- a/Firebase/Auth/Source/Auth/FIRAuth.m +++ b/Firebase/Auth/Source/Auth/FIRAuth.m @@ -857,12 +857,6 @@ - (void)internalSignInAndRetrieveDataWithCredential:(FIRAuthCredential *)credent requestConfiguration:_requestConfiguration]; request.autoCreate = !isReauthentication; [credential prepareVerifyAssertionRequest:request]; - if ([credential isKindOfClass:[FIROAuthCredential class]]) { - FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential; - request.requestURI = OAuthCredential.OAuthResponseURLString; - request.sessionID = OAuthCredential.sessionID; - request.pendingToken = OAuthCredential.pendingToken; - } [FIRAuthBackend verifyAssertion:request callback:^(FIRVerifyAssertionResponse *response, NSError *error) { if (error) { @@ -875,7 +869,10 @@ - (void)internalSignInAndRetrieveDataWithCredential:(FIRAuthCredential *)credent if (response.needConfirmation) { if (callback) { NSString *email = response.email; - callback(nil, [FIRAuthErrorUtils accountExistsWithDifferentCredentialErrorWithEmail:email]); + FIROAuthCredential *credential = + [[FIROAuthCredential alloc] initWithVerifyAssertionResponse:response]; + callback(nil, [FIRAuthErrorUtils accountExistsWithDifferentCredentialErrorWithEmail:email + updatedCredential:credential]); } return; } @@ -894,9 +891,12 @@ - (void)internalSignInAndRetrieveDataWithCredential:(FIRAuthCredential *)credent if (callback) { FIRAdditionalUserInfo *additionalUserInfo = [FIRAdditionalUserInfo userInfoWithVerifyAssertionResponse:response]; + FIROAuthCredential *updatedOAuthCredential = + [[FIROAuthCredential alloc] initWithVerifyAssertionResponse:response]; FIRAuthDataResult *result = user ? [[FIRAuthDataResult alloc] initWithUser:user - additionalUserInfo:additionalUserInfo] : nil; + additionalUserInfo:additionalUserInfo + credential:updatedOAuthCredential] : nil; callback(result, error); } }]; diff --git a/Firebase/Auth/Source/Auth/FIRAuthDataResult.m b/Firebase/Auth/Source/Auth/FIRAuthDataResult.m index 9d8e5988d0e..ec72d76365f 100644 --- a/Firebase/Auth/Source/Auth/FIRAuthDataResult.m +++ b/Firebase/Auth/Source/Auth/FIRAuthDataResult.m @@ -18,6 +18,7 @@ #import "FIRAdditionalUserInfo.h" #import "FIRUser.h" +#import "FIROAuthCredential.h" NS_ASSUME_NONNULL_BEGIN @@ -33,12 +34,24 @@ @implementation FIRAuthDataResult */ static NSString *const kUserCodingKey = @"user"; +/** @var kCredentialCodingKey + @brief The key used to encode the credential for NSSecureCoding. + */ +static NSString *const kCredentialCodingKey = @"credential"; + - (nullable instancetype)initWithUser:(nullable FIRUser *)user additionalUserInfo:(nullable FIRAdditionalUserInfo *)additionalUserInfo { + return [self initWithUser:user additionalUserInfo:additionalUserInfo credential:nil]; +} + +- (nullable instancetype)initWithUser:(nullable FIRUser *)user + additionalUserInfo:(nullable FIRAdditionalUserInfo *)additionalUserInfo + credential:(nullable FIROAuthCredential *)credential { self = [super init]; if (self) { _additionalUserInfo = additionalUserInfo; _user = user; + _credential = credential; } return self; } @@ -55,13 +68,16 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { FIRAdditionalUserInfo *additionalUserInfo = [aDecoder decodeObjectOfClass:[FIRAdditionalUserInfo class] forKey:kAdditionalUserInfoCodingKey]; - - return [self initWithUser:user additionalUserInfo:additionalUserInfo]; + FIROAuthCredential *credential = + [aDecoder decodeObjectOfClass:[FIROAuthCredential class] + forKey:kCredentialCodingKey]; + return [self initWithUser:user additionalUserInfo:additionalUserInfo credential:credential]; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_user forKey:kUserCodingKey]; [aCoder encodeObject:_additionalUserInfo forKey:kAdditionalUserInfoCodingKey]; + [aCoder encodeObject:_credential forKey:kCredentialCodingKey]; } @end diff --git a/Firebase/Auth/Source/Auth/FIRAuthDataResult_Internal.h b/Firebase/Auth/Source/Auth/FIRAuthDataResult_Internal.h index b02bf59de83..3212e15bc9e 100644 --- a/Firebase/Auth/Source/Auth/FIRAuthDataResult_Internal.h +++ b/Firebase/Auth/Source/Auth/FIRAuthDataResult_Internal.h @@ -25,8 +25,18 @@ NS_ASSUME_NONNULL_BEGIN @param user The signed in user reference. @param additionalUserInfo The additional user info if available. */ +- (nullable instancetype)initWithUser:(nullable FIRUser *)user + additionalUserInfo:(nullable FIRAdditionalUserInfo *)additionalUserInfo; + +/** @fn initWithUser:additionalUserInfo: + @brief Designated initializer. + @param user The signed in user reference. + @param additionalUserInfo The additional user info if available. + @param credential The updated OAuth credential if available. + */ - (nullable instancetype)initWithUser:(nullable FIRUser *)user additionalUserInfo:(nullable FIRAdditionalUserInfo *)additionalUserInfo + credential:(nullable FIROAuthCredential *)credential NS_DESIGNATED_INITIALIZER; @end diff --git a/Firebase/Auth/Source/Backend/FIRAuthBackend.m b/Firebase/Auth/Source/Backend/FIRAuthBackend.m index 16c8c94420d..665570bd5f4 100644 --- a/Firebase/Auth/Source/Backend/FIRAuthBackend.m +++ b/Firebase/Auth/Source/Backend/FIRAuthBackend.m @@ -1067,13 +1067,8 @@ + (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorM NSString *email; if ([response isKindOfClass:[FIRVerifyAssertionResponse class]]) { FIRVerifyAssertionResponse *verifyAssertion = (FIRVerifyAssertionResponse *)response; - if (verifyAssertion.oauthIDToken.length || verifyAssertion.oauthAccessToken.length) { - credential = - [[FIROAuthCredential alloc] initWithProviderID:verifyAssertion.providerID - IDToken:verifyAssertion.oauthIDToken - accessToken:verifyAssertion.oauthAccessToken - pendingToken:verifyAssertion.pendingToken]; - } + credential = + [[FIROAuthCredential alloc] initWithVerifyAssertionResponse:verifyAssertion]; email = verifyAssertion.email; } return [FIRAuthErrorUtils credentialAlreadyInUseErrorWithMessage:serverDetailErrorMessage diff --git a/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.h index a75d0a259d4..295e2ffb153 100644 --- a/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.h +++ b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.h @@ -196,6 +196,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, strong, readonly, nullable) NSString *oauthAccessToken; +/** @property oauthSecretToken + @brief The secret for the OpenID OAuth extention. + */ +@property(nonatomic, readonly, nullable) NSString *oauthSecretToken; + /** @property pendingToken @brief The pending ID Token string. */ diff --git a/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.m index e4792414cc7..a5f23d51deb 100644 --- a/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.m +++ b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionResponse.m @@ -76,6 +76,7 @@ - (BOOL)setWithDictionary:(NSDictionary *)dictionary _oauthExpirationDate = [dictionary[@"oauthExpireIn"] isKindOfClass:[NSString class]] ? [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"oauthExpireIn"] doubleValue]] : nil; _oauthAccessToken = [dictionary[@"oauthAccessToken"] copy]; + _oauthSecretToken = [dictionary[@"oauthTokenSecret"] copy]; _pendingToken = [dictionary[@"pendingToken"] copy]; return YES; } diff --git a/Firebase/Auth/Source/Public/FIRAuthDataResult.h b/Firebase/Auth/Source/Public/FIRAuthDataResult.h index bc4fa4a2a02..f463dd7a68e 100644 --- a/Firebase/Auth/Source/Public/FIRAuthDataResult.h +++ b/Firebase/Auth/Source/Public/FIRAuthDataResult.h @@ -17,6 +17,7 @@ #import @class FIRAdditionalUserInfo; +@class FIROAuthCredential; @class FIRUser; NS_ASSUME_NONNULL_BEGIN @@ -37,7 +38,14 @@ NS_SWIFT_NAME(AuthDataResult) /** @property user @brief The signed in user. */ -@property(nonatomic, readonly) FIRUser *user; +@property(nonatomic, readonly, nullable) FIRUser *user; + +/** @property credential + @brief The updated OAuth credential after the the sign-in, link and reauthenticate action. + @detial This property is for OAuth sign in only. + */ +@property(nonatomic, readonly, nullable) FIROAuthCredential *credential; + /** @property additionalUserInfo @brief If available contains the additional IdP specific information about signed in user. diff --git a/Firebase/Auth/Source/Public/FIROAuthCredential.h b/Firebase/Auth/Source/Public/FIROAuthCredential.h index db642c5dcea..94abe4f22b4 100644 --- a/Firebase/Auth/Source/Public/FIROAuthCredential.h +++ b/Firebase/Auth/Source/Public/FIROAuthCredential.h @@ -36,6 +36,13 @@ NS_SWIFT_NAME(OAuthCredential) */ @property(nonatomic, readonly, nullable) NSString *accessToken; +/** @property secret + @brief The secret associated with this credential. This will be nil for OAuth 2.0 providers. + @detail OAuthCredential already exposes a providerId getter. This will help the developer + determine whether an access token/secret pair is needed. + */ +@property(nonatomic, readonly, nullable) NSString *secret; + /** @fn init @brief This class is not supposed to be instantiated directly. */ diff --git a/Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h b/Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h index a0d1166f1c8..a5bdc0a08da 100644 --- a/Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h @@ -23,17 +23,20 @@ NS_ASSUME_NONNULL_BEGIN /** @brief A string constant identifying the Twitter identity provider. */ -extern NSString *const FIRTwitterAuthProviderID NS_SWIFT_NAME(TwitterAuthProviderID); +extern NSString *const FIRTwitterAuthProviderID NS_SWIFT_NAME(TwitterAuthProviderID) + DEPRECATED_MSG_ATTRIBUTE("Please use \"twitter.com\" instead."); /** @brief A string constant identifying the Twitter sign-in method. */ -extern NSString *const _Nonnull FIRTwitterAuthSignInMethod NS_SWIFT_NAME(TwitterAuthSignInMethod); +extern NSString *const _Nonnull FIRTwitterAuthSignInMethod NS_SWIFT_NAME(TwitterAuthSignInMethod) + DEPRECATED_MSG_ATTRIBUTE("Please use \"twitter.com\" instead."); /** @class FIRTwitterAuthProvider @brief Utility class for constructing Twitter credentials. */ +DEPRECATED_MSG_ATTRIBUTE("Please use FIROAuthProvider instead of FIRTwitterAuthProvider.") NS_SWIFT_NAME(TwitterAuthProvider) @interface FIRTwitterAuthProvider : NSObject diff --git a/Firebase/Auth/Source/User/FIRUser.m b/Firebase/Auth/Source/User/FIRUser.m index c822595e87c..f0c4e7c992e 100644 --- a/Firebase/Auth/Source/User/FIRUser.m +++ b/Firebase/Auth/Source/User/FIRUser.m @@ -39,6 +39,7 @@ #import "FIRGetAccountInfoResponse.h" #import "FIRGetOOBConfirmationCodeRequest.h" #import "FIRGetOOBConfirmationCodeResponse.h" +#import "FIROAuthCredential_Internal.h" #import "FIRSecureTokenService.h" #import "FIRSetAccountInfoRequest.h" #import "FIRSetAccountInfoResponse.h" @@ -1110,8 +1111,12 @@ - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential } FIRAdditionalUserInfo *additionalUserInfo = [FIRAdditionalUserInfo userInfoWithVerifyAssertionResponse:response]; + FIROAuthCredential *updatedOAuthCredential = + [[FIROAuthCredential alloc] initWithVerifyAssertionResponse:response]; FIRAuthDataResult *result = - [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:additionalUserInfo]; + [[FIRAuthDataResult alloc] initWithUser:self + additionalUserInfo:additionalUserInfo + credential:updatedOAuthCredential]; // Update the new token and refresh user info again. self->_tokenService = [[FIRSecureTokenService alloc] initWithRequestConfiguration:requestConfiguration diff --git a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h index bbf726f20c4..410956bd61a 100644 --- a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h +++ b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h @@ -219,10 +219,12 @@ NS_ASSUME_NONNULL_BEGIN /** @fn accountExistsWithDifferentCredentialErrorWithEmail: @brief Constructs an @c NSError with the @c FIRAuthErrorAccountExistsWithDifferentCredential code. - @param Email The email address that is already associated with an existing account + @param email The email address that is already associated with an existing account + @param updatedCredential The updated credential for the existing account @return The NSError instance associated with the given FIRAuthError. */ -+ (NSError *)accountExistsWithDifferentCredentialErrorWithEmail:(nullable NSString *)Email; ++ (NSError *)accountExistsWithDifferentCredentialErrorWithEmail:(nullable NSString *)email + updatedCredential:(nullable FIRAuthCredential *)updatedCredential; /** @fn providerAlreadyLinkedErrorWithMessage: @brief Constructs an @c NSError with the @c FIRAuthErrorCodeProviderAlreadyLinked code. diff --git a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m index 0300e4a5f9e..5355a9f0749 100644 --- a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m +++ b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m @@ -915,12 +915,14 @@ + (NSError *)invalidEmailErrorWithMessage:(nullable NSString *)message { return [self errorWithCode:FIRAuthInternalErrorCodeInvalidEmail message:message]; } -+ (NSError *)accountExistsWithDifferentCredentialErrorWithEmail:(nullable NSString *)email { - NSDictionary *userInfo; - if (email.length) { - userInfo = @{ - FIRAuthErrorUserInfoEmailKey : email, - }; ++ (NSError *)accountExistsWithDifferentCredentialErrorWithEmail:(nullable NSString *)email + updatedCredential:(nullable FIRAuthCredential *)updatedCredential { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + if (email) { + userInfo[FIRAuthErrorUserInfoEmailKey] = email; + } + if (updatedCredential) { + userInfo[FIRAuthErrorUserInfoUpdatedCredentialKey] = updatedCredential; } return [self errorWithCode:FIRAuthInternalErrorCodeAccountExistsWithDifferentCredential userInfo:userInfo]; From 82ced2d8c1418758907248b256520154bb576a51 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Thu, 4 Apr 2019 14:47:59 -0400 Subject: [PATCH 139/214] Add FSTDelegateValue which delegates to model::FieldValue (#2681) * Fix FSTFieldValue instanceof check to use FSTFieldValue.type Missed in the previous PR because this gets the class via [value class] rather than [value isKindOfClass]. * Add FSTDelegateValue which delegates to model::FieldValue Also migrates FSTBooleanValue to FSTDelegateValue as a proof-of-concept. Other FSTXValues still TODO. --- .../Example/Tests/Model/FSTFieldValueTests.mm | 14 +- Firestore/Source/API/FSTUserDataConverter.mm | 2 +- Firestore/Source/Model/FSTFieldValue.h | 16 +- Firestore/Source/Model/FSTFieldValue.mm | 188 +++++++++++------- Firestore/Source/Remote/FSTSerializerBeta.mm | 80 ++++---- .../firebase/firestore/model/field_value.cc | 25 +++ .../firebase/firestore/model/field_value.h | 10 + .../firebase/firestore/model/field_value.mm | 23 +++ .../firebase/firestore/testutil/xcgmock.h | 2 +- 9 files changed, 229 insertions(+), 131 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/model/field_value.mm diff --git a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm index 94b6807fef6..9bbc18481d3 100644 --- a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm +++ b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm @@ -124,7 +124,7 @@ - (void)testWrapsBooleans { NSArray *values = @[ @YES, @NO ]; for (id value in values) { FSTFieldValue *wrapped = FSTTestFieldValue(value); - XCTAssertEqualObjects([wrapped class], [FSTBooleanValue class]); + XCTAssertEqualObjects([wrapped class], [FSTDelegateValue class]); XCTAssertEqualObjects([wrapped value], value); XCTAssertEqual(wrapped.type, FieldValue::Type::Boolean); } @@ -287,7 +287,7 @@ - (void)testWrapsSimpleObjects { FSTObjectValue *expected = [[FSTObjectValue alloc] initWithDictionary:@{ @"a" : [FSTStringValue stringValue:@"foo"], @"b" : [FSTIntegerValue integerValue:1LL], - @"c" : [FSTBooleanValue trueValue], + @"c" : FieldValue::True().Wrap(), @"d" : [FSTNullValue nullValue] }]; XCTAssertEqualObjects(actual, expected); @@ -300,7 +300,7 @@ - (void)testWrapsNestedObjects { @"a" : [[FSTObjectValue alloc] initWithDictionary:@{ @"b" : [[FSTObjectValue alloc] initWithDictionary:@{@"c" : [FSTStringValue stringValue:@"foo"]}], - @"d" : [FSTBooleanValue booleanValue:YES] + @"d" : FieldValue::True().Wrap() }] }]; XCTAssertEqualObjects(actual, expected); @@ -312,7 +312,7 @@ - (void)testExtractsFields { FSTAssertIsKindOfClass(obj, FSTObjectValue); FSTAssertIsKindOfClass([obj valueForPath:testutil::Field("foo")], FSTObjectValue); - XCTAssertEqualObjects([obj valueForPath:testutil::Field("foo.a")], [FSTBooleanValue trueValue]); + XCTAssertEqualObjects([obj valueForPath:testutil::Field("foo.a")], FieldValue::True().Wrap()); XCTAssertEqualObjects([obj valueForPath:testutil::Field("foo.b")], [FSTStringValue stringValue:@"string"]); @@ -424,7 +424,7 @@ - (void)testDeletesNestedKeys { - (void)testArrays { FSTArrayValue *expected = [[FSTArrayValue alloc] - initWithValueNoCopy:@[ [FSTStringValue stringValue:@"value"], [FSTBooleanValue trueValue] ]]; + initWithValueNoCopy:@[ [FSTStringValue stringValue:@"value"], FieldValue::True().Wrap() ]]; FSTArrayValue *actual = (FSTArrayValue *)FSTTestFieldValue(@[ @"value", @YES ]); XCTAssertEqualObjects(actual, expected); @@ -434,8 +434,8 @@ - (void)testArrays { - (void)testValueEquality { DatabaseId database_id = DatabaseId("project", DatabaseId::kDefault); NSArray *groups = @[ - @[ FSTTestFieldValue(@YES), [FSTBooleanValue booleanValue:YES] ], - @[ FSTTestFieldValue(@NO), [FSTBooleanValue booleanValue:NO] ], + @[ FSTTestFieldValue(@YES), FieldValue::True().Wrap() ], + @[ FSTTestFieldValue(@NO), FieldValue::False().Wrap() ], @[ FSTTestFieldValue([NSNull null]), [FSTNullValue nullValue] ], @[ FSTTestFieldValue(@(0.0 / 0.0)), FSTTestFieldValue(@(NAN)), [FSTDoubleValue nanValue] ], // -0.0 and 0.0 compare: the same (but are not isEqual:) diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm index 9563a751fcb..a0f5ff85afa 100644 --- a/Firestore/Source/API/FSTUserDataConverter.mm +++ b/Firestore/Source/API/FSTUserDataConverter.mm @@ -426,7 +426,7 @@ - (nullable FSTFieldValue *)parseScalarValue:(nullable id)input context:(ParseCo // legitimate usage of signed chars is impossible, but this should be rare. // // Additionally, for consistency, map unsigned chars to bools in the same way. - return [FSTBooleanValue booleanValue:[input boolValue]]; + return FieldValue::FromBoolean([input boolValue]).Wrap(); default: // All documented codes should be handled above, so this shouldn't happen. diff --git a/Firestore/Source/Model/FSTFieldValue.h b/Firestore/Source/Model/FSTFieldValue.h index bf00c3875c5..98bd07f156e 100644 --- a/Firestore/Source/Model/FSTFieldValue.h +++ b/Firestore/Source/Model/FSTFieldValue.h @@ -125,15 +125,6 @@ enum class ServerTimestampBehavior { None, Estimate, Previous }; + (instancetype)nullValue; @end -/** - * A boolean value stored in Firestore. - */ -@interface FSTBooleanValue : FSTFieldValue -+ (instancetype)trueValue; -+ (instancetype)falseValue; -+ (instancetype)booleanValue:(BOOL)value; -@end - /** * Base class inherited from by FSTIntegerValue and FSTDoubleValue. It implements proper number * comparisons between the two types. @@ -288,4 +279,11 @@ enum class ServerTimestampBehavior { None, Estimate, Previous }; @end +/** + * A value that delegates to the c++ model::FieldValue. + */ +@interface FSTDelegateValue : FSTFieldValue ++ (instancetype)delegateWithValue:(FieldValue &&)value; +@end + NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Model/FSTFieldValue.mm b/Firestore/Source/Model/FSTFieldValue.mm index 92c351f8be3..75fb0995a19 100644 --- a/Firestore/Source/Model/FSTFieldValue.mm +++ b/Firestore/Source/Model/FSTFieldValue.mm @@ -16,6 +16,9 @@ #import "Firestore/Source/Model/FSTFieldValue.h" +#include +#include + #import "FIRDocumentSnapshot.h" #import "FIRTimestamp.h" @@ -161,77 +164,6 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { @end -#pragma mark - FSTBooleanValue - -@interface FSTBooleanValue () -@property(nonatomic, assign, readonly) BOOL internalValue; -@end - -@implementation FSTBooleanValue - -+ (instancetype)trueValue { - static FSTBooleanValue *sharedInstance = nil; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - sharedInstance = [[FSTBooleanValue alloc] initWithValue:YES]; - }); - return sharedInstance; -} - -+ (instancetype)falseValue { - static FSTBooleanValue *sharedInstance = nil; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - sharedInstance = [[FSTBooleanValue alloc] initWithValue:NO]; - }); - return sharedInstance; -} - -+ (instancetype)booleanValue:(BOOL)value { - return value ? [FSTBooleanValue trueValue] : [FSTBooleanValue falseValue]; -} - -- (id)initWithValue:(BOOL)value { - self = [super init]; - if (self) { - _internalValue = value; - } - return self; -} - -- (FieldValue::Type)type { - return FieldValue::Type::Boolean; -} - -- (FSTTypeOrder)typeOrder { - return FSTTypeOrderBoolean; -} - -- (id)value { - return self.internalValue ? @YES : @NO; -} - -- (BOOL)isEqual:(id)other { - // Since we create shared instances for true / false, we can use reference equality. - return self == other; -} - -- (NSUInteger)hash { - return self.internalValue ? 1231 : 1237; -} - -- (NSComparisonResult)compare:(FSTFieldValue *)other { - if (other.type == FieldValue::Type::Boolean) { - return WrapCompare(self.internalValue, ((FSTBooleanValue *)other).internalValue); - } else { - return [self defaultCompare:other]; - } -} - -@end - #pragma mark - FSTNumberValue @implementation FSTNumberValue @@ -1027,4 +959,118 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { @end +@interface FSTDelegateValue () +@property(nonatomic, assign, readonly) FieldValue internalValue; +@end + +@implementation FSTDelegateValue ++ (instancetype)delegateWithValue:(FieldValue &&)value { + return [[FSTDelegateValue alloc] initWithValue:std::move(value)]; +} + +- (id)initWithValue:(FieldValue &&)value { + self = [super init]; + if (self) { + _internalValue = std::move(value); + } + return self; +} + +- (FieldValue::Type)type { + return self.internalValue.type(); +} + +- (FSTTypeOrder)typeOrder { + switch (self.internalValue.type()) { + case FieldValue::Type::Null: + return FSTTypeOrderNull; + case FieldValue::Type::Boolean: + return FSTTypeOrderBoolean; + case FieldValue::Type::Integer: + case FieldValue::Type::Double: + return FSTTypeOrderNumber; + case FieldValue::Type::Timestamp: + case FieldValue::Type::ServerTimestamp: + return FSTTypeOrderTimestamp; + case FieldValue::Type::String: + return FSTTypeOrderString; + case FieldValue::Type::Blob: + return FSTTypeOrderBlob; + case FieldValue::Type::Reference: + return FSTTypeOrderReference; + case FieldValue::Type::GeoPoint: + return FSTTypeOrderGeoPoint; + case FieldValue::Type::Array: + return FSTTypeOrderArray; + case FieldValue::Type::Object: + return FSTTypeOrderObject; + } + UNREACHABLE(); +} + +- (BOOL)isEqual:(id)other { + // TODO(rsgowman): Port the other FST*Value's, and then remove this comment: + // + // Simplification: We'll assume that (eg) FSTBooleanValue(true) != + // FSTDelegateValue(FieldValue::FromBoolean(true)). That's not great. We'll + // handle this by ensuring that we remove (eg) FSTBooleanValue at the same + // time that FSTDelegateValue handles (eg) booleans to ensure this case never + // occurs. + + if (other == self) { + return YES; + } + if (![other isKindOfClass:[self class]]) { + return NO; + } + + return self.internalValue == ((FSTDelegateValue *)other).internalValue; +} + +- (id)value { + switch (self.internalValue.type()) { + case FieldValue::Type::Null: + HARD_FAIL("TODO(rsgowman): implement"); + case FieldValue::Type::Boolean: + return self.internalValue.boolean_value() ? @YES : @NO; + case FieldValue::Type::Integer: + case FieldValue::Type::Double: + case FieldValue::Type::Timestamp: + case FieldValue::Type::ServerTimestamp: + case FieldValue::Type::String: + case FieldValue::Type::Blob: + case FieldValue::Type::Reference: + case FieldValue::Type::GeoPoint: + case FieldValue::Type::Array: + case FieldValue::Type::Object: + HARD_FAIL("TODO(rsgowman): implement"); + } + UNREACHABLE(); +} + +- (NSComparisonResult)compare:(FSTFieldValue *)other { + // TODO(rsgowman): Port the other FST*Value's, and then remove this comment: + // + // Simplification: We'll assume that if Comparable(self.type, other.type), + // then other must be a FSTDelegateValue. That's not great. We'll handle this + // by ensuring that we remove (eg) FSTBooleanValue at the same time that + // FSTDelegateValue handles (eg) booleans to ensure this case never occurs. + + if (FieldValue::Comparable(self.type, other.type)) { + HARD_ASSERT([other isKindOfClass:[FSTDelegateValue class]]); + return WrapCompare(self.internalValue, ((FSTDelegateValue *)other).internalValue); + } else { + return [self defaultCompare:other]; + } +} + +- (NSUInteger)hash { + return self.internalValue.Hash(); +} + +@end + +template <> +struct Comparator : public std::less {}; + NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm index e1115729fd8..4537c8a4775 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.mm +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -199,49 +199,45 @@ - (NSString *)encodedDatabaseID { #pragma mark - FSTFieldValue <=> Value proto - (GCFSValue *)encodedFieldValue:(FSTFieldValue *)fieldValue { - Class fieldClass = [fieldValue class]; - if (fieldClass == [FSTNullValue class]) { - return [self encodedNull]; - - } else if (fieldClass == [FSTBooleanValue class]) { - return [self encodedBool:[[fieldValue value] boolValue]]; - - } else if (fieldClass == [FSTIntegerValue class]) { - return [self encodedInteger:[[fieldValue value] longLongValue]]; - - } else if (fieldClass == [FSTDoubleValue class]) { - return [self encodedDouble:[[fieldValue value] doubleValue]]; - - } else if (fieldClass == [FSTStringValue class]) { - return [self encodedString:[fieldValue value]]; - - } else if (fieldClass == [FSTTimestampValue class]) { - FIRTimestamp *value = static_cast([fieldValue value]); - return [self encodedTimestampValue:Timestamp{value.seconds, value.nanoseconds}]; - } else if (fieldClass == [FSTGeoPointValue class]) { - return [self encodedGeoPointValue:[fieldValue value]]; - - } else if (fieldClass == [FSTBlobValue class]) { - return [self encodedBlobValue:[fieldValue value]]; - - } else if (fieldClass == [FSTReferenceValue class]) { - FSTReferenceValue *ref = (FSTReferenceValue *)fieldValue; - DocumentKey key = [[ref value] key]; - return [self encodedReferenceValueForDatabaseID:[ref databaseID] key:key]; - - } else if (fieldClass == [FSTObjectValue class]) { - GCFSValue *result = [GCFSValue message]; - result.mapValue = [self encodedMapValue:(FSTObjectValue *)fieldValue]; - return result; - - } else if (fieldClass == [FSTArrayValue class]) { - GCFSValue *result = [GCFSValue message]; - result.arrayValue = [self encodedArrayValue:(FSTArrayValue *)fieldValue]; - return result; + switch (fieldValue.type) { + case FieldValue::Type::Null: + return [self encodedNull]; + case FieldValue::Type::Boolean: + return [self encodedBool:[[fieldValue value] boolValue]]; + case FieldValue::Type::Integer: + return [self encodedInteger:[[fieldValue value] longLongValue]]; + case FieldValue::Type::Double: + return [self encodedDouble:[[fieldValue value] doubleValue]]; + case FieldValue::Type::Timestamp: { + FIRTimestamp *value = static_cast([fieldValue value]); + return [self encodedTimestampValue:Timestamp{value.seconds, value.nanoseconds}]; + } + case FieldValue::Type::String: + return [self encodedString:[fieldValue value]]; + case FieldValue::Type::Blob: + return [self encodedBlobValue:[fieldValue value]]; + case FieldValue::Type::Reference: { + FSTReferenceValue *ref = (FSTReferenceValue *)fieldValue; + DocumentKey key = [[ref value] key]; + return [self encodedReferenceValueForDatabaseID:[ref databaseID] key:key]; + } + case FieldValue::Type::GeoPoint: + return [self encodedGeoPointValue:[fieldValue value]]; + case FieldValue::Type::Array: { + GCFSValue *result = [GCFSValue message]; + result.arrayValue = [self encodedArrayValue:(FSTArrayValue *)fieldValue]; + return result; + } + case FieldValue::Type::Object: { + GCFSValue *result = [GCFSValue message]; + result.mapValue = [self encodedMapValue:(FSTObjectValue *)fieldValue]; + return result; + } - } else { - HARD_FAIL("Unhandled type %s on %s", NSStringFromClass([fieldValue class]), fieldValue); + case FieldValue::Type::ServerTimestamp: + HARD_FAIL("Unhandled type %s on %s", NSStringFromClass([fieldValue class]), fieldValue); } + UNREACHABLE(); } - (FSTFieldValue *)decodedFieldValue:(GCFSValue *)valueProto { @@ -250,7 +246,7 @@ - (FSTFieldValue *)decodedFieldValue:(GCFSValue *)valueProto { return [FSTNullValue nullValue]; case GCFSValue_ValueType_OneOfCase_BooleanValue: - return [FSTBooleanValue booleanValue:valueProto.booleanValue]; + return FieldValue::FromBoolean(valueProto.booleanValue).Wrap(); case GCFSValue_ValueType_OneOfCase_IntegerValue: return [FSTIntegerValue integerValue:valueProto.integerValue]; diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc index fee10451e9a..a018f8a3911 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.cc +++ b/Firestore/core/src/firebase/firestore/model/field_value.cc @@ -26,6 +26,7 @@ #include "Firestore/core/src/firebase/firestore/immutable/sorted_map.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hashing.h" #include "absl/memory/memory.h" using firebase::firestore::util::Comparator; @@ -347,6 +348,30 @@ FieldValue FieldValue::FromMap(FieldValue::Map&& value) { return result; } +size_t FieldValue::Hash() const { + switch (type()) { + case FieldValue::Type::Null: + HARD_FAIL("TODO(rsgowman): Implement"); + + case FieldValue::Type::Boolean: + return util::Hash(boolean_value()); + + case FieldValue::Type::Integer: + case FieldValue::Type::Double: + case FieldValue::Type::Timestamp: + case FieldValue::Type::ServerTimestamp: + case FieldValue::Type::String: + case FieldValue::Type::Blob: + case FieldValue::Type::Reference: + case FieldValue::Type::GeoPoint: + case FieldValue::Type::Array: + case FieldValue::Type::Object: + HARD_FAIL("TODO(rsgowman): Implement"); + } + + UNREACHABLE(); +} + bool operator<(const FieldValue::Map& lhs, const FieldValue::Map& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h index c91a1688b90..0ed39d5f214 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.h +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -32,6 +32,10 @@ #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/types/optional.h" +#if __OBJC__ +@class FSTFieldValue; +#endif // __OBJC__ + namespace firebase { namespace firestore { namespace model { @@ -101,6 +105,10 @@ class FieldValue { FieldValue& operator=(const FieldValue& value); FieldValue& operator=(FieldValue&& value); +#if __OBJC__ + FSTFieldValue* Wrap() &&; +#endif // __OBJC__ + /** Returns the true type for this value. */ Type type() const { return tag_; @@ -182,6 +190,8 @@ class FieldValue { static FieldValue FromMap(const Map& value); static FieldValue FromMap(Map&& value); + size_t Hash() const; + friend bool operator<(const FieldValue& lhs, const FieldValue& rhs); private: diff --git a/Firestore/core/src/firebase/firestore/model/field_value.mm b/Firestore/core/src/firebase/firestore/model/field_value.mm new file mode 100644 index 00000000000..d914e467078 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_value.mm @@ -0,0 +1,23 @@ +/* + * Copyright 2019 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_value.h" + +#import "Firestore/Source/Model/FSTFieldValue.h" + +FSTFieldValue* FieldValue::Wrap() && { + return [FSTDelegateValue delegateWithValue:std::move(*this)]; +} diff --git a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h index 37c75806231..14ecb206e3f 100644 --- a/Firestore/core/test/firebase/firestore/testutil/xcgmock.h +++ b/Firestore/core/test/firebase/firestore/testutil/xcgmock.h @@ -145,8 +145,8 @@ OBJC_PRINT_TO(FSTArrayRemoveFieldValue); OBJC_PRINT_TO(FSTArrayUnionFieldValue); OBJC_PRINT_TO(FSTArrayValue); OBJC_PRINT_TO(FSTBlobValue); -OBJC_PRINT_TO(FSTBooleanValue); OBJC_PRINT_TO(FSTBound); +OBJC_PRINT_TO(FSTDelegateValue); OBJC_PRINT_TO(FSTDeleteFieldValue); OBJC_PRINT_TO(FSTDeleteMutation); OBJC_PRINT_TO(FSTDeletedDocument); From f7e893c2811fb9b0574003528436da9b8edbc3d8 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Thu, 4 Apr 2019 13:53:48 -0700 Subject: [PATCH 140/214] Don't init FIRAuthStoredUserManager with a nil keychainServiceName. (#2721) This avoids a crash we're running into in the Firestore unit tests (I think there's a race in this code related to Firestore deleting the FirebaseApp instance at the end of each test). --- Firebase/Auth/Source/Auth/FIRAuth.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Firebase/Auth/Source/Auth/FIRAuth.m b/Firebase/Auth/Source/Auth/FIRAuth.m index 372cef61025..7221471fb6c 100644 --- a/Firebase/Auth/Source/Auth/FIRAuth.m +++ b/Firebase/Auth/Source/Auth/FIRAuth.m @@ -395,11 +395,10 @@ - (nullable instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)a [FIRAuth keychainServiceNameForAppName:strongSelf->_firebaseAppName]; if (keychainServiceName) { strongSelf->_keychain = [[FIRAuthKeychain alloc] initWithService:keychainServiceName]; + strongSelf.storedUserManager = + [[FIRAuthStoredUserManager alloc] initWithServiceName:keychainServiceName]; } - strongSelf.storedUserManager = - [[FIRAuthStoredUserManager alloc] initWithServiceName:keychainServiceName]; - NSError *error; NSString *storedUserAccessGroup = [strongSelf.storedUserManager getStoredUserAccessGroupWithError:&error]; if (!error) { From 10fb730818b3c3e2236a9e01a28510837d99a7f6 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Thu, 4 Apr 2019 14:36:42 -0700 Subject: [PATCH 141/214] Update FIRAuthSettings.h (#2722) --- Firebase/Auth/Source/Public/FIRAuthSettings.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Firebase/Auth/Source/Public/FIRAuthSettings.h b/Firebase/Auth/Source/Public/FIRAuthSettings.h index 55097777f3b..4ac7ce87621 100644 --- a/Firebase/Auth/Source/Public/FIRAuthSettings.h +++ b/Firebase/Auth/Source/Public/FIRAuthSettings.h @@ -21,6 +21,7 @@ NS_ASSUME_NONNULL_BEGIN /** @class FIRAuthSettings @brief Determines settings related to an auth object. */ +NS_SWIFT_NAME(AuthSettings) @interface FIRAuthSettings : NSObject /** @property appVerificationDisabledForTesting From 0aa81c9032ec291a9f563157f4aaf475980e5b0c Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Thu, 4 Apr 2019 17:57:43 -0400 Subject: [PATCH 142/214] Revert "Revert "GULAppDelegateSwizzler - remote notifications methods and tvOS support (#2698)"" (#2726) This reverts commit a3a2267fe91e81c3e1528c2497491ce1c593227b. --- .../GULAppDelegateSwizzler.m | 244 ++++++++++++++++-- GoogleUtilities/Common/GULLoggerCodes.h | 29 ++- .../GoogleUtilities.xcodeproj/project.pbxproj | 6 +- GoogleUtilities/Example/Podfile | 20 +- .../Swizzler/GULAppDelegateSwizzlerTest.m | 193 +++++++++++++- 5 files changed, 450 insertions(+), 42 deletions(-) diff --git a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m index 8bbabad3778..368e7b71dbc 100644 --- a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m +++ b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m @@ -14,7 +14,7 @@ #import "TargetConditionals.h" -#if TARGET_OS_IOS +#if TARGET_OS_IOS || TARGET_OS_TV #import #import @@ -47,14 +47,39 @@ typedef BOOL (*GULRealContinueUserActivityIMP)( id, SEL, UIApplication *, NSUserActivity *, void (^)(NSArray *restorableObjects)); #pragma clang diagnostic pop +typedef void (*GULRealDidRegisterForRemoteNotificationsIMP)(id, SEL, UIApplication *, NSData *); + +typedef void (*GULRealDidFailToRegisterForRemoteNotificationsIMP)(id, + SEL, + UIApplication *, + NSError *); + +typedef void (*GULRealDidReceiveRemoteNotificationIMP)(id, SEL, UIApplication *, NSDictionary *); + +typedef void (*GULRealDidReceiveRemoteNotificationWithCompletionIMP)( + id, SEL, UIApplication *, NSDictionary *, void (^)(UIBackgroundFetchResult)); + typedef void (^GULAppDelegateInterceptorCallback)(id); // The strings below are the keys for associated objects. static char const *const kGULContinueUserActivityIMPKey = "GUL_continueUserActivityIMP"; static char const *const kGULHandleBackgroundSessionIMPKey = "GUL_handleBackgroundSessionIMP"; static char const *const kGULOpenURLOptionsIMPKey = "GUL_openURLOptionsIMP"; + +static char const *const kGULRealDidRegisterForRemoteNotificationsIMPKey = + "GUL_didRegisterForRemoteNotificationsIMP"; +static char const *const kGULRealDidFailToRegisterForRemoteNotificationsIMPKey = + "GUL_didFailToRegisterForRemoteNotificationsIMP"; +static char const *const kGULRealDidReceiveRemoteNotificationIMPKey = + "GUL_didReceiveRemoteNotificationIMP"; +static char const *const kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey = + "GUL_didReceiveRemoteNotificationWithCompletionIMP"; +#if TARGET_OS_IOS +// The method application:openURL:sourceApplication:annotation: is not available on tvOS static char const *const kGULOpenURLOptionsSourceAnnotationsIMPKey = "GUL_openURLSourceApplicationAnnotationIMP"; +#endif // TARGET_OS_IOS + static char const *const kGULRealClassKey = "GUL_realClass"; static NSString *const kGULAppDelegateKeyPath = @"delegate"; @@ -333,19 +358,6 @@ + (void)createSubclassWithObject:(id)anObject { fromClass:realClass]; NSValue *continueUserActivityIMPPointer = [NSValue valueWithPointer:continueUserActivityIMP]; - // For application:openURL:sourceApplication:annotation: - SEL openURLSourceApplicationAnnotationSEL = @selector(application: - openURL:sourceApplication:annotation:); - [GULAppDelegateSwizzler addInstanceMethodWithSelector:openURLSourceApplicationAnnotationSEL - fromClass:[GULAppDelegateSwizzler class] - toClass:appDelegateSubClass]; - GULRealOpenURLSourceApplicationAnnotationIMP openURLSourceApplicationAnnotationIMP = - (GULRealOpenURLSourceApplicationAnnotationIMP)[GULAppDelegateSwizzler - implementationOfMethodSelector:openURLSourceApplicationAnnotationSEL - fromClass:realClass]; - NSValue *openURLSourceAppAnnotationIMPPointer = - [NSValue valueWithPointer:openURLSourceApplicationAnnotationIMP]; - // For application:handleEventsForBackgroundURLSession:completionHandler: SEL handleEventsForBackgroundURLSessionSEL = @selector(application: handleEventsForBackgroundURLSession:completionHandler:); @@ -359,6 +371,83 @@ + (void)createSubclassWithObject:(id)anObject { NSValue *handleBackgroundSessionIMPPointer = [NSValue valueWithPointer:handleBackgroundSessionIMP]; + // For application:didRegisterForRemoteNotificationsWithDeviceToken: + SEL didRegisterForRemoteNotificationsSEL = @selector(application: + didRegisterForRemoteNotificationsWithDeviceToken:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:didRegisterForRemoteNotificationsSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidRegisterForRemoteNotificationsIMP didRegisterForRemoteNotificationsIMP = + (GULRealDidRegisterForRemoteNotificationsIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:didRegisterForRemoteNotificationsSEL + fromClass:realClass]; + NSValue *didRegisterForRemoteNotificationsIMPPointer = + [NSValue valueWithPointer:didRegisterForRemoteNotificationsIMP]; + + // For application:didFailToRegisterForRemoteNotificationsWithError: + SEL didFailToRegisterForRemoteNotificationsSEL = @selector(application: + didFailToRegisterForRemoteNotificationsWithError:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:didFailToRegisterForRemoteNotificationsSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidFailToRegisterForRemoteNotificationsIMP didFailToRegisterForRemoteNotificationsIMP = + (GULRealDidFailToRegisterForRemoteNotificationsIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:didFailToRegisterForRemoteNotificationsSEL + fromClass:realClass]; + NSValue *didFailToRegisterForRemoteNotificationsIMPPointer = + [NSValue valueWithPointer:didFailToRegisterForRemoteNotificationsIMP]; + + // For application:didReceiveRemoteNotification: + SEL didReceiveRemoteNotificationSEL = @selector(application:didReceiveRemoteNotification:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:didReceiveRemoteNotificationSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidReceiveRemoteNotificationIMP didReceiveRemoteNotificationIMP = + (GULRealDidReceiveRemoteNotificationIMP) + [GULAppDelegateSwizzler implementationOfMethodSelector:didReceiveRemoteNotificationSEL + fromClass:realClass]; + NSValue *didReceiveRemoteNotificationIMPPointer = + [NSValue valueWithPointer:didReceiveRemoteNotificationIMP]; + + // For application:didReceiveRemoteNotification:fetchCompletionHandler: + NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer; + SEL didReceiveRemoteNotificationWithCompletionSEL = + @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); + if ([anObject respondsToSelector:didReceiveRemoteNotificationWithCompletionSEL]) { + // Only add the application:didReceiveRemoteNotification:fetchCompletionHandler: method if + // the original AppDelegate implements it. + // This fixes a bug if an app only implements application:didReceiveRemoteNotification: + // (if we add the method with completion, iOS sees that one exists and does not call + // the method without the completion, which in this case is the only one the app implements). + + [GULAppDelegateSwizzler + addInstanceMethodWithSelector:didReceiveRemoteNotificationWithCompletionSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidReceiveRemoteNotificationWithCompletionIMP + didReceiveRemoteNotificationWithCompletionIMP = + (GULRealDidReceiveRemoteNotificationWithCompletionIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:didReceiveRemoteNotificationWithCompletionSEL + fromClass:realClass]; + didReceiveRemoteNotificationWithCompletionIMPPointer = + [NSValue valueWithPointer:didReceiveRemoteNotificationWithCompletionIMP]; + } + +#if TARGET_OS_IOS + // For application:openURL:sourceApplication:annotation: + SEL openURLSourceApplicationAnnotationSEL = @selector(application: + openURL:sourceApplication:annotation:); + [GULAppDelegateSwizzler addInstanceMethodWithSelector:openURLSourceApplicationAnnotationSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealOpenURLSourceApplicationAnnotationIMP openURLSourceApplicationAnnotationIMP = + (GULRealOpenURLSourceApplicationAnnotationIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:openURLSourceApplicationAnnotationSEL + fromClass:realClass]; + NSValue *openURLSourceAppAnnotationIMPPointer = + [NSValue valueWithPointer:openURLSourceApplicationAnnotationIMP]; +#endif // TARGET_OS_IOS + // Override the description too so the custom class name will not show up. [GULAppDelegateSwizzler addInstanceMethodWithDestinationSelector:@selector(description) withImplementationFromSourceSelector:@selector(fakeDescription) @@ -374,8 +463,24 @@ + (void)createSubclassWithObject:(id)anObject { objc_setAssociatedObject(anObject, &kGULOpenURLOptionsIMPKey, openURLOptionsIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } + + objc_setAssociatedObject(anObject, &kGULRealDidRegisterForRemoteNotificationsIMPKey, + didRegisterForRemoteNotificationsIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(anObject, &kGULRealDidFailToRegisterForRemoteNotificationsIMPKey, + didFailToRegisterForRemoteNotificationsIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationIMPKey, + didReceiveRemoteNotificationIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey, + didReceiveRemoteNotificationWithCompletionIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +#if TARGET_OS_IOS objc_setAssociatedObject(anObject, &kGULOpenURLOptionsSourceAnnotationsIMPKey, openURLSourceAppAnnotationIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +#endif // TARGET_OS_IOS + objc_setAssociatedObject(anObject, &kGULRealClassKey, realClass, OBJC_ASSOCIATION_RETAIN_NONATOMIC); @@ -557,6 +662,8 @@ - (BOOL)application:(UIApplication *)application return returnedValue; } +#if TARGET_OS_IOS + - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication @@ -587,6 +694,8 @@ - (BOOL)application:(UIApplication *)application return returnedValue; } +#endif // TARGET_OS_IOS + #pragma mark - [Donor Methods] Network overridden handler methods #pragma clang diagnostic push @@ -647,7 +756,112 @@ - (BOOL)application:(UIApplication *)application } #pragma clang diagnostic pop +#pragma mark - [Donor Methods] Remote Notifications + +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + NSValue *didRegisterForRemoteNotificationsIMPPointer = + objc_getAssociatedObject(self, &kGULRealDidRegisterForRemoteNotificationsIMPKey); + GULRealDidRegisterForRemoteNotificationsIMP didRegisterForRemoteNotificationsIMP = + [didRegisterForRemoteNotificationsIMPPointer pointerValue]; + + // Notify interceptors. + SEL methodSelector = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); + [GULAppDelegateSwizzler + notifyInterceptorsWithMethodSelector:methodSelector + callback:^(id interceptor) { + [interceptor application:application + didRegisterForRemoteNotificationsWithDeviceToken: + deviceToken]; + }]; + // Call the real implementation if the real App Delegate has any. + if (didRegisterForRemoteNotificationsIMP) { + didRegisterForRemoteNotificationsIMP(self, methodSelector, application, deviceToken); + } +} + +- (void)application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + NSValue *didFailToRegisterForRemoteNotificationsIMPPointer = + objc_getAssociatedObject(self, &kGULRealDidFailToRegisterForRemoteNotificationsIMPKey); + GULRealDidFailToRegisterForRemoteNotificationsIMP didFailToRegisterForRemoteNotificationsIMP = + [didFailToRegisterForRemoteNotificationsIMPPointer pointerValue]; + + // Notify interceptors. + SEL methodSelector = @selector(application:didFailToRegisterForRemoteNotificationsWithError:); + [GULAppDelegateSwizzler + notifyInterceptorsWithMethodSelector:methodSelector + callback:^(id interceptor) { + [interceptor application:application + didFailToRegisterForRemoteNotificationsWithError:error]; + }]; + // Call the real implementation if the real App Delegate has any. + if (didFailToRegisterForRemoteNotificationsIMP) { + didFailToRegisterForRemoteNotificationsIMP(self, methodSelector, application, error); + } +} + +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer = + objc_getAssociatedObject(self, &kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey); + GULRealDidReceiveRemoteNotificationWithCompletionIMP + didReceiveRemoteNotificationWithCompletionIMP = + [didReceiveRemoteNotificationWithCompletionIMPPointer pointerValue]; + + // Notify interceptors. + SEL methodSelector = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); + [GULAppDelegateSwizzler + notifyInterceptorsWithMethodSelector:methodSelector + callback:^(id interceptor) { + [interceptor application:application + didReceiveRemoteNotification:userInfo + fetchCompletionHandler:completionHandler]; + }]; + // Call the real implementation if the real App Delegate has any. + if (didReceiveRemoteNotificationWithCompletionIMP) { + didReceiveRemoteNotificationWithCompletionIMP(self, methodSelector, application, userInfo, + completionHandler); + } +} + +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo { + NSValue *didReceiveRemoteNotificationIMPPointer = + objc_getAssociatedObject(self, &kGULRealDidReceiveRemoteNotificationIMPKey); + GULRealDidReceiveRemoteNotificationIMP didReceiveRemoteNotificationIMP = + [didReceiveRemoteNotificationIMPPointer pointerValue]; + + // Notify interceptors. + SEL methodSelector = @selector(application:didReceiveRemoteNotification:); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [GULAppDelegateSwizzler + notifyInterceptorsWithMethodSelector:methodSelector + callback:^(id interceptor) { + [interceptor application:application + didReceiveRemoteNotification:userInfo]; + }]; +#pragma clang diagnostic pop + // Call the real implementation if the real App Delegate has any. + if (didReceiveRemoteNotificationIMP) { + didReceiveRemoteNotificationIMP(self, methodSelector, application, userInfo); + } +} + + (void)proxyAppDelegate:(id)appDelegate { + if (![appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) { + GULLogNotice( + kGULLoggerSwizzler, NO, + [NSString + stringWithFormat:@"I-SWZ%06ld", + (long)kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate], + @"App Delegate does not conform to UIApplicationDelegate protocol. %@", + [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]); + return; + } + id originalDelegate = appDelegate; // Do not create a subclass if it is not enabled. if (![GULAppDelegateSwizzler isAppDelegateProxyEnabled]) { @@ -714,4 +928,4 @@ + (void)resetProxyOriginalDelegateOnceToken { @end -#endif // TARGET_OS_IOS +#endif // TARGET_OS_IOS || TARGET_OS_TV diff --git a/GoogleUtilities/Common/GULLoggerCodes.h b/GoogleUtilities/Common/GULLoggerCodes.h index b71c03797cd..fd22ba637a2 100644 --- a/GoogleUtilities/Common/GULLoggerCodes.h +++ b/GoogleUtilities/Common/GULLoggerCodes.h @@ -16,20 +16,21 @@ typedef NS_ENUM(NSInteger, GULSwizzlerMessageCode) { // App Delegate Swizzling. - kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000, // I-SWZ001000 - kGULSwizzlerMessageCodeAppDelegateSwizzling001 = 1001, // I-SWZ001001 - kGULSwizzlerMessageCodeAppDelegateSwizzling002 = 1002, // I-SWZ001002 - kGULSwizzlerMessageCodeAppDelegateSwizzling003 = 1003, // I-SWZ001003 - kGULSwizzlerMessageCodeAppDelegateSwizzling004 = 1004, // I-SWZ001004 - kGULSwizzlerMessageCodeAppDelegateSwizzling005 = 1005, // I-SWZ001005 - kGULSwizzlerMessageCodeAppDelegateSwizzling006 = 1006, // I-SWZ001006 - kGULSwizzlerMessageCodeAppDelegateSwizzling007 = 1007, // I-SWZ001007 - kGULSwizzlerMessageCodeAppDelegateSwizzling008 = 1008, // I-SWZ001008 - kGULSwizzlerMessageCodeAppDelegateSwizzling009 = 1009, // I-SWZ001009 - kGULSwizzlerMessageCodeAppDelegateSwizzling010 = 1010, // I-SWZ001010 - kGULSwizzlerMessageCodeAppDelegateSwizzling011 = 1011, // I-SWZ001011 - kGULSwizzlerMessageCodeAppDelegateSwizzling012 = 1012, // I-SWZ001012 - kGULSwizzlerMessageCodeAppDelegateSwizzling013 = 1013, // I-SWZ001013 + kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000, // I-SWZ001000 + kGULSwizzlerMessageCodeAppDelegateSwizzling001 = 1001, // I-SWZ001001 + kGULSwizzlerMessageCodeAppDelegateSwizzling002 = 1002, // I-SWZ001002 + kGULSwizzlerMessageCodeAppDelegateSwizzling003 = 1003, // I-SWZ001003 + kGULSwizzlerMessageCodeAppDelegateSwizzling004 = 1004, // I-SWZ001004 + kGULSwizzlerMessageCodeAppDelegateSwizzling005 = 1005, // I-SWZ001005 + kGULSwizzlerMessageCodeAppDelegateSwizzling006 = 1006, // I-SWZ001006 + kGULSwizzlerMessageCodeAppDelegateSwizzling007 = 1007, // I-SWZ001007 + kGULSwizzlerMessageCodeAppDelegateSwizzling008 = 1008, // I-SWZ001008 + kGULSwizzlerMessageCodeAppDelegateSwizzling009 = 1009, // I-SWZ001009 + kGULSwizzlerMessageCodeAppDelegateSwizzling010 = 1010, // I-SWZ001010 + kGULSwizzlerMessageCodeAppDelegateSwizzling011 = 1011, // I-SWZ001011 + kGULSwizzlerMessageCodeAppDelegateSwizzling012 = 1012, // I-SWZ001012 + kGULSwizzlerMessageCodeAppDelegateSwizzling013 = 1013, // I-SWZ001013 + kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate = 1014, // I-SWZ001014 // Method Swizzling. kGULSwizzlerMessageCodeMethodSwizzling000 = 2000, // I-SWZ002000 diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj index 08778f3f85c..30940fb98ea 100644 --- a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; + 9A414672225259F900B08D77 /* GULAppDelegateSwizzlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EFBE67F92101401100E756A7 /* GULAppDelegateSwizzlerTest.m */; }; 9A7C37C2224BD9C600033B0D /* GULAppDelegateSwizzlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EFBE67F92101401100E756A7 /* GULAppDelegateSwizzlerTest.m */; }; DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */; }; DE84BBC421D7EC900048A176 /* GULUserDefaultsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */; }; @@ -93,7 +94,7 @@ 6003F5AE195388D20070C39A /* Tests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; - 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GoogleUtilities.podspec; path = ../GoogleUtilities.podspec; sourceTree = ""; }; + 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GoogleUtilities.podspec; path = ../../GoogleUtilities.podspec; sourceTree = ""; }; DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULAppEnvironmentUtilTest.m; sourceTree = ""; }; DE84BBC321D7EC900048A176 /* GULUserDefaultsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULUserDefaultsTests.m; sourceTree = ""; }; DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULReachabilityCheckerTest.m; sourceTree = ""; }; @@ -671,6 +672,7 @@ DEC9786A20F6D66300014E20 /* GULMutableDictionaryTest.m in Sources */, DE84BBC621D7EC900048A176 /* GULUserDefaultsTests.m in Sources */, DEC9786C20F6D66700014E20 /* GULReachabilityCheckerTest.m in Sources */, + 9A414672225259F900B08D77 /* GULAppDelegateSwizzlerTest.m in Sources */, DEC9786820F6D65B00014E20 /* GULLoggerTest.m in Sources */, DEC9786D20F6D66B00014E20 /* GULAppEnvironmentUtilTest.m in Sources */, ); @@ -1101,6 +1103,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", + "$(SRCROOT)/../AppDelegateSwizzler/Internal", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -1131,6 +1134,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../Reachability", + "$(SRCROOT)/../AppDelegateSwizzler/Internal", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/GoogleUtilities/Example/Podfile b/GoogleUtilities/Example/Podfile index aa230a65d94..e114b6fa8d2 100644 --- a/GoogleUtilities/Example/Podfile +++ b/GoogleUtilities/Example/Podfile @@ -9,16 +9,6 @@ target 'Example_iOS' do inherit! :search_paths pod 'OCMock' end - - post_install do |installer_representation| - installer_representation.pods_project.targets.each do |target| - target.build_configurations.each do |config| - if config.name != 'Release' - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'GUL_UNSWIZZLING_ENABLED=1', 'GUL_APP_DELEGATE_TESTING=1'] - end - end - end - end end target 'Example_macOS' do @@ -42,3 +32,13 @@ target 'Example_tvOS' do pod 'OCMock' end end + +post_install do |installer_representation| + installer_representation.pods_project.targets.each do |target| + target.build_configurations.each do |config| + if config.name != 'Release' + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'GUL_UNSWIZZLING_ENABLED=1', 'GUL_APP_DELEGATE_TESTING=1'] + end + end + end +end \ No newline at end of file diff --git a/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m b/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m index d2f8f1fbefe..5356fff334f 100644 --- a/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m +++ b/GoogleUtilities/Example/Tests/Swizzler/GULAppDelegateSwizzlerTest.m @@ -60,7 +60,17 @@ @interface GULTestAppDelegate : UIResponder { /** A URL property that is set by the app delegate methods, which is then used to verify if the app * delegate methods were properly called. */ -@property(nonatomic, copy) NSString *url; +@property(nonatomic, copy) NSURL *url; + +@property(nonatomic, strong) NSData *remoteNotificationsDeviceToken; +@property(nonatomic, strong) NSError *failToRegisterForRemoteNotificationsError; +@property(nonatomic, strong) NSDictionary *remoteNotification; +@property(nonatomic, copy) void (^remoteNotificationCompletionHandler)(UIBackgroundFetchResult); + +/** + * The application is set each time a UIApplicationDelegate method is called + */ +@property(nonatomic, weak) UIApplication *application; @end @@ -120,7 +130,8 @@ - (instancetype)init { - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { - _url = [url copy]; + self.application = app; + self.url = url; _isOpenURLOptionsMethodCalled = YES; return NO; } @@ -128,9 +139,39 @@ - (BOOL)application:(UIApplication *)app - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(nonnull NSString *)identifier completionHandler:(nonnull void (^)(void))completionHandler { + self.application = application; _backgroundSessionID = [identifier copy]; } +- (void)application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + self.application = application; + self.remoteNotificationsDeviceToken = deviceToken; +} + +- (void)application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + self.application = application; + self.failToRegisterForRemoteNotificationsError = error; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo { + self.application = application; + self.remoteNotification = userInfo; +} +#pragma clang diagnostic pop + +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + self.application = application; + self.remoteNotification = userInfo; + self.remoteNotificationCompletionHandler = completionHandler; +} + // These are methods to test whether changing the class still maintains behavior that the app // delegate proxy shouldn't have modified. @@ -169,6 +210,7 @@ - (BOOL)application:(UIApplication *)app return YES; } +#if TARGET_OS_IOS - (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url sourceApplication:(nullable NSString *)sourceApplication @@ -176,6 +218,7 @@ - (BOOL)application:(UIApplication *)application _URLForIOS8 = [url copy]; return YES; } +#endif // TARGET_OS_IOS #if SDK_HAS_USERACTIVITY @@ -202,6 +245,12 @@ - (void)tearDown { [super tearDown]; } +- (void)testNotAppDelegateIsNotSwizzled { + NSObject *notAppDelegate = [[NSObject alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:(id)notAppDelegate]; + XCTAssertEqualObjects(NSStringFromClass([notAppDelegate class]), @"NSObject"); +} + /** Tests proxying an object that responds to UIApplicationDelegate protocol and makes sure that * it is isa swizzled and that the object after proxying responds to the expected methods * and doesn't have its ivars modified. @@ -235,14 +284,27 @@ - (void)testProxyAppDelegate { XCTAssertEqual(sizeBefore, sizeAfter); // After being proxied, it should be able to respond to the required method selector. +#if TARGET_OS_IOS XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application:openURL:sourceApplication:annotation:)]); +#endif // TARGET_OS_IOS + XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: continueUserActivity:restorationHandler:)]); XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application:openURL:options:)]); XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: handleEventsForBackgroundURLSession:completionHandler:)]); + XCTAssertTrue([realAppDelegate + respondsToSelector:@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); + XCTAssertTrue([realAppDelegate + respondsToSelector:@selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); + XCTAssertTrue([realAppDelegate respondsToSelector:@selector(application: + didReceiveRemoteNotification:)]); + XCTAssertTrue([realAppDelegate + respondsToSelector:@selector(application: + didReceiveRemoteNotification:fetchCompletionHandler:)]); + // Make sure that the class has changed. XCTAssertNotEqualObjects([realAppDelegate class], realAppDelegateClassBefore); @@ -458,6 +520,7 @@ - (void)testResultOfApplicationOpenURLOptionsIsORed { XCTAssertTrue(shouldOpen); } +#if TARGET_OS_IOS /** Tests that application:openURL:sourceApplication:annotation: is invoked on the interceptors if * it exists. */ @@ -540,6 +603,7 @@ - (void)testApplicationOpenURLSourceApplicationAnnotationResultIsORed { // The result is YES if one of the interceptors returns YES. XCTAssertTrue(shouldOpen); } +#endif // TARGET_OS_IOS /** Tests that application:handleEventsForBackgroundURLSession:completionHandler: is invoked on the * interceptors if it exists. @@ -648,6 +712,131 @@ - (void)testApplicationContinueUserActivityRestorationHandlerResultsAreORed { XCTAssertTrue(shouldContinueUserActvitiy); } +- (void)testApplicationDidRegisterForRemoteNotificationsIsInvokedOnInterceptors { + NSData *deviceToken = [NSData data]; + UIApplication *application = [UIApplication sharedApplication]; + + id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor application:application + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]); + + id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor2 application:application + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]); + + GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; + + [testAppDelegate application:application + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; + OCMVerifyAll(interceptor); + OCMVerifyAll(interceptor2); + + XCTAssertEqual(testAppDelegate.application, application); + XCTAssertEqual(testAppDelegate.remoteNotificationsDeviceToken, deviceToken); +} + +- (void)testApplicationDidFailToRegisterForRemoteNotificationsIsInvokedOnInterceptors { + NSError *error = [NSError errorWithDomain:@"test" code:-1 userInfo:nil]; + UIApplication *application = [UIApplication sharedApplication]; + + id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor application:application + didFailToRegisterForRemoteNotificationsWithError:error]); + + id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor2 application:application + didFailToRegisterForRemoteNotificationsWithError:error]); + + GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; + + [testAppDelegate application:application didFailToRegisterForRemoteNotificationsWithError:error]; + OCMVerifyAll(interceptor); + OCMVerifyAll(interceptor2); + + XCTAssertEqual(testAppDelegate.application, application); + XCTAssertEqual(testAppDelegate.failToRegisterForRemoteNotificationsError, error); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (void)testApplicationDidReceiveRemoteNotificationIsInvokedOnInterceptors { + NSDictionary *notification = @{}; + UIApplication *application = [UIApplication sharedApplication]; + + id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor application:application didReceiveRemoteNotification:notification]); + + id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor2 application:application didReceiveRemoteNotification:notification]); + + GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; + + [testAppDelegate application:application didReceiveRemoteNotification:notification]; + OCMVerifyAll(interceptor); + OCMVerifyAll(interceptor2); + + XCTAssertEqual(testAppDelegate.application, application); + XCTAssertEqual(testAppDelegate.remoteNotification, notification); +} +#pragma clang diagnostic pop + +- (void)testApplicationDidReceiveRemoteNotificationWithCompletionIsInvokedOnInterceptors { + NSDictionary *notification = @{}; + UIApplication *application = [UIApplication sharedApplication]; + void (^completion)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) { + }; + + id interceptor = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor application:application + didReceiveRemoteNotification:notification + fetchCompletionHandler:completion]); + + id interceptor2 = OCMProtocolMock(@protocol(UIApplicationDelegate)); + OCMExpect([interceptor2 application:application + didReceiveRemoteNotification:notification + fetchCompletionHandler:completion]); + + GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init]; + [GULAppDelegateSwizzler proxyAppDelegate:testAppDelegate]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2]; + + [testAppDelegate application:application + didReceiveRemoteNotification:notification + fetchCompletionHandler:completion]; + OCMVerifyAll(interceptor); + OCMVerifyAll(interceptor2); + + XCTAssertEqual(testAppDelegate.application, application); + XCTAssertEqual(testAppDelegate.remoteNotification, notification); + XCTAssertEqual(testAppDelegate.remoteNotificationCompletionHandler, completion); +} + +- (void)testApplicationDidReceiveRemoteNotificationWithCompletionImplementationIsNotAdded { + // The delegate without application:didReceiveRemoteNotification:fetchCompletionHandler: + // implementation + GULTestInterceptorAppDelegate *legacyDelegate = [[GULTestInterceptorAppDelegate alloc] init]; + + XCTAssertFalse([legacyDelegate + respondsToSelector:@selector(application: + didReceiveRemoteNotification:fetchCompletionHandler:)]); + + [GULAppDelegateSwizzler proxyAppDelegate:legacyDelegate]; + + XCTAssertFalse([legacyDelegate + respondsToSelector:@selector(application: + didReceiveRemoteNotification:fetchCompletionHandler:)]); +} + #pragma mark - Tests to test that Plist flag is honored /** Tests that app delegate proxy is enabled when there is no Info.plist dictionary. */ From f025da18b646221c03204737de2a346e949ab1e1 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 4 Apr 2019 18:22:16 -0700 Subject: [PATCH 143/214] Remove deprecated API to connect direct channel (#2717) --- Firebase/Messaging/FIRMMessageCode.h | 1 + Firebase/Messaging/FIRMessaging.m | 30 ++------------- Firebase/Messaging/Public/FIRMessaging.h | 47 ------------------------ 3 files changed, 4 insertions(+), 74 deletions(-) diff --git a/Firebase/Messaging/FIRMMessageCode.h b/Firebase/Messaging/FIRMMessageCode.h index 2a85d963df6..4c47838c050 100644 --- a/Firebase/Messaging/FIRMMessageCode.h +++ b/Firebase/Messaging/FIRMMessageCode.h @@ -46,6 +46,7 @@ typedef NS_ENUM(NSInteger, FIRMessagingMessageCode) { kFIRMessagingMessageCodeAPNSTokenNotAvailableDuringTokenFetch = 2022, // I-FCM002022 kFIRMessagingMessageCodeTokenDelegateMethodsNotImplemented = 2023, // I-FCM002023 kFIRMessagingMessageCodeTopicFormatIsDeprecated = 2024, + kFIRMessagingMessageCodeDirectChannelConnectionFailed = 2025, // FIRMessagingClient.m kFIRMessagingMessageCodeClient000 = 4000, // I-FCM004000 kFIRMessagingMessageCodeClient001 = 4001, // I-FCM004001 diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 46ec4427a6c..3fa64c81e82 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -710,6 +710,9 @@ - (void)updateAutomaticClientConnection { if (!error) { // It means we connected. Fire connection change notification [self notifyOfDirectChannelConnectionChange]; + } else { + FIRMessagingLoggerError(kFIRMessagingMessageCodeDirectChannelConnectionFailed, + @"Failed to connect to direct channel, error: %@\n", error); } }]; } else if (!shouldBeConnected && self.client.isConnected) { @@ -723,33 +726,6 @@ - (void)notifyOfDirectChannelConnectionChange { [center postNotificationName:FIRMessagingConnectionStateChangedNotification object:self]; } -#pragma mark - Connect - -- (void)connectWithCompletion:(FIRMessagingConnectCompletion)handler { - _FIRMessagingDevAssert([NSThread isMainThread], - @"FIRMessaging connect should be called from main thread only."); - _FIRMessagingDevAssert(self.isClientSetup, @"FIRMessaging client not setup."); - [self.client connectWithHandler:^(NSError *error) { - if (handler) { - handler(error); - } - if (!error) { - // It means we connected. Fire connection change notification - [self notifyOfDirectChannelConnectionChange]; - } - }]; - -} - -- (void)disconnect { - _FIRMessagingDevAssert([NSThread isMainThread], - @"FIRMessaging should be called from main thread only."); - if ([self.client isConnected]) { - [self.client disconnect]; - [self notifyOfDirectChannelConnectionChange]; - } -} - #pragma mark - Topics + (NSString *)normalizeTopic:(NSString *)topic { diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h index e1942f5c291..15caf8ebd21 100644 --- a/Firebase/Messaging/Public/FIRMessaging.h +++ b/Firebase/Messaging/Public/FIRMessaging.h @@ -57,23 +57,6 @@ typedef void(^FIRMessagingDeleteFCMTokenCompletion)(NSError * _Nullable error) */ typedef void (^FIRMessagingTopicOperationCompletion)(NSError *_Nullable error); -/** - * The completion handler invoked once the data connection with FIRMessaging is - * established. The data connection is used to send a continuous stream of - * data and all the FIRMessaging data notifications arrive through this connection. - * Once the connection is established we invoke the callback with `nil` error. - * Correspondingly if we get an error while trying to establish a connection - * we invoke the handler with an appropriate error object and do an - * exponential backoff to try and connect again unless successful. - * - * @param error The error object if any describing why the data connection - * to FIRMessaging failed. - */ -typedef void(^FIRMessagingConnectCompletion)(NSError * __nullable error) - NS_SWIFT_NAME(MessagingConnectCompletion) - __deprecated_msg("Please listen for the FIRMessagingConnectionStateChangedNotification " - "NSNotification instead."); - #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 /** * Notification sent when the upstream message has been delivered @@ -426,36 +409,6 @@ NS_SWIFT_NAME(Messaging) completion:(FIRMessagingDeleteFCMTokenCompletion)completion NS_SWIFT_NAME(deleteFCMToken(forSenderID:completion:)); - -#pragma mark - FCM Direct Channel - -/** - * Create a FIRMessaging data connection which will be used to send the data notifications - * sent by your server. It will also be used to send ACKS and other messages based - * on the FIRMessaging ACKS and other messages based on the FIRMessaging protocol. - * - * - * @param handler The handler to be invoked once the connection is established. - * If the connection fails we invoke the handler with an - * appropriate error code letting you know why it failed. At - * the same time, FIRMessaging performs exponential backoff to retry - * establishing a connection and invoke the handler when successful. - */ -- (void)connectWithCompletion:(FIRMessagingConnectCompletion)handler - NS_SWIFT_NAME(connect(handler:)) - __deprecated_msg("Please use the shouldEstablishDirectChannel property instead."); - -/** - * Disconnect the current FIRMessaging data connection. This stops any attempts to - * connect to FIRMessaging. Calling this on an already disconnected client is a no-op. - * - * Call this before `teardown` when your app is going to the background. - * Since the FIRMessaging connection won't be allowed to live when in the background, it is - * prudent to close the connection. - */ -- (void)disconnect - __deprecated_msg("Please use the shouldEstablishDirectChannel property instead."); - #pragma mark - Topics /** From 45909c3ceb2318cd537adbd6930ba004d809a729 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 4 Apr 2019 19:11:53 -0700 Subject: [PATCH 144/214] Remove unusedimport (#2730) --- Firebase/Auth/Source/Auth/FIRAuth.m | 1 - 1 file changed, 1 deletion(-) diff --git a/Firebase/Auth/Source/Auth/FIRAuth.m b/Firebase/Auth/Source/Auth/FIRAuth.m index 7221471fb6c..957f0c782fb 100644 --- a/Firebase/Auth/Source/Auth/FIRAuth.m +++ b/Firebase/Auth/Source/Auth/FIRAuth.m @@ -22,7 +22,6 @@ #import #endif -#import #import #import #import From cd507af5590bb3eb1be5341b8a0f616202fe542d Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Fri, 5 Apr 2019 10:46:56 -0400 Subject: [PATCH 145/214] Auth - use GULAppDelegateSwizzler (#2710) --- .../Auth/Tests/FIRAuthAppDelegateProxyTests.m | 755 ------------------ Example/Auth/Tests/FIRAuthTests.m | 178 ++++- Example/Firebase.xcodeproj/project.pbxproj | 4 - Firebase/Auth/Source/Auth/FIRAuth.m | 49 +- .../Utilities/FIRAuthAppDelegateProxy.h | 87 -- .../Utilities/FIRAuthAppDelegateProxy.m | 412 ---------- FirebaseAuth.podspec | 2 +- 7 files changed, 216 insertions(+), 1271 deletions(-) delete mode 100644 Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m delete mode 100644 Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.h delete mode 100644 Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.m diff --git a/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m b/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m deleted file mode 100644 index 92871c51e52..00000000000 --- a/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m +++ /dev/null @@ -1,755 +0,0 @@ -/* - * 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 -#import - -#import - -#import "FIRAuthAppDelegateProxy.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -/** @class FIRAuthEmptyAppDelegate - @brief A @c UIApplicationDelegate implementation that does nothing. - */ -@interface FIRAuthEmptyAppDelegate : NSObject -@end -@implementation FIRAuthEmptyAppDelegate -@end - -/** @class FIRAuthLegacyAppDelegate - @brief A @c UIApplicationDelegate implementation that implements - `application:didReceiveRemoteNotification:` and - `application:openURL:sourceApplication:annotation:`. - */ -@interface FIRAuthLegacyAppDelegate : NSObject - -/** @var notificationReceived - @brief The last notification received, if any. - */ -@property(nonatomic, copy, nullable) NSDictionary *notificationReceived; - -/** @var urlOpened - @brief The last URL opened, if any. - */ -@property(nonatomic, copy, nullable) NSURL *urlOpened; - -@end - -@implementation FIRAuthLegacyAppDelegate - -- (void)application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)userInfo { - self.notificationReceived = userInfo; -} - -- (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - sourceApplication:(nullable NSString *)sourceApplication - annotation:(id)annotation { - self.urlOpened = url; - return NO; -} - -@end - -/** @class FIRAuthModernAppDelegate - @brief A @c UIApplicationDelegate implementation that implements both - `application:didRegisterForRemoteNotificationsWithDeviceToken:`, - `application:didReceiveRemoteNotification:fetchCompletionHandler:`, and - `application:openURL:options:`. - */ -@interface FIRAuthModernAppDelegate : NSObject - -/** @var deviceTokenReceived - @brief The last device token received, if any. - */ -@property(nonatomic, copy, nullable) NSData *deviceTokenReceived; - -/** @var tokenErrorReceived - @brief The last token error received, if any. - */ -@property(nonatomic, copy, nullable) NSError *tokenErrorReceived; - -/** @var notificationReceived - @brief The last notification received, if any. - */ -@property(nonatomic, copy, nullable) NSDictionary *notificationReceived; - -/** @var urlOpened - @brief The last URL opened, if any. - */ -@property(nonatomic, copy, nullable) NSURL *urlOpened; - -@end - -@implementation FIRAuthModernAppDelegate - -- (void)application:(UIApplication *)application - didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { - self.deviceTokenReceived = deviceToken; -} - -- (void)application:(UIApplication *)application - didFailToRegisterForRemoteNotificationsWithError:(nonnull NSError *)error { - self.tokenErrorReceived = error; -} - -- (void)application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)userInfo - fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { - self.notificationReceived = userInfo; - completionHandler(UIBackgroundFetchResultNewData); -} - -- (BOOL)application:(UIApplication *)app - openURL:(NSURL *)url - options:(NSDictionary *)options { - self.urlOpened = url; - return NO; -} - -@end - -/** @class FIRAuthOtherLegacyAppDelegate - @brief A @c UIApplicationDelegate implementation that implements `application:handleOpenURL:`. - */ -@interface FIRAuthOtherLegacyAppDelegate : NSObject - -/** @var urlOpened - @brief The last URL opened, if any. - */ -@property(nonatomic, copy, nullable) NSURL *urlOpened; - -@end - -@implementation FIRAuthOtherLegacyAppDelegate - -- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { - self.urlOpened = url; - return NO; -} - -@end - -/** @class FIRAuthAppDelegateProxyTests - @brief Unit tests for @c FIRAuthAppDelegateProxy . - */ -@interface FIRAuthAppDelegateProxyTests : XCTestCase -@end -@implementation FIRAuthAppDelegateProxyTests { - /** @var _mockApplication - @brief The mock UIApplication used for testing. - */ - id _mockApplication; - - /** @var _deviceToken - @brief The fake APNs device token for testing. - */ - NSData *_deviceToken; - - /** @var _error - @brief The fake error for testing. - */ - NSError *_error; - - /** @var _notification - @brief The fake notification for testing. - */ - NSDictionary* _notification; - - /** @var _url - @brief The fake URL for testing. - */ - NSURL *_url; - - /** @var _isIOS9orLater - @brief Whether the OS version is iOS 9 or later. - */ - BOOL _isIOS9orLater; -} - -- (void)setUp { - [super setUp]; - _mockApplication = OCMClassMock([UIApplication class]); - _deviceToken = [@"asdf" dataUsingEncoding:NSUTF8StringEncoding]; - _error = [NSError errorWithDomain:@"FakeError" code:12345 userInfo:nil]; - _notification = @{ @"zxcv" : @1234 }; - _url = [NSURL URLWithString:@"https://abc.def/ghi"]; - _isIOS9orLater = [[[UIDevice currentDevice] systemVersion] doubleValue] >= 9.0; -} - -- (void)tearDown { - OCMVerifyAll(_mockApplication); - [super tearDown]; -} - -/** @fn testSharedInstance - @brief Tests that the shared instance is the same one. - */ -- (void)testSharedInstance { - FIRAuthAppDelegateProxy *proxy1 = [FIRAuthAppDelegateProxy sharedInstance]; - FIRAuthAppDelegateProxy *proxy2 = [FIRAuthAppDelegateProxy sharedInstance]; - XCTAssertEqual(proxy1, proxy2); -} - -/** @fn testNilApplication - @brief Tests that initialization fails if the application is nil. - */ -- (void)testNilApplication { - XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:nil]); -} - -/** @fn testNilDelegate - @brief Tests that initialization fails if the application's delegate is nil. - */ -- (void)testNilDelegate { - OCMExpect([_mockApplication delegate]).andReturn(nil); - XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]); -} - -/** @fn testNonconformingDelegate - @brief Tests that initialization fails if the application's delegate does not conform to - @c UIApplicationDelegate protocol. - */ -- (void)testNonconformingDelegate { - OCMExpect([_mockApplication delegate]).andReturn(@"abc"); - XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]); -} - -/** @fn testDisabledByBundleEntry - @brief Tests that initialization fails if the proxy is disabled by a bundle entry. - */ -- (void)testDisabledByBundleEntry { - // Swizzle NSBundle's objectForInfoDictionaryKey to return @NO for the specific key. - Method method = class_getInstanceMethod([NSBundle class], @selector(objectForInfoDictionaryKey:)); - __block IMP originalImplementation; - IMP newImplmentation = imp_implementationWithBlock(^id(id object, NSString *key) { - if ([key isEqualToString:@"FirebaseAppDelegateProxyEnabled"]) { - return @NO; - } - typedef id (*Implementation)(id object, SEL cmd, NSString *key); - return ((Implementation)originalImplementation)(object, @selector(objectForInfoDictionaryKey:), - key); - }); - originalImplementation = method_setImplementation(method, newImplmentation); - - // Verify that initialization fails. - FIRAuthEmptyAppDelegate *delegate = [[FIRAuthEmptyAppDelegate alloc] init]; - OCMStub([_mockApplication delegate]).andReturn(delegate); - XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]); - - // Unswizzle. - imp_removeBlock(method_setImplementation(method, originalImplementation)); -} - -// Deprecated methods are call intentionally in tests to verify behaviors on older iOS systems. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - -/** @fn testEmptyDelegateOneHandler - @brief Tests that the proxy works against an empty @c UIApplicationDelegate for one handler. - */ -- (void)testEmptyDelegateOneHandler { - FIRAuthEmptyAppDelegate *delegate = [[FIRAuthEmptyAppDelegate alloc] init]; - OCMExpect([_mockApplication delegate]).andReturn(delegate); - __weak id weakProxy; - @autoreleasepool { - FIRAuthAppDelegateProxy *proxy = - [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]; - XCTAssertNotNil(proxy); - - // Verify certain methods are swizzled while others are not. - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]); - XCTAssertFalse([delegate respondsToSelector: - @selector(application:didReceiveRemoteNotification:)]); - if (_isIOS9orLater) { - XCTAssertTrue([delegate respondsToSelector:@selector(application:openURL:options:)]); - XCTAssertFalse([delegate respondsToSelector: - @selector(application:openURL:sourceApplication:annotation:)]); - } else { - XCTAssertFalse([delegate respondsToSelector:@selector(application:openURL:options:)]); - XCTAssertTrue([delegate respondsToSelector: - @selector(application:openURL:sourceApplication:annotation:)]); - } - XCTAssertFalse([delegate respondsToSelector:@selector(application:handleOpenURL:)]); - - // Verify the handler is called after being added. - __weak id weakHandler; - @autoreleasepool { - id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler)); - [proxy addHandler:mockHandler]; - - // Verify `application:didRegisterForRemoteNotificationsWithDeviceToken:` is handled. - OCMExpect([mockHandler setAPNSToken:_deviceToken]); - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - OCMVerifyAll(mockHandler); - - // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled. - OCMExpect([mockHandler handleAPNSTokenError:_error]); - [delegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - OCMVerifyAll(mockHandler); - - // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled. - OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES); - __block BOOL fetchCompletionHandlerCalled = NO; - [delegate application:_mockApplication - didReceiveRemoteNotification:_notification - fetchCompletionHandler:^(UIBackgroundFetchResult result) { - XCTAssertEqual(result, UIBackgroundFetchResultNoData); - fetchCompletionHandlerCalled = YES; - }]; - OCMVerifyAll(mockHandler); - XCTAssertTrue(fetchCompletionHandlerCalled); - - // Verify one of the `application:openURL:...` methods is handled. - OCMExpect([mockHandler canHandleURL:_url]).andReturn(YES); - if (_isIOS9orLater) { - // Verify `application:openURL:options:` is handled. - XCTAssertTrue([delegate application:_mockApplication openURL:_url options:@{}]); - } else { - // Verify `application:openURL:sourceApplication:annotation:` is handled. - XCTAssertTrue([delegate application:_mockApplication - openURL:_url - sourceApplication:@"sourceApplication" - annotation:@"annotaton"]); - } - OCMVerifyAll(mockHandler); - - weakHandler = mockHandler; - XCTAssertNotNil(weakHandler); - } - // Verify the handler is not retained by the proxy. - XCTAssertNil(weakHandler); - - // Verify nothing bad happens after the handler is released. - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - [delegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - [delegate application:_mockApplication - didReceiveRemoteNotification:_notification - fetchCompletionHandler:^(UIBackgroundFetchResult result) { - XCTFail(@"Should not call completion handler."); - }]; - if (_isIOS9orLater) { - XCTAssertFalse([delegate application:_mockApplication openURL:_url options:@{}]); - } else { - XCTAssertFalse([delegate application:_mockApplication - openURL:_url - sourceApplication:@"sourceApplication" - annotation:@"annotaton"]); - } - weakProxy = proxy; - XCTAssertNotNil(weakProxy); - } - // Verify the proxy does not retain itself. - XCTAssertNil(weakProxy); - // Verify nothing bad happens after the proxy is released. - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - [delegate application:_mockApplication - didReceiveRemoteNotification:_notification - fetchCompletionHandler:^(UIBackgroundFetchResult result) { - XCTFail(@"Should not call completion handler."); - }]; - if (_isIOS9orLater) { - XCTAssertNoThrow([delegate application:_mockApplication openURL:_url options:@{}]); - } else { - XCTAssertNoThrow([delegate application:_mockApplication - openURL:_url - sourceApplication:@"sourceApplication" - annotation:@"annotaton"]); - } -} - -/** @fn testLegacyDelegateTwoHandlers - @brief Tests that the proxy works against a legacy @c UIApplicationDelegate for two handlers. - */ -- (void)testLegacyDelegateTwoHandlers { - FIRAuthLegacyAppDelegate *delegate = [[FIRAuthLegacyAppDelegate alloc] init]; - OCMExpect([_mockApplication delegate]).andReturn(delegate); - __weak id weakProxy; - @autoreleasepool { - FIRAuthAppDelegateProxy *proxy = - [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]; - XCTAssertNotNil(proxy); - - // Verify certain methods are swizzled while others are not. - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); - XCTAssertFalse([delegate respondsToSelector: - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]); - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didReceiveRemoteNotification:)]); - XCTAssertFalse([delegate respondsToSelector:@selector(application:openURL:options:)]); - XCTAssertTrue([delegate respondsToSelector: - @selector(application:openURL:sourceApplication:annotation:)]); - XCTAssertFalse([delegate respondsToSelector:@selector(application:handleOpenURL:)]); - - // Verify the handler is called after being added. - __weak id weakHandler1; - @autoreleasepool { - id mockHandler1 = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler)); - [proxy addHandler:mockHandler1]; - __weak id weakHandler2; - @autoreleasepool { - id mockHandler2 = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler)); - [proxy addHandler:mockHandler2]; - - // Verify `application:didRegisterForRemoteNotificationsWithDeviceToken:` is handled. - OCMExpect([mockHandler1 setAPNSToken:_deviceToken]); - OCMExpect([mockHandler2 setAPNSToken:_deviceToken]); - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - OCMVerifyAll(mockHandler1); - OCMVerifyAll(mockHandler2); - - // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled. - OCMExpect([mockHandler1 handleAPNSTokenError:_error]); - OCMExpect([mockHandler2 handleAPNSTokenError:_error]); - [delegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - OCMVerifyAll(mockHandler1); - OCMVerifyAll(mockHandler2); - - // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled. - OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(YES); - // handler2 shouldn't been invoked because it is already handled by handler1. - [delegate application:_mockApplication didReceiveRemoteNotification:_notification]; - OCMVerifyAll(mockHandler1); - OCMVerifyAll(mockHandler2); - XCTAssertNil(delegate.notificationReceived); - - // Verify `application:openURL:sourceApplication:annotation:` is handled. - OCMExpect([mockHandler1 canHandleURL:_url]).andReturn(YES); - XCTAssertTrue([delegate application:_mockApplication - openURL:_url - sourceApplication:@"sourceApplication" - annotation:@"annotaton"]); - OCMVerifyAll(mockHandler1); - OCMVerifyAll(mockHandler2); - XCTAssertNil(delegate.urlOpened); - - weakHandler2 = mockHandler2; - XCTAssertNotNil(weakHandler2); - } - // Verify the handler2 is not retained by the proxy. - XCTAssertNil(weakHandler2); - - // Verify `application:didRegisterForRemoteNotificationsWithDeviceToken:` is handled. - OCMExpect([mockHandler1 setAPNSToken:_deviceToken]); - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - OCMVerifyAll(mockHandler1); - - // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled. - OCMExpect([mockHandler1 handleAPNSTokenError:_error]); - [delegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - OCMVerifyAll(mockHandler1); - - // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is NOT handled. - OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(NO); - [delegate application:_mockApplication didReceiveRemoteNotification:_notification]; - OCMVerifyAll(mockHandler1); - XCTAssertEqualObjects(delegate.notificationReceived, _notification); - delegate.notificationReceived = nil; - - // Verify `application:openURL:sourceApplication:annotation:` is NOT handled. - OCMExpect([mockHandler1 canHandleURL:_url]).andReturn(NO); - XCTAssertFalse([delegate application:_mockApplication - openURL:_url - sourceApplication:@"sourceApplication" - annotation:@"annotation"]); - OCMVerifyAll(mockHandler1); - XCTAssertEqualObjects(delegate.urlOpened, _url); - delegate.urlOpened = nil; - - weakHandler1 = mockHandler1; - XCTAssertNotNil(weakHandler1); - } - // Verify the handler1 is not retained by the proxy. - XCTAssertNil(weakHandler1); - - // Verify the delegate still works after all handlers are released. - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - [delegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - [delegate application:_mockApplication didReceiveRemoteNotification:_notification]; - XCTAssertEqualObjects(delegate.notificationReceived, _notification); - delegate.notificationReceived = nil; - XCTAssertFalse([delegate application:_mockApplication - openURL:_url - sourceApplication:@"sourceApplication" - annotation:@"annotation"]); - XCTAssertEqualObjects(delegate.urlOpened, _url); - delegate.urlOpened = nil; - - weakProxy = proxy; - XCTAssertNotNil(weakProxy); - } - // Verify the proxy does not retain itself. - XCTAssertNil(weakProxy); - - // Verify the delegate still works after the proxy is released. - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - [delegate application:_mockApplication didReceiveRemoteNotification:_notification]; - XCTAssertEqualObjects(delegate.notificationReceived, _notification); - delegate.notificationReceived = nil; - XCTAssertFalse([delegate application:_mockApplication - openURL:_url - sourceApplication:@"sourceApplication" - annotation:@"annotation"]); - XCTAssertEqualObjects(delegate.urlOpened, _url); - delegate.urlOpened = nil; -} - -/** @fn testModernDelegateWithOtherInstance - @brief Tests that the proxy works against a modern @c UIApplicationDelegate along with - another unaffected instance. - */ -- (void)testModernDelegateWithUnaffectedInstance { - FIRAuthModernAppDelegate *delegate = [[FIRAuthModernAppDelegate alloc] init]; - OCMExpect([_mockApplication delegate]).andReturn(delegate); - FIRAuthModernAppDelegate *unaffectedDelegate = [[FIRAuthModernAppDelegate alloc] init]; - __weak id weakProxy; - @autoreleasepool { - FIRAuthAppDelegateProxy *proxy = - [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]; - XCTAssertNotNil(proxy); - - // Verify certain methods are swizzled while others are not. - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); - XCTAssertTrue([delegate respondsToSelector: - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]); - XCTAssertFalse([delegate respondsToSelector: - @selector(application:didReceiveRemoteNotification:)]); - XCTAssertTrue([delegate respondsToSelector:@selector(application:openURL:options:)]); - if (_isIOS9orLater) { - XCTAssertFalse([delegate respondsToSelector: - @selector(application:openURL:sourceApplication:annotation:)]); - } else { - XCTAssertTrue([delegate respondsToSelector: - @selector(application:openURL:sourceApplication:annotation:)]); - } - XCTAssertFalse([delegate respondsToSelector:@selector(application:handleOpenURL:)]); - - // Verify the handler is called after being added. - __weak id weakHandler; - @autoreleasepool { - id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler)); - [proxy addHandler:mockHandler]; - - // Verify `application:didRegisterForRemoteNotificationsWithDeviceToken:` is handled. - OCMExpect([mockHandler setAPNSToken:_deviceToken]); - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - OCMVerifyAll(mockHandler); - XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken); - delegate.deviceTokenReceived = nil; - - // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled. - OCMExpect([mockHandler handleAPNSTokenError:_error]); - [delegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - OCMVerifyAll(mockHandler); - XCTAssertEqualObjects(delegate.tokenErrorReceived, _error); - delegate.tokenErrorReceived = nil; - - // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled. - OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES); - __block BOOL fetchCompletionHandlerCalled = NO; - [delegate application:_mockApplication - didReceiveRemoteNotification:_notification - fetchCompletionHandler:^(UIBackgroundFetchResult result) { - XCTAssertEqual(result, UIBackgroundFetchResultNoData); - fetchCompletionHandlerCalled = YES; - }]; - OCMVerifyAll(mockHandler); - XCTAssertTrue(fetchCompletionHandlerCalled); - XCTAssertNil(delegate.notificationReceived); - - // Verify one of the `application:openURL:...` methods is handled. - OCMExpect([mockHandler canHandleURL:_url]).andReturn(YES); - if (_isIOS9orLater) { - // Verify `application:openURL:options:` is handled. - XCTAssertTrue([delegate application:_mockApplication openURL:_url options:@{}]); - } else { - // Verify `application:openURL:sourceApplication:annotation:` is handled. - XCTAssertTrue([delegate application:_mockApplication - openURL:_url - sourceApplication:@"sourceApplication" - annotation:@"annotaton"]); - } - OCMVerifyAll(mockHandler); - XCTAssertNil(delegate.urlOpened); - - // Verify unaffected delegate instance. - [unaffectedDelegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - XCTAssertEqualObjects(unaffectedDelegate.deviceTokenReceived, _deviceToken); - unaffectedDelegate.deviceTokenReceived = nil; - [unaffectedDelegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - XCTAssertEqualObjects(unaffectedDelegate.tokenErrorReceived, _error); - unaffectedDelegate.tokenErrorReceived = nil; - fetchCompletionHandlerCalled = NO; - [unaffectedDelegate application:_mockApplication - didReceiveRemoteNotification:_notification - fetchCompletionHandler:^(UIBackgroundFetchResult result) { - XCTAssertEqual(result, UIBackgroundFetchResultNewData); - fetchCompletionHandlerCalled = YES; - }]; - XCTAssertTrue(fetchCompletionHandlerCalled); - XCTAssertEqualObjects(unaffectedDelegate.notificationReceived, _notification); - unaffectedDelegate.notificationReceived = nil; - XCTAssertFalse([unaffectedDelegate application:_mockApplication openURL:_url options:@{}]); - XCTAssertEqualObjects(unaffectedDelegate.urlOpened, _url); - unaffectedDelegate.urlOpened = nil; - - weakHandler = mockHandler; - XCTAssertNotNil(weakHandler); - } - // Verify the handler is not retained by the proxy. - XCTAssertNil(weakHandler); - - // Verify the delegate still works after the handler is released. - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken); - delegate.deviceTokenReceived = nil; - [delegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - XCTAssertEqualObjects(delegate.tokenErrorReceived, _error); - delegate.tokenErrorReceived = nil; - __block BOOL fetchCompletionHandlerCalled = NO; - [delegate application:_mockApplication - didReceiveRemoteNotification:_notification - fetchCompletionHandler:^(UIBackgroundFetchResult result) { - XCTAssertEqual(result, UIBackgroundFetchResultNewData); - fetchCompletionHandlerCalled = YES; - }]; - XCTAssertEqualObjects(delegate.notificationReceived, _notification); - delegate.notificationReceived = nil; - XCTAssertTrue(fetchCompletionHandlerCalled); - XCTAssertFalse([delegate application:_mockApplication openURL:_url options:@{}]); - XCTAssertEqualObjects(delegate.urlOpened, _url); - delegate.urlOpened = nil; - - weakProxy = proxy; - XCTAssertNotNil(weakProxy); - } - // Verify the proxy does not retain itself. - XCTAssertNil(weakProxy); - - // Verify the delegate still works after the proxy is released. - [delegate application:_mockApplication - didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; - XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken); - delegate.deviceTokenReceived = nil; - [delegate application:_mockApplication - didFailToRegisterForRemoteNotificationsWithError:_error]; - XCTAssertEqualObjects(delegate.tokenErrorReceived, _error); - delegate.tokenErrorReceived = nil; - __block BOOL fetchCompletionHandlerCalled = NO; - [delegate application:_mockApplication - didReceiveRemoteNotification:_notification - fetchCompletionHandler:^(UIBackgroundFetchResult result) { - XCTAssertEqual(result, UIBackgroundFetchResultNewData); - fetchCompletionHandlerCalled = YES; - }]; - XCTAssertEqualObjects(delegate.notificationReceived, _notification); - delegate.notificationReceived = nil; - XCTAssertTrue(fetchCompletionHandlerCalled); - XCTAssertFalse([delegate application:_mockApplication openURL:_url options:@{}]); - XCTAssertEqualObjects(delegate.urlOpened, _url); - delegate.urlOpened = nil; -} - -/** @fn testOtherLegacyDelegateHandleOpenURL - @brief Tests that the proxy works against another legacy @c UIApplicationDelegate for - `application:handleOpenURL:`. - */ -- (void)testOtherLegacyDelegateHandleOpenURL { - FIRAuthOtherLegacyAppDelegate *delegate = [[FIRAuthOtherLegacyAppDelegate alloc] init]; - OCMExpect([_mockApplication delegate]).andReturn(delegate); - __weak id weakProxy; - @autoreleasepool { - FIRAuthAppDelegateProxy *proxy = - [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]; - XCTAssertNotNil(proxy); - - // Verify certain methods are swizzled while others are not. - XCTAssertFalse([delegate respondsToSelector:@selector(application:openURL:options:)]); - XCTAssertFalse([delegate respondsToSelector: - @selector(application:openURL:sourceApplication:annotation:)]); - XCTAssertTrue([delegate respondsToSelector:@selector(application:handleOpenURL:)]); - - // Verify the handler is called after being added. - __weak id weakHandler; - @autoreleasepool { - id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler)); - [proxy addHandler:mockHandler]; - - // Verify `application:handleOpenURL:` is handled. - OCMExpect([mockHandler canHandleURL:_url]).andReturn(YES); - XCTAssertTrue([delegate application:_mockApplication handleOpenURL:_url]); - OCMVerifyAll(mockHandler); - - weakHandler = mockHandler; - XCTAssertNotNil(weakHandler); - } - // Verify the handler is not retained by the proxy. - XCTAssertNil(weakHandler); - - // Verify nothing bad happens after the handler is released. - XCTAssertFalse([delegate application:_mockApplication handleOpenURL:_url]); - XCTAssertEqualObjects(delegate.urlOpened, _url); - delegate.urlOpened = nil; - - weakProxy = proxy; - XCTAssertNotNil(weakProxy); - } - // Verify the proxy does not retain itself. - XCTAssertNil(weakProxy); - // Verify nothing bad happens after the proxy is released. - XCTAssertFalse([delegate application:_mockApplication handleOpenURL:_url]); - XCTAssertEqualObjects(delegate.urlOpened, _url); - delegate.urlOpened = nil; -} - -#pragma clang diagnostic pop // ignored "-Wdeprecated-declarations" - -@end - -NS_ASSUME_NONNULL_END diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m index 557447a8a58..91c0cdd63a4 100644 --- a/Example/Auth/Tests/FIRAuthTests.m +++ b/Example/Auth/Tests/FIRAuthTests.m @@ -28,6 +28,8 @@ #import #import +#import + #import "FIRAdditionalUserInfo.h" #import "FIRAuth_Internal.h" #import "FIRAuthOperationType.h" @@ -70,10 +72,14 @@ #import "FIRActionCodeSettings.h" #if TARGET_OS_IOS +#import "FIRAuthAPNSToken.h" +#import "FIRAuthAPNSTokenManager.h" +#import "FIRAuthNotificationManager.h" #import "FIRAuthUIDelegate.h" +#import "FIRAuthURLPresenter.h" #import "FIRPhoneAuthCredential.h" #import "FIRPhoneAuthProvider.h" -#endif +#endif // TARGET_OS_IOS /** @var kAPIKey @brief The fake API key. @@ -236,6 +242,40 @@ */ static const NSTimeInterval kWaitInterval = .5; +#if TARGET_OS_IOS +/** @class FIRAuthAppDelegate + @brief Application delegate implementation to test the app delegate proxying + */ +@interface FIRAuthAppDelegate : NSObject +@end + +@implementation FIRAuthAppDelegate +- (void)application:(UIApplication *)application +didReceiveRemoteNotification:(NSDictionary *)userInfo +fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { +} + +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)url + options:(NSDictionary *)options { + return NO; +} + +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(nullable NSString *)sourceApplication + annotation:(id)annotation { + return NO; +} + +@end + +#endif // TARGET_OS_IOS + +@interface GULAppDelegateSwizzler (FIRMessagingRemoteNotificationsProxyTest) ++ (void)resetProxyOriginalDelegateOnceToken; +@end + /** Category for FIRAuth to expose FIRComponentRegistrant conformance. */ @interface FIRAuth () @end @@ -244,7 +284,20 @@ @interface FIRAuth () @brief Tests for @c FIRAuth. */ @interface FIRAuthTests : XCTestCase +#if TARGET_OS_IOS +/// A partial mock of `[FIRAuth auth].tokenManager` +@property(nonatomic, strong) id mockTokenManager; +/// A partial mock of `[FIRAuth auth].notificationManager` +@property(nonatomic, strong) id mockNotificationManager; +/// A partial mock of `[FIRAuth auth].authURLPresenter` +@property(nonatomic, strong) id mockAuthURLPresenter; +/// A partial mock of `[UIApplication sharedApplication]` +@property(nonatomic, strong) id mockApplication; +/// An application delegate instance returned by `self.mockApplication.delegate` +@property(nonatomic, strong) FIRAuthAppDelegate *fakeApplicationDelegate; +#endif // TARGET_OS_IOS @end + @implementation FIRAuthTests { /** @var _mockBackend @@ -277,6 +330,16 @@ + (NSDictionary *)googleProfile { - (void)setUp { [super setUp]; + +#if TARGET_OS_IOS + // Make sure the `self.fakeApplicationDelegate` will be swizzled on FIRAuth init. + [GULAppDelegateSwizzler resetProxyOriginalDelegateOnceToken]; + + self.fakeApplicationDelegate = [[FIRAuthAppDelegate alloc] init]; + self.mockApplication = OCMPartialMock([UIApplication sharedApplication]); + OCMStub([self.mockApplication delegate]).andReturn(self.fakeApplicationDelegate); +#endif // TARGET_OS_IOS + _mockBackend = OCMProtocolMock(@protocol(FIRAuthBackendImplementation)); [FIRAuthBackend setBackendImplementation:_mockBackend]; [FIRApp resetAppForAuthUnitTests]; @@ -292,11 +355,32 @@ - (void)setUp { XCTAssertEqualObjects(FIRAuthGlobalWorkQueue(), queue); _FIRAuthDispatcherCallback = task; }]; + +#if TARGET_OS_IOS + // Wait until FIRAuth initialization completes + [self waitForAuthGlobalWorkQueueDrain]; + self.mockTokenManager = OCMPartialMock([FIRAuth auth].tokenManager); + self.mockNotificationManager = OCMPartialMock([FIRAuth auth].notificationManager); + self.mockAuthURLPresenter = OCMPartialMock([FIRAuth auth].authURLPresenter); +#endif // TARGET_OS_IOS } - (void)tearDown { [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:nil]; [[FIRAuthDispatcher sharedInstance] setDispatchAfterImplementation:nil]; + +#if TARGET_OS_IOS + [self.mockAuthURLPresenter stopMocking]; + self.mockAuthURLPresenter = nil; + [self.mockNotificationManager stopMocking]; + self.mockNotificationManager = nil; + [self.mockTokenManager stopMocking]; + self.mockTokenManager = nil; + [self.mockApplication stopMocking]; + self.mockApplication = nil; + self.fakeApplicationDelegate = nil; +#endif // TARGET_OS_IOS + [super tearDown]; } @@ -2294,6 +2378,90 @@ - (void)testAutoRefreshAppForegroundedNotification { } #endif +#if TARGET_OS_IOS +#pragma mark - Application Delegate tests +- (void)testAppDidRegisterForRemoteNotifications_APNSTokenUpdated { + NSData *apnsToken = [NSData data]; + + OCMExpect([self.mockTokenManager setToken:[OCMArg checkWithBlock:^BOOL(FIRAuthAPNSToken *token) { + XCTAssertEqual(token.data, apnsToken); + XCTAssertEqual(token.type, FIRAuthAPNSTokenTypeUnknown); + return YES; + }]]); + + [self.fakeApplicationDelegate application:self.mockApplication +didRegisterForRemoteNotificationsWithDeviceToken:apnsToken]; + + [self.mockTokenManager verify]; +} + +- (void)testAppDidFailToRegisterForRemoteNotifications_TokenManagerCancels { + NSError *error = [NSError errorWithDomain:@"FIRAuthTests" code:-1 userInfo:nil]; + + OCMExpect([self.mockTokenManager cancelWithError:error]); + + [self.fakeApplicationDelegate application:self.mockApplication +didFailToRegisterForRemoteNotificationsWithError:error]; + + [self.mockTokenManager verify]; +} + +- (void)testAppDidReceiveRemoteNotification_NotificationManagerHandleCanNotification { + NSDictionary *notification = @{@"test" : @""}; + + OCMExpect([self.mockNotificationManager canHandleNotification:notification]); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [self.fakeApplicationDelegate application:self.mockApplication + didReceiveRemoteNotification:notification]; +#pragma clang diagnostic pop + + [self.mockNotificationManager verify]; +} + +- (void)testAppDidReceiveRemoteNotificationWithCompletion_NotificationManagerHandleCanNotification { + NSDictionary *notification = @{@"test" : @""}; + + OCMExpect([self.mockNotificationManager canHandleNotification:notification]); + + [self.fakeApplicationDelegate application:self.mockApplication + didReceiveRemoteNotification:notification + fetchCompletionHandler:^(UIBackgroundFetchResult result) {}]; + + [self.mockNotificationManager verify]; +} + +- (void)testAppOpenURL_AuthPresenterCanHandleURL { + NSURL *url = [NSURL URLWithString:@"https://localhost"]; + + [OCMExpect([self.mockAuthURLPresenter canHandleURL:url]) andReturnValue:@(YES)]; + + XCTAssertTrue([self.fakeApplicationDelegate application:self.mockApplication + openURL:url + options:@{}]); + + [self.mockAuthURLPresenter verify]; +} + +- (void)testAppOpenURLWithSourceApplication_AuthPresenterCanHandleURL { + NSURL *url = [NSURL URLWithString:@"https://localhost"]; + + [OCMExpect([self.mockAuthURLPresenter canHandleURL:url]) andReturnValue:@(YES)]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + XCTAssertTrue([self.fakeApplicationDelegate application:self.mockApplication + openURL:url + sourceApplication:@"" + annotation:[[NSObject alloc] init]]); +#pragma clang diagnostic pop + + [self.mockAuthURLPresenter verify]; +} + +#endif // TARGET_OS_IOS + #pragma mark - Interoperability Tests /** @fn testComponentsBeingRegistered @@ -2552,4 +2720,12 @@ - (void)waitForTimeIntervel:(NSTimeInterval)timeInterval { [self waitForExpectationsWithTimeout:timeInterval + kExpectationTimeout handler:nil]; } +- (void)waitForAuthGlobalWorkQueueDrain { + dispatch_semaphore_t workerSemaphore = dispatch_semaphore_create(0); + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + dispatch_semaphore_signal(workerSemaphore); + }); + dispatch_semaphore_wait(workerSemaphore, DISPATCH_TIME_FOREVER /*DISPATCH_TIME_NOW + 10 * NSEC_PER_SEC*/); +} + @end diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 6a0d9c9054a..d4f2342c782 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -344,7 +344,6 @@ DE0E5BBB1EA7D92E00FAA825 /* FIRVerifyClientRequestTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE0E5BB91EA7D92E00FAA825 /* FIRVerifyClientRequestTest.m */; }; DE0E5BBC1EA7D92E00FAA825 /* FIRVerifyClientResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE0E5BBA1EA7D92E00FAA825 /* FIRVerifyClientResponseTests.m */; }; DE0E5BBD1EA7D93100FAA825 /* FIRAuthAppCredentialTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE0E5BB51EA7D91C00FAA825 /* FIRAuthAppCredentialTests.m */; }; - DE0E5BBE1EA7D93500FAA825 /* FIRAuthAppDelegateProxyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE0E5BB61EA7D91C00FAA825 /* FIRAuthAppDelegateProxyTests.m */; }; DE17A2BA214215C0002A15ED /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; DE17A2BB214215C0002A15ED /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; DE17A2BC214215C0002A15ED /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; @@ -1136,7 +1135,6 @@ D92C82C51F578DF000D5EAFF /* FIRGetProjectConfigResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRGetProjectConfigResponseTests.m; sourceTree = ""; }; D92C82C61F578DF000D5EAFF /* FIRGetProjectConfigRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRGetProjectConfigRequestTests.m; sourceTree = ""; }; DE0E5BB51EA7D91C00FAA825 /* FIRAuthAppCredentialTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRAuthAppCredentialTests.m; sourceTree = ""; }; - DE0E5BB61EA7D91C00FAA825 /* FIRAuthAppDelegateProxyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRAuthAppDelegateProxyTests.m; sourceTree = ""; }; DE0E5BB91EA7D92E00FAA825 /* FIRVerifyClientRequestTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRVerifyClientRequestTest.m; sourceTree = ""; }; DE0E5BBA1EA7D92E00FAA825 /* FIRVerifyClientResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRVerifyClientResponseTests.m; sourceTree = ""; }; DE17A2A02141FA61002A15ED /* DL-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "DL-Info.plist"; sourceTree = ""; }; @@ -2467,7 +2465,6 @@ DE750DB61EB3DD4000A75E47 /* FIRAuthAPNSTokenTests.m */, DE750DB71EB3DD4000A75E47 /* FIRAuthAppCredentialManagerTests.m */, DE0E5BB51EA7D91C00FAA825 /* FIRAuthAppCredentialTests.m */, - DE0E5BB61EA7D91C00FAA825 /* FIRAuthAppDelegateProxyTests.m */, DE9314FD1E86C6FF0083EDBF /* FIRAuthBackendCreateAuthURITests.m */, DE9314FE1E86C6FF0083EDBF /* FIRAuthBackendRPCImplementationTests.m */, DE9314FF1E86C6FF0083EDBF /* FIRAuthDispatcherTests.m */, @@ -4910,7 +4907,6 @@ 7E9485421F578AC4005A3939 /* FIRAuthURLPresenterTests.m in Sources */, 7E21E0731F857DFC00D0AC1C /* FIROAuthProviderTests.m in Sources */, DE750DBD1EB3DD5B00A75E47 /* FIRAuthAPNSTokenTests.m in Sources */, - DE0E5BBE1EA7D93500FAA825 /* FIRAuthAppDelegateProxyTests.m in Sources */, 7EFA2E041F71C93300DD354F /* FIRUserMetadataTests.m in Sources */, DE0E5BBC1EA7D92E00FAA825 /* FIRVerifyClientResponseTests.m in Sources */, DE9315751E86C71C0083EDBF /* FIRUserTests.m in Sources */, diff --git a/Firebase/Auth/Source/Auth/FIRAuth.m b/Firebase/Auth/Source/Auth/FIRAuth.m index 957f0c782fb..7fb0b5e9281 100644 --- a/Firebase/Auth/Source/Auth/FIRAuth.m +++ b/Firebase/Auth/Source/Auth/FIRAuth.m @@ -28,6 +28,7 @@ #import #import #import +#import #import #import "FIREmailPasswordAuthCredential.h" @@ -77,7 +78,6 @@ #import "FIRAuthAPNSToken.h" #import "FIRAuthAPNSTokenManager.h" #import "FIRAuthAppCredentialManager.h" -#import "FIRAuthAppDelegateProxy.h" #import "FIRPhoneAuthCredential_Internal.h" #import "FIRAuthNotificationManager.h" #import "FIRAuthURLPresenter.h" @@ -230,7 +230,7 @@ + (FIRActionCodeOperation)actionCodeOperationForRequestType:(NSString *)requestT #pragma mark - FIRAuth #if TARGET_OS_IOS -@interface FIRAuth () +@interface FIRAuth () #else @interface FIRAuth () #endif @@ -378,8 +378,7 @@ - (nullable instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)a } UIApplication *application = [applicationClass sharedApplication]; - // Initialize the shared FIRAuthAppDelegateProxy instance in the main thread if not already. - [FIRAuthAppDelegateProxy sharedInstance]; + [GULAppDelegateSwizzler proxyOriginalDelegate]; #endif // Continue with the rest of initialization in the work thread. @@ -434,7 +433,7 @@ - (nullable instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)a initWithApplication:application appCredentialManager:strongSelf->_appCredentialManager]; - [[FIRAuthAppDelegateProxy sharedInstance] addHandler:strongSelf]; + [GULAppDelegateSwizzler registerAppDelegateInterceptor:strongSelf]; #endif }); } @@ -1442,19 +1441,47 @@ - (nullable NSData *)APNSToken { return result; } -- (void)setAPNSToken:(nullable NSData *)APNSToken { - [self setAPNSToken:APNSToken type:FIRAuthAPNSTokenTypeUnknown]; +#pragma mark - UIApplicationDelegate + +- (void)application:(UIApplication *)application +didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + [self setAPNSToken:deviceToken type:FIRAuthAPNSTokenTypeUnknown]; } -- (void)setAPNSToken:(NSData *)token type:(FIRAuthAPNSTokenType)type { +- (void)application:(UIApplication *)application +didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - self->_tokenManager.token = [[FIRAuthAPNSToken alloc] initWithData:token type:type]; + [self->_tokenManager cancelWithError:error]; }); } -- (void)handleAPNSTokenError:(NSError *)error { +- (void)application:(UIApplication *)application +didReceiveRemoteNotification:(NSDictionary *)userInfo +fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + [self canHandleNotification:userInfo]; +} + +- (void)application:(UIApplication *)application +didReceiveRemoteNotification:(NSDictionary *)userInfo { + [self canHandleNotification:userInfo]; +} + +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)url + options:(NSDictionary *)options { + return [self canHandleURL:url]; +} + +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(nullable NSString *)sourceApplication + annotation:(id)annotation { + return [self canHandleURL:url]; +} + +- (void)setAPNSToken:(NSData *)token type:(FIRAuthAPNSTokenType)type { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - [self->_tokenManager cancelWithError:error]; + self->_tokenManager.token = [[FIRAuthAPNSToken alloc] initWithData:token type:type]; }); } diff --git a/Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.h b/Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.h deleted file mode 100644 index ccae93a7899..00000000000 --- a/Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 -#import - -NS_ASSUME_NONNULL_BEGIN - -/** @protocol FIRAuthAppDelegateHandler - @brief The protocol to handle app delegate methods. - */ -@protocol FIRAuthAppDelegateHandler - -/** @fn setAPNSToken: - @brief Sets the APNs device token. - @param token The APNs device token. - */ -- (void)setAPNSToken:(NSData *)token; - -/** @fn handleAPNSTokenError: - @brief Handles APNs device token error. - @param error The APNs device token error. - */ -- (void)handleAPNSTokenError:(NSError *)error; - -/** @fn canHandleNotification: - @brief Checks whether the notification can be handled by the receiver, and handles it if so. - @param notification The notification in question, which will be consumed if returns @c YES. - @return Whether the notification can be (and already has been) handled by the receiver. - */ -- (BOOL)canHandleNotification:(nonnull NSDictionary *)notification; - -/** @fn canHandleURL: - @brief Checks whether the URL can be handled by the receiver, and handles it if so. - @param url The URL in question, which will be consumed if returns @c YES. - @return Whether the URL can be (and already has been) handled by the receiver. - */ -- (BOOL)canHandleURL:(nonnull NSURL *)url; - -@end - -/** @class FIRAuthAppDelegateProxy - @brief A manager for swizzling @c UIApplicationDelegate methods. - */ -@interface FIRAuthAppDelegateProxy : NSObject - -/** @fn initWithApplication - @brief Initialize the instance with the given @c UIApplication. - @returns An initialized instance, or @c nil if a proxy cannot be established. - @remarks This method should only be called from tests if called outside of this class. - */ -- (nullable instancetype)initWithApplication:(nullable UIApplication *)application - NS_DESIGNATED_INITIALIZER; - -/** @fn init - @brief Call @c sharedInstance to get an instance of this class. - */ -- (instancetype)init NS_UNAVAILABLE; - -/** @fn addHandler: - @brief Adds a handler for UIApplicationDelegate methods. - @param handler The handler to be added. - */ -- (void)addHandler:(__weak id)handler; - -/** @fn sharedInstance - @brief Gets the shared instance of this class. - @returns The shared instance of this class. - */ -+ (nullable instancetype)sharedInstance; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.m b/Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.m deleted file mode 100644 index d97fedc6a69..00000000000 --- a/Firebase/Auth/Source/Utilities/FIRAuthAppDelegateProxy.m +++ /dev/null @@ -1,412 +0,0 @@ -/* - * 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 "FIRAuthAppDelegateProxy.h" - -#import - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** @var kProxyEnabledBundleKey - @brief The key in application's bundle plist for whether or not proxy should be enabled. - @remarks This key is a shared constant with Analytics and FCM. - */ -static NSString *const kProxyEnabledBundleKey = @"FirebaseAppDelegateProxyEnabled"; - -/** @fn noop - @brief A function that does nothing. - @remarks This is used as the placeholder for unimplemented UApplicationDelegate methods, - because once we added a method there is no way to remove it from the class. - */ -#if !OBJC_OLD_DISPATCH_PROTOTYPES -static void noop(void) { -} -#else -static id noop(id object, SEL cmd, ...) { - return nil; -} -#endif - -/** @fn isIOS9orLater - @brief Checks whether the iOS version is 9 or later. - @returns Whether the iOS version is 9 or later. - */ -static BOOL isIOS9orLater() { -#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) - if (@available(iOS 9.0, *)) { - return YES; - } - return NO; -#else - // UIApplicationOpenURLOptionsAnnotationKey is only available on iOS 9+. - return &UIApplicationOpenURLOptionsAnnotationKey != NULL; -#endif -} - -@implementation FIRAuthAppDelegateProxy { - /** @var _appDelegate - @brief The application delegate whose method is being swizzled. - */ - id _appDelegate; - - /** @var _orginalImplementationsBySelector - @brief A map from selectors to original implementations that have been swizzled. - */ - NSMutableDictionary *_originalImplementationsBySelector; - - /** @var _handlers - @brief The array of weak pointers of `id`. - */ - NSPointerArray *_handlers; -} - -- (nullable instancetype)initWithApplication:(nullable UIApplication *)application { - self = [super init]; - if (self) { - id proxyEnabled = [[NSBundle mainBundle] objectForInfoDictionaryKey:kProxyEnabledBundleKey]; - if ([proxyEnabled isKindOfClass:[NSNumber class]] && !((NSNumber *)proxyEnabled).boolValue) { - return nil; - } - _appDelegate = application.delegate; - if (![_appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) { - return nil; - } - _originalImplementationsBySelector = [[NSMutableDictionary alloc] init]; - _handlers = [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsWeakMemory]; - - // Swizzle the methods. - __weak FIRAuthAppDelegateProxy *weakSelf = self; - SEL registerDeviceTokenSelector = - @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); - [self replaceSelector:registerDeviceTokenSelector - withBlock:^(id object, UIApplication* application, NSData *deviceToken) { - [weakSelf object:object - selector:registerDeviceTokenSelector - application:application - didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; - }]; - SEL failToRegisterRemoteNotificationSelector = - @selector(application:didFailToRegisterForRemoteNotificationsWithError:); - [self replaceSelector:failToRegisterRemoteNotificationSelector - withBlock:^(id object, UIApplication* application, NSError *error) { - [weakSelf object:object - selector:failToRegisterRemoteNotificationSelector - application:application - didFailToRegisterForRemoteNotificationsWithError:error]; - }]; - SEL receiveNotificationSelector = @selector(application:didReceiveRemoteNotification:); - SEL receiveNotificationWithHandlerSelector = - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); - if ([_appDelegate respondsToSelector:receiveNotificationWithHandlerSelector] || - ![_appDelegate respondsToSelector:receiveNotificationSelector]) { - // Replace the modern selector which is available on iOS 7 and above. - [self replaceSelector:receiveNotificationWithHandlerSelector - withBlock:^(id object, UIApplication *application, NSDictionary *notification, - void (^completionHandler)(UIBackgroundFetchResult)) { - [weakSelf object:object - selector:receiveNotificationWithHandlerSelector - application:application - didReceiveRemoteNotification:notification - fetchCompletionHandler:completionHandler]; - }]; - } else { - // Replace the deprecated selector because this is the only one that the client app uses. - [self replaceSelector:receiveNotificationSelector - withBlock:^(id object, UIApplication *application, NSDictionary *notification) { - [weakSelf object:object - selector:receiveNotificationSelector - application:application - didReceiveRemoteNotification:notification]; - }]; - } - SEL openURLOptionsSelector = @selector(application:openURL:options:); - SEL openURLAnnotationSelector = @selector(application:openURL:sourceApplication:annotation:); - SEL handleOpenURLSelector = @selector(application:handleOpenURL:); - if (isIOS9orLater() && - ([_appDelegate respondsToSelector:openURLOptionsSelector] || - (![_appDelegate respondsToSelector:openURLAnnotationSelector] && - ![_appDelegate respondsToSelector:handleOpenURLSelector]))) { - // Replace the modern selector which is avaliable on iOS 9 and above because this is the one - // that the client app uses or the client app doesn't use any of them. - [self replaceSelector:openURLOptionsSelector - withBlock:^BOOL(id object, UIApplication *application, NSURL *url, - NSDictionary *options) { - return [weakSelf object:object - selector:openURLOptionsSelector - application:application - openURL:url - options:options]; - }]; - } else if ([_appDelegate respondsToSelector:openURLAnnotationSelector] || - ![_appDelegate respondsToSelector:handleOpenURLSelector]) { - // Replace the longer form of the deprecated selectors on iOS 8 and below because this is the - // one that the client app uses or the client app doesn't use either of the applicable ones. - [self replaceSelector:openURLAnnotationSelector - withBlock:^(id object, UIApplication *application, NSURL *url, - NSString *sourceApplication, id annotation) { - return [weakSelf object:object - selector:openURLAnnotationSelector - application:application - openURL:url - sourceApplication:sourceApplication - annotation:annotation]; - }]; - } else { - // Replace the shorter form of the deprecated selectors on iOS 8 and below because this is - // the only one that the client app uses. - [self replaceSelector:handleOpenURLSelector - withBlock:^(id object, UIApplication *application, NSURL *url) { - return [weakSelf object:object - selector:handleOpenURLSelector - application:application - handleOpenURL:url]; - }]; - } - // Reset the application delegate to clear the system cache that indicates whether each of the - // openURL: methods is implemented on the application delegate. - application.delegate = nil; - application.delegate = _appDelegate; - } - return self; -} - -- (void)dealloc { - for (NSValue *selector in _originalImplementationsBySelector) { - IMP implementation = _originalImplementationsBySelector[selector].pointerValue; - Method method = class_getInstanceMethod([_appDelegate class], selector.pointerValue); - imp_removeBlock(method_setImplementation(method, implementation)); - } -} - -- (void)addHandler:(__weak id)handler { - @synchronized (_handlers) { - [_handlers addPointer:(__bridge void *)handler]; - } -} - -+ (nullable instancetype)sharedInstance { - static dispatch_once_t onceToken; - static FIRAuthAppDelegateProxy *_Nullable sharedInstance; - // iOS App extensions should not call [UIApplication sharedApplication], even if UIApplication - // responds to it. - static Class applicationClass = nil; - dispatch_once(&onceToken, ^{ - if (![GULAppEnvironmentUtil isAppExtension]) { - Class cls = NSClassFromString(@"UIApplication"); - if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) { - applicationClass = cls; - } - } - UIApplication *application = [applicationClass sharedApplication]; - sharedInstance = [[self alloc] initWithApplication:application]; - }); - return sharedInstance; -} - -#pragma mark - UIApplicationDelegate proxy methods. - -- (void)object:(id)object - selector:(SEL)selector - application:(UIApplication *)application - didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { - if (object == _appDelegate) { - for (id handler in [self handlers]) { - [handler setAPNSToken:deviceToken]; - } - } - IMP originalImplementation = [self originalImplementationForSelector:selector]; - if (originalImplementation && originalImplementation != &noop) { - typedef void (*Implmentation)(id, SEL, UIApplication*, NSData *); - ((Implmentation)originalImplementation)(object, selector, application, deviceToken); - } -} - -- (void)object:(id)object - selector:(SEL)selector - application:(UIApplication *)application - didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - if (object == _appDelegate) { - for (id handler in [self handlers]) { - [handler handleAPNSTokenError:error]; - } - } - IMP originalImplementation = [self originalImplementationForSelector:selector]; - if (originalImplementation && originalImplementation != &noop) { - typedef void (*Implmentation)(id, SEL, UIApplication *, NSError *); - ((Implmentation)originalImplementation)(object, selector, application, error); - } -} - -- (void)object:(id)object - selector:(SEL)selector - application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)notification - fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { - if (object == _appDelegate) { - for (id handler in [self handlers]) { - if ([handler canHandleNotification:notification]) { - completionHandler(UIBackgroundFetchResultNoData); - return; - }; - } - } - IMP originalImplementation = [self originalImplementationForSelector:selector]; - if (originalImplementation && originalImplementation != &noop) { - typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *, - void (^)(UIBackgroundFetchResult)); - ((Implmentation)originalImplementation)(object, selector, application, notification, - completionHandler); - } -} - -- (void)object:(id)object - selector:(SEL)selector - application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)notification { - if (object == _appDelegate) { - for (id handler in [self handlers]) { - if ([handler canHandleNotification:notification]) { - return; - }; - } - } - IMP originalImplementation = [self originalImplementationForSelector:selector]; - if (originalImplementation && originalImplementation != &noop) { - typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *); - ((Implmentation)originalImplementation)(object, selector, application, notification); - } -} - -- (BOOL)object:(id)object - selector:(SEL)selector - application:(UIApplication *)application - openURL:(NSURL *)url - options:(NSDictionary *)options { - if (object == _appDelegate && [self delegateCanHandleURL:url]) { - return YES; - } - IMP originalImplementation = [self originalImplementationForSelector:selector]; - if (originalImplementation && originalImplementation != &noop) { - typedef BOOL (*Implmentation)(id, SEL, UIApplication*, NSURL *, NSDictionary *); - return ((Implmentation)originalImplementation)(object, selector, application, url, options); - } - return NO; -} - -- (BOOL)object:(id)object - selector:(SEL)selector - application:(UIApplication *)application - openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation { - if (object == _appDelegate && [self delegateCanHandleURL:url]) { - return YES; - } - IMP originalImplementation = [self originalImplementationForSelector:selector]; - if (originalImplementation && originalImplementation != &noop) { - typedef BOOL (*Implmentation)(id, SEL, UIApplication*, NSURL *, NSString *, id); - return ((Implmentation)originalImplementation)(object, selector, application, url, - sourceApplication, annotation); - } - return NO; -} - -- (BOOL)object:(id)object - selector:(SEL)selector - application:(UIApplication *)application - handleOpenURL:(NSURL *)url { - if (object == _appDelegate && [self delegateCanHandleURL:url]) { - return YES; - } - IMP originalImplementation = [self originalImplementationForSelector:selector]; - if (originalImplementation && originalImplementation != &noop) { - typedef BOOL (*Implmentation)(id, SEL, UIApplication*, NSURL *); - return ((Implmentation)originalImplementation)(object, selector, application, url); - } - return NO; -} - -#pragma mark - Internal Methods - -/** @fn delegateCanHandleURL: - @brief Checks for whether any of the delegates can handle the URL. - @param url The URL in question. - @return Whether any of the delegate can handle the URL. - */ -- (BOOL)delegateCanHandleURL:(NSURL *)url { - for (id handler in [self handlers]) { - if ([handler canHandleURL:url]) { - return YES; - }; - } - return NO; -} - -/** @fn handlers - @brief Gets the list of handlers from `_handlers` safely. - */ -- (NSArray> *)handlers { - @synchronized (_handlers) { - NSMutableArray> *liveHandlers = - [[NSMutableArray> alloc] initWithCapacity:_handlers.count]; - for (__weak id handler in _handlers) { - if (handler) { - [liveHandlers addObject:handler]; - } - } - if (liveHandlers.count < _handlers.count) { - [_handlers compact]; - } - return liveHandlers; - } -} - -/** @fn replaceSelector:withBlock: - @brief replaces the implementation for a method of `_appDelegate` specified by a selector. - @param selector The selector for the method. - @param block The block as the new implementation of the method. - */ -- (void)replaceSelector:(SEL)selector withBlock:(id)block { - Method originalMethod = class_getInstanceMethod([_appDelegate class], selector); - IMP newImplementation = imp_implementationWithBlock(block); - IMP originalImplementation; - if (originalMethod) { - originalImplementation = method_setImplementation(originalMethod, newImplementation) ?: &noop; - } else { - // The original method was not implemented in the class, add it with the new implementation. - struct objc_method_description methodDescription = - protocol_getMethodDescription(@protocol(UIApplicationDelegate), selector, NO, YES); - class_addMethod([_appDelegate class], selector, newImplementation, methodDescription.types); - originalImplementation = &noop; - } - _originalImplementationsBySelector[[NSValue valueWithPointer:selector]] = - [NSValue valueWithPointer:originalImplementation]; -} - -/** @fn originalImplementationForSelector: - @brief Gets the original implementation for the given selector. - @param selector The selector for the method that has been replaced. - @return The original implementation if there was one. - */ -- (IMP)originalImplementationForSelector:(SEL)selector { - return _originalImplementationsBySelector[[NSValue valueWithPointer:selector]].pointerValue; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index e6b52d6fe2b..a2b9a7f8f1a 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -28,7 +28,6 @@ supports email and password accounts, as well as several 3rd party authenticatio source = 'Firebase/Auth/Source/' s.source_files = source + '**/*.[mh]' s.osx.exclude_files = [ - source + '**/FIRAuthAppDelegateProxy.[mh]', source + '**/FIRAuthNotificationManager.[mh]', source + '**/FIRAuthAppCredentialManager.[mh]', source + '**/FIRAuthAPNSTokenManager.[mh]', @@ -64,6 +63,7 @@ supports email and password accounts, as well as several 3rd party authenticatio s.ios.framework = 'SafariServices' s.dependency 'FirebaseAuthInterop', '~> 1.0' s.dependency 'FirebaseCore', '~> 5.2' + s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.2' s.dependency 'GoogleUtilities/Environment', '~> 5.2' s.dependency 'GTMSessionFetcher/Core', '~> 1.1' end From 9bdcf3fb67aeb75bc086c1c67bd4814d40bebfab Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 5 Apr 2019 08:24:36 -0700 Subject: [PATCH 146/214] Remove missing analytics runtime warning (#2734) --- Firebase/Core/FIRApp.m | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index a38e8f3366c..520dcbcb865 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -311,11 +311,7 @@ - (BOOL)configureCore { // always initialize first by itself before the other SDKs. if ([self.name isEqualToString:kFIRDefaultAppName]) { Class firAnalyticsClass = NSClassFromString(@"FIRAnalytics"); - if (!firAnalyticsClass) { - FIRLogWarning(kFIRLoggerCore, @"I-COR000022", - @"Firebase Analytics is not available. To add it, include Firebase/Core in the " - @"Podfile or add FirebaseAnalytics.framework to the Link Build Phase"); - } else { + if (firAnalyticsClass) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" SEL startWithConfigurationSelector = @selector(startWithConfiguration:options:); From dae3442966b27c7f77a0021b995e97ba0c79f979 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Fri, 5 Apr 2019 14:35:15 -0700 Subject: [PATCH 147/214] Remove deprecated APIs (#2723) --- Example/Auth/ApiTests/FacebookAuthTests.m | 4 +- Example/Auth/ApiTests/GoogleAuthTests.m | 2 +- Example/Auth/ApiTests/GoogleAuthTests.swift | 2 +- Example/Auth/Sample/MainViewController.m | 95 +++++---- Example/Auth/Tests/FIRAuthTests.m | 112 +++++----- Example/Auth/Tests/FIRUserTests.m | 197 ++++++++---------- Example/Firebase.xcodeproj/project.pbxproj | 6 +- Firebase/Auth/Source/Auth/FIRAuth.m | 117 +---------- Firebase/Auth/Source/Public/FIRAuth.h | 175 ++++------------ Firebase/Auth/Source/Public/FIRAuthErrors.h | 14 -- .../Auth/Source/Public/FIREmailAuthProvider.h | 14 +- .../Auth/Source/Public/FIROAuthProvider.h | 12 -- Firebase/Auth/Source/Public/FIRUser.h | 55 +++-- Firebase/Auth/Source/User/FIRUser.m | 29 ++- 14 files changed, 287 insertions(+), 547 deletions(-) diff --git a/Example/Auth/ApiTests/FacebookAuthTests.m b/Example/Auth/ApiTests/FacebookAuthTests.m index cb74fe71461..b21c47e1650 100644 --- a/Example/Auth/ApiTests/FacebookAuthTests.m +++ b/Example/Auth/ApiTests/FacebookAuthTests.m @@ -54,7 +54,7 @@ - (void)testSignInWithFaceboook { XCTestExpectation *expectation = [self expectationWithDescription:@"Facebook sign-in finished."]; [auth signInWithCredential:credential - completion:^(FIRUser *user, NSError *error) { + completion:^(FIRAuthDataResult *result, NSError *error) { if (error) { NSLog(@"Facebook sign in error: %@", error); } @@ -94,7 +94,7 @@ - (void)testLinkAnonymousAccountToFacebookAccount { XCTestExpectation *expectation = [self expectationWithDescription:@"Facebook linking finished."]; [auth.currentUser linkWithCredential:credential - completion:^(FIRUser *user, NSError *error) { + completion:^(FIRAuthDataResult *result, NSError *error) { if (error) { NSLog(@"Link to Facebok error: %@", error); } diff --git a/Example/Auth/ApiTests/GoogleAuthTests.m b/Example/Auth/ApiTests/GoogleAuthTests.m index 4611a1b8edf..0d176e02f39 100644 --- a/Example/Auth/ApiTests/GoogleAuthTests.m +++ b/Example/Auth/ApiTests/GoogleAuthTests.m @@ -48,7 +48,7 @@ - (void)testSignInWithGoogle { XCTestExpectation *expectation = [self expectationWithDescription:@"Signing in with Google finished."]; [auth signInWithCredential:credential - completion:^(FIRUser *user, NSError *error) { + completion:^(FIRAuthDataResult *result, NSError *error) { if (error) { NSLog(@"Signing in with Google had error: %@", error); } diff --git a/Example/Auth/ApiTests/GoogleAuthTests.swift b/Example/Auth/ApiTests/GoogleAuthTests.swift index 9a2126ca614..c0f6ecb5fea 100644 --- a/Example/Auth/ApiTests/GoogleAuthTests.swift +++ b/Example/Auth/ApiTests/GoogleAuthTests.swift @@ -35,7 +35,7 @@ class GoogleAuthTestsSwift: FIRAuthApiTestsBase { let credential = GoogleAuthProvider.credential(withIDToken: googleIdToken, accessToken: googleAccessToken) let expectation = self.expectation(description: "Signing in with Google finished.") - auth.signInAndRetrieveData(with: credential) { _, error in + auth.signIn(with: credential) { _, error in if error != nil { print("Signing in with Google had error: %@", error!) } diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 6d74531b1b2..a4c17c09923 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -1131,8 +1131,9 @@ - (void)signInWithProvider:(nonnull id)provider callback:(void(^)( [self logFailedTest:@"The test needs a valid credential to continue."]; return; } - [[AppManager auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[AppManager auth] signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { if (error) { [self logFailure:@"sign-in with provider failed" error:error]; [self logFailedTest:@"Sign-in should succeed"]; @@ -1283,9 +1284,9 @@ - (void)automatedEmailSignUp { [auth signOut:NULL]; FIRAuthCredential *credential = [FIREmailAuthProvider credentialWithEmail:kFakeEmail password:kFakePassword]; - [auth signInWithCredential:credential - completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [auth signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { if (error) { [self logFailure:@"sign-in with Email/Password failed" error:error]; [self logFailedTest:@"sign-in with Email/Password should succeed."]; @@ -1381,8 +1382,10 @@ - (void)automatedAccountLinking { callback:^(FIRAuthCredential *credential, NSError *error) { if (credential) { - [result.user linkWithCredential:credential completion:^(FIRUser *user, + [result.user linkAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *result, NSError *error) { + FIRUser *user = result.user; if (error) { [self logFailure:@"link auth provider failed" error:error]; [self logFailedTest:@"Account needs to be linked to complete the test."]; @@ -1573,9 +1576,9 @@ - (void)updateEmailPasswordWithCompletion:(void(^)(void))completion { [auth signOut:NULL]; FIRAuthCredential *credential = [FIREmailAuthProvider credentialWithEmail:kFakeEmail password:kFakePassword]; - [auth signInWithCredential:credential - completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [auth signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { if (error) { [self logFailure:@"sign-in with Email/Password failed" error:error]; [self logFailedTest:@"sign-in with Email/Password should succeed."]; @@ -2003,9 +2006,9 @@ - (void)signInEmailPassword { [FIREmailAuthProvider credentialWithEmail:email password:password]; [self showSpinner:^{ - [[AppManager auth] signInWithCredential:credential - completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[AppManager auth] signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { [self hideSpinner:^{ if (error) { [self logFailure:@"sign-in with Email/Password failed" error:error]; @@ -2033,10 +2036,10 @@ - (void)signInEmailPasswordAuthDataResult { return; } [self showSpinner:^{ - [[AppManager auth] signInAndRetrieveDataWithEmail:email - password:password - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[AppManager auth] signInWithEmail:email + password:password + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { [self hideSpinner:^{ if (error) { [self logFailure:@"sign-in with Email/Password failed" error:error]; @@ -2270,8 +2273,9 @@ - (void)reauthenticateEmailPassword { } [self showEmailPasswordDialogWithCompletion:^(FIRAuthCredential *credential) { [self showSpinner:^{ - [[self user] reauthenticateWithCredential:credential - completion:^(NSError *_Nullable error) { + [[self user] reauthenticateAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { if (error) { [self logFailure:@"reauthicate with email/password failed" error:error]; } else { @@ -2321,13 +2325,14 @@ - (void)reauthenticate:(id)authProvider retrieveData:(BOOL)retriev } [self showTypicalUIForUserUpdateResultsWithTitle:@"Reauthenticate" error:error]; }; - FIRUserProfileChangeCallback callback = ^(NSError *_Nullable error) { + FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { completion(nil, error); }; if (retrieveData) { [user reauthenticateAndRetrieveDataWithCredential:credential completion:completion]; } else { - [user reauthenticateWithCredential:credential completion:callback]; + [user reauthenticateAndRetrieveDataWithCredential:credential completion:callback]; } } }]; @@ -2367,14 +2372,14 @@ - (void)signinWithProvider:(id)authProvider retrieveData:(BOOL)ret } [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In" error:error]; }; - FIRAuthResultCallback callback = ^(FIRUser *_Nullable user, - NSError *_Nullable error) { + FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { completion(nil, error); }; if (retrieveData) { [auth signInAndRetrieveDataWithCredential:credential completion:completion]; } else { - [auth signInWithCredential:credential completion:callback]; + [auth signInAndRetrieveDataWithCredential:credential completion:callback]; } } }]; @@ -2551,14 +2556,14 @@ - (void)linkWithAuthProvider:(id)authProvider retrieveData:(BOOL)r [self showTypicalUIForUserUpdateResultsWithTitle:@"Link Account" error:error]; } }; - FIRAuthResultCallback callback = ^(FIRUser *_Nullable user, - NSError *_Nullable error) { + FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { completion(nil, error); }; if (retrieveData) { [user linkAndRetrieveDataWithCredential:credential completion:completion]; } else { - [user linkWithCredential:credential completion:callback]; + [user linkAndRetrieveDataWithCredential:credential completion:callback]; } } }]; @@ -2602,8 +2607,8 @@ - (void)linkWithFacebookAndRetrieveData { - (void)linkWithEmailPassword { [self showEmailPasswordDialogWithCompletion:^(FIRAuthCredential *credential) { [self showSpinner:^{ - [[self user] linkWithCredential:credential - completion:^(FIRUser *user, NSError *_Nullable error) { + [[self user] linkAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *result, NSError *error) { if (error) { [self logFailure:@"link Email/Password failed" error:error]; } else { @@ -3108,10 +3113,10 @@ - (void)createUserAuthDataResult { } [self showSpinner:^{ - [[AppManager auth] createUserAndRetrieveDataWithEmail:email - password:password - completion:^(FIRAuthDataResult *_Nullable result, - NSError *_Nullable error) { + [[AppManager auth] createUserWithEmail:email + password:password + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { if (error) { [self logFailure:@"create user failed" error:error]; } else { @@ -3378,9 +3383,9 @@ - (void)linkPhoneNumber:(NSString *_Nullable)phoneNumber FIRPhoneAuthCredential *credential = [[AppManager phoneAuthProvider] credentialWithVerificationID:verificationID verificationCode:verificationCode]; - [[self user] linkWithCredential:credential - completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[self user] linkAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { [self hideSpinner:^{ if (error) { if (error.code == FIRAuthErrorCodeCredentialAlreadyInUse) { @@ -3396,9 +3401,9 @@ - (void)linkPhoneNumber:(NSString *_Nullable)phoneNumber [self showSpinner:^{ FIRPhoneAuthCredential *credential = error.userInfo[FIRAuthErrorUserInfoUpdatedCredentialKey]; - [[AppManager auth] signInWithCredential:credential - completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[AppManager auth] signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { [self hideSpinner:^{ if (error) { [self logFailure:@"failed to verify phone number" error:error]; @@ -3467,7 +3472,7 @@ - (void)signInAnonymously { success. */ - (void)signInAnonymouslyAuthDataResult { - [[AppManager auth] signInAnonymouslyAndRetrieveDataWithCompletion: + [[AppManager auth] signInAnonymouslyWithCompletion: ^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { if (error) { [self logFailure:@"sign-in anonymously failed" error:error]; @@ -3492,9 +3497,9 @@ - (void)signInWithGitHub { FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:FIRGitHubAuthProviderID accessToken:accessToken]; if (credential) { - [[AppManager auth] signInWithCredential:credential - completion:^(FIRUser *_Nullable result, - NSError *_Nullable error) { + [[AppManager auth] signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { if (error) { [self logFailure:@"sign-in with provider failed" error:error]; } else { @@ -3660,9 +3665,9 @@ - (void)doSignInWithCustomToken:(NSString *_Nullable)userEnteredTokenText { } - (void)doSignInAndRetrieveDataWithCustomToken:(NSString *_Nullable)userEnteredTokenText { - [[AppManager auth] signInAndRetrieveDataWithCustomToken:userEnteredTokenText - completion:^(FIRAuthDataResult *_Nullable result, - NSError *_Nullable error) { + [[AppManager auth] signInWithCustomToken:userEnteredTokenText + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { if (error) { [self logFailure:@"sign-in with custom token failed" error:error]; [self showMessagePromptWithTitle:kSignInErrorAlertTitle diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m index 91c0cdd63a4..9e6252934ec 100644 --- a/Example/Auth/Tests/FIRAuthTests.m +++ b/Example/Auth/Tests/FIRAuthTests.m @@ -459,7 +459,7 @@ - (void)testFetchProvidersForEmailSuccessDeprecatedProviderID { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" NSArray *allProviders = - @[ FIRGoogleAuthProviderID, FIREmailPasswordAuthProviderID ]; + @[ FIRGoogleAuthProviderID, FIREmailAuthProviderID ]; #pragma clang diagnostic pop OCMExpect([_mockBackend createAuthURI:[OCMArg any] callback:[OCMArg any]]) @@ -552,9 +552,9 @@ - (void)testPhoneAuthSuccess { [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID verificationCode:kVerificationCode]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:credential - completion:^(FIRAuthDataResult *_Nullable authDataResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authDataResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUser:authDataResult.user]; XCTAssertTrue(authDataResult.additionalUserInfo.isNewUser); @@ -578,10 +578,10 @@ - (void)testPhoneAuthMissingVerificationCode { [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID verificationCode:@""]; - [[FIRAuth auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user, + [[FIRAuth auth] signInWithCredential:credential completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - XCTAssertNil(user); + XCTAssertNil(result); XCTAssertEqual(error.code, FIRAuthErrorCodeMissingVerificationCode); [expectation fulfill]; }]; @@ -600,10 +600,10 @@ - (void)testPhoneAuthMissingVerificationID { [[FIRPhoneAuthProvider provider] credentialWithVerificationID:@"" verificationCode:kVerificationCode]; - [[FIRAuth auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user, + [[FIRAuth auth] signInWithCredential:credential completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - XCTAssertNil(user); + XCTAssertNil(result); XCTAssertEqual(error.code, FIRAuthErrorCodeMissingVerificationID); [expectation fulfill]; }]; @@ -784,10 +784,9 @@ - (void)testSignInAndRetrieveDataWithEmailPasswordSuccess { [self expectGetAccountInfo]; XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] signInAndRetrieveDataWithEmail:kEmail - password:kFakePassword - completion:^(FIRAuthDataResult *_Nullable result, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithEmail:kEmail + password:kFakePassword + completion:^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUser:result.user]; XCTAssertFalse(result.additionalUserInfo.isNewUser); @@ -808,10 +807,9 @@ - (void)testSignInAndRetrieveDataWithEmailPasswordFailure { .andDispatchError2([FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]); XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] signInAndRetrieveDataWithEmail:kEmail - password:kFakePassword - completion:^(FIRAuthDataResult *_Nullable result, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithEmail:kEmail + password:kFakePassword + completion:^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(result); XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword); @@ -1060,9 +1058,9 @@ - (void)testSignInWithEmailLinkCredentialSuccess { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *emailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail link:kFakeEmailSignInlink]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:emailCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:emailCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNotNil(authResult.user); XCTAssertEqualObjects(authResult.user.refreshToken, kRefreshToken); @@ -1088,10 +1086,10 @@ - (void)testSignInWithEmailLinkCredentialFailure { FIRAuthCredential *emailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail link:kFakeEmailSignInlink]; [[FIRAuth auth] signInWithCredential:emailCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - XCTAssertNil(user); + XCTAssertNil(result); XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled); XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]); [expectation fulfill]; @@ -1125,10 +1123,10 @@ - (void)testSignInWithEmailCredentialSuccess { FIRAuthCredential *emailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; [[FIRAuth auth] signInWithCredential:emailCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - [self assertUser:user]; + [self assertUser:result.user]; XCTAssertNil(error); [expectation fulfill]; }]; @@ -1161,13 +1159,13 @@ - (void)testSignInWithEmailCredentialSuccessWithDepricatedProvider { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" FIRAuthCredential *emailCredential = - [FIREmailPasswordAuthProvider credentialWithEmail:kEmail password:kFakePassword]; + [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; #pragma clang diagnostic pop [[FIRAuth auth] signInWithCredential:emailCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - [self assertUser:user]; + [self assertUser:result.user]; XCTAssertNil(error); [expectation fulfill]; }]; @@ -1188,10 +1186,10 @@ - (void)testSignInWithEmailCredentialFailure { FIRAuthCredential *emailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; [[FIRAuth auth] signInWithCredential:emailCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - XCTAssertNil(user); + XCTAssertNil(result); XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled); XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]); [expectation fulfill]; @@ -1214,7 +1212,8 @@ - (void)testSignInWithEmailCredentialEmptyPassword { FIRAuthCredential *emailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail password:emptyString]; [[FIRAuth auth] signInWithCredential:emailCredential - completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { + completion:^(FIRAuthDataResult * _Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword); [expectation fulfill]; @@ -1334,7 +1333,7 @@ - (void)testSignInWithGoogleAccountExistsError { FIRAuthCredential *googleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; [[FIRAuth auth] signInWithCredential:googleCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertEqual(error.code, FIRAuthErrorCodeAccountExistsWithDifferentCredential); @@ -1374,10 +1373,10 @@ - (void)testSignInWithGoogleCredentialSuccess { FIRAuthCredential *googleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; [[FIRAuth auth] signInWithCredential:googleCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - [self assertUserGoogle:user]; + [self assertUserGoogle:result.user]; XCTAssertNil(error); [expectation fulfill]; }]; @@ -1430,10 +1429,11 @@ - (void)testSignInWithOAuthCredentialSuccess { FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential; XCTAssertEqualObjects(OAuthCredential.OAuthResponseURLString, kOAuthRequestURI); XCTAssertEqualObjects(OAuthCredential.sessionID, kOAuthSessionID); - [[FIRAuth auth] signInWithCredential:OAuthCredential completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:OAuthCredential + completion:^(FIRAuthDataResult * _Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - [self assertUserGoogle:user]; + [self assertUserGoogle:result.user]; XCTAssertNil(error); [expectation fulfill]; }]; @@ -1474,9 +1474,9 @@ - (void)testSignInAndRetrieveDataWithCredentialSuccess { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *googleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:googleCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:googleCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserGoogle:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1502,10 +1502,10 @@ - (void)testSignInWithGoogleCredentialFailure { FIRAuthCredential *googleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; [[FIRAuth auth] signInWithCredential:googleCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - XCTAssertNil(user); + XCTAssertNil(result.user); XCTAssertEqual(error.code, FIRAuthErrorCodeEmailAlreadyInUse); XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]); [expectation fulfill]; @@ -1588,7 +1588,7 @@ - (void)testSignInAnonymouslyAndRetrieveDataSuccess { [self expectGetAccountInfoAnonymous]; XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] signInAnonymouslyAndRetrieveDataWithCompletion: + [[FIRAuth auth] signInAnonymouslyWithCompletion: ^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserAnonymous:result.user]; @@ -1608,7 +1608,7 @@ - (void)testSignInAnonymouslyAndRetrieveDataFailure { .andDispatchError2([FIRAuthErrorUtils operationNotAllowedErrorWithMessage:nil]); XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] signInAnonymouslyAndRetrieveDataWithCompletion: + [[FIRAuth auth] signInAnonymouslyWithCompletion: ^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(result); @@ -1693,9 +1693,9 @@ - (void)testSignInAndRetrieveDataWithCustomTokenSuccess { [self expectGetAccountInfo]; XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] signInAndRetrieveDataWithCustomToken:kCustomToken - completion:^(FIRAuthDataResult *_Nullable result, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCustomToken:kCustomToken + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUser:result.user]; XCTAssertFalse(result.additionalUserInfo.isNewUser); @@ -1715,9 +1715,9 @@ - (void)testSignInAndRetrieveDataWithCustomTokenFailure { .andDispatchError2([FIRAuthErrorUtils invalidCustomTokenErrorWithMessage:nil]); XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] signInAndRetrieveDataWithCustomToken:kCustomToken - completion:^(FIRAuthDataResult *_Nullable result, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCustomToken:kCustomToken + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(result); XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidCustomToken); @@ -1808,10 +1808,10 @@ - (void)testCreateUserAndRetrieveDataWithEmailPasswordSuccess { [self expectGetAccountInfo]; XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] createUserAndRetrieveDataWithEmail:kEmail - password:kFakePassword - completion:^(FIRAuthDataResult *_Nullable result, - NSError *_Nullable error) { + [[FIRAuth auth] createUserWithEmail:kEmail + password:kFakePassword + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUser:result.user]; XCTAssertTrue(result.additionalUserInfo.isNewUser); @@ -1833,10 +1833,10 @@ - (void)testCreateUserAndRetrieveDataWithEmailPasswordFailure { .andDispatchError2([FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:reason]); XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [[FIRAuth auth] signOut:NULL]; - [[FIRAuth auth] createUserAndRetrieveDataWithEmail:kEmail - password:kFakePassword - completion:^(FIRAuthDataResult *_Nullable result, - NSError *_Nullable error) { + [[FIRAuth auth] createUserWithEmail:kEmail + password:kFakePassword + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(result); XCTAssertEqual(error.code, FIRAuthErrorCodeWeakPassword); diff --git a/Example/Auth/Tests/FIRUserTests.m b/Example/Auth/Tests/FIRUserTests.m index 263f32a68c9..596e6ee0bf5 100644 --- a/Example/Auth/Tests/FIRUserTests.m +++ b/Example/Auth/Tests/FIRUserTests.m @@ -1257,7 +1257,8 @@ - (void)testReauthenticateSuccess { }); FIRAuthCredential *emailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; - [user reauthenticateWithCredential:emailCredential completion:^(NSError *_Nullable error) { + [user reauthenticateWithCredential:emailCredential completion:^(FIRAuthDataResult * _Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(error); // Verify that the current user is unchanged. @@ -1285,9 +1286,9 @@ - (void)testReauthenticateAndRetrieveDataSuccess { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *googleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:googleCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:googleCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserGoogle:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1305,10 +1306,9 @@ - (void)testReauthenticateAndRetrieveDataSuccess { FIRAuthCredential *reauthenticateGoogleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; [authResult.user - reauthenticateAndRetrieveDataWithCredential:reauthenticateGoogleCredential - completion:^(FIRAuthDataResult *_Nullable - reauthenticateAuthResult, - NSError *_Nullable error) { + reauthenticateWithCredential:reauthenticateGoogleCredential + completion:^(FIRAuthDataResult *_Nullable reauthenticateAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(error); // Verify that the current user is unchanged. @@ -1373,7 +1373,8 @@ - (void)testReauthenticateFailure { }); FIRAuthCredential *emailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; - [user reauthenticateWithCredential:emailCredential completion:^(NSError *_Nullable error) { + [user reauthenticateWithCredential:emailCredential completion:^(FIRAuthDataResult * _Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); // Verify user mismatch error. XCTAssertEqual(error.code, FIRAuthErrorCodeUserMismatch); @@ -1408,7 +1409,9 @@ - (void)testReauthenticateUserMismatchFailure { }); FIRAuthCredential *googleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; - [user reauthenticateWithCredential:googleCredential completion:^(NSError *_Nullable error) { + [user reauthenticateWithCredential:googleCredential + completion:^(FIRAuthDataResult * _Nullable result, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); // Verify user mismatch error. XCTAssertEqual(error.code, FIRAuthErrorCodeUserMismatch); @@ -1437,9 +1440,9 @@ - (void)testlinkAndRetrieveDataSuccess { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1456,10 +1459,9 @@ - (void)testlinkAndRetrieveDataSuccess { FIRAuthCredential *linkGoogleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; - [authResult.user linkAndRetrieveDataWithCredential:linkGoogleCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [authResult.user linkWithCredential:linkGoogleCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(error); // Verify that the current user is unchanged. @@ -1498,9 +1500,9 @@ - (void)testlinkAndRetrieveDataError { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1520,10 +1522,9 @@ - (void)testlinkAndRetrieveDataError { FIRAuthCredential *linkGoogleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; - [authResult.user linkAndRetrieveDataWithCredential:linkGoogleCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [authResult.user linkWithCredential:linkGoogleCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(linkAuthResult); XCTAssertEqual(error.code, FIRAuthErrorCodeAccountExistsWithDifferentCredential); @@ -1551,9 +1552,9 @@ - (void)testlinkAndRetrieveDataProviderAlreadyLinked { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1563,10 +1564,9 @@ - (void)testlinkAndRetrieveDataProviderAlreadyLinked { FIRAuthCredential *linkFacebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [authResult.user linkAndRetrieveDataWithCredential:linkFacebookCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [authResult.user linkWithCredential:linkFacebookCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(linkAuthResult); XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked); @@ -1593,9 +1593,9 @@ - (void)testlinkAndRetrieveDataErrorAutoSignOut { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1613,10 +1613,9 @@ - (void)testlinkAndRetrieveDataErrorAutoSignOut { FIRAuthCredential *linkGoogleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; - [authResult.user linkAndRetrieveDataWithCredential:linkGoogleCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [authResult.user linkWithCredential:linkGoogleCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(linkAuthResult); XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled); @@ -1672,9 +1671,9 @@ - (void)testLinkingAnonymousAccountsUpdatesIsAnonymous { }); XCTAssertTrue(user.isAnonymous); - [user linkAndRetrieveDataWithCredential:linkEmailCredential - completion:^(FIRAuthDataResult *_Nullable linkAuthResult, - NSError *_Nullable error) { + [user linkWithCredential:linkEmailCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(error); XCTAssertEqualObjects(user.email, kEmail); @@ -1702,9 +1701,9 @@ - (void)testlinkEmailAndRetrieveDataSuccess { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1737,10 +1736,9 @@ - (void)testlinkEmailAndRetrieveDataSuccess { FIRAuthCredential *linkEmailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; - [authResult.user linkAndRetrieveDataWithCredential:linkEmailCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [authResult.user linkWithCredential:linkEmailCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(error); XCTAssertEqualObjects(linkAuthResult.user.email, kEmail); @@ -1770,9 +1768,9 @@ - (void)testlinkEmailProviderAlreadyLinkedError { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1805,19 +1803,17 @@ - (void)testlinkEmailProviderAlreadyLinkedError { FIRAuthCredential *linkEmailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; - [authResult.user linkAndRetrieveDataWithCredential:linkEmailCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [authResult.user linkWithCredential:linkEmailCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertNil(error); XCTAssertEqualObjects(linkAuthResult.user.email, kEmail); XCTAssertEqualObjects(linkAuthResult.user.displayName, kEmailDisplayName); // Try linking same credential a second time to trigger client side error. - [authResult.user linkAndRetrieveDataWithCredential:linkEmailCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [authResult.user linkWithCredential:linkEmailCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(linkAuthResult); XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked); @@ -1846,9 +1842,9 @@ - (void)testlinkEmailAndRetrieveDataError { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1867,11 +1863,10 @@ - (void)testlinkEmailAndRetrieveDataError { }); FIRAuthCredential *linkEmailCredential = - [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; - [authResult.user linkAndRetrieveDataWithCredential:linkEmailCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; + [authResult.user linkWithCredential:linkEmailCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(linkAuthResult); XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests); @@ -1899,9 +1894,9 @@ - (void)testlinkEmailAndRetrieveDataErrorAutoSignOut { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1921,10 +1916,9 @@ - (void)testlinkEmailAndRetrieveDataErrorAutoSignOut { FIRAuthCredential *linkEmailCredential = [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; - [authResult.user linkAndRetrieveDataWithCredential:linkEmailCredential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [authResult.user linkWithCredential:linkEmailCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); XCTAssertNil(linkAuthResult); XCTAssertEqual(error.code, FIRAuthErrorCodeUserTokenExpired); @@ -1952,9 +1946,9 @@ - (void)testlinkCredentialSuccess { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -1972,10 +1966,10 @@ - (void)testlinkCredentialSuccess { FIRAuthCredential *linkGoogleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; [authResult.user linkWithCredential:linkGoogleCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { XCTAssertNil(error); - id userInfo = user.providerData.firstObject; + id userInfo = result.user.providerData.firstObject; XCTAssertEqual(userInfo.providerID, FIRGoogleAuthProviderID); XCTAssertEqual([FIRAuth auth].currentUser, authResult.user); [expectation fulfill]; @@ -2002,9 +1996,9 @@ - (void)testlinkCredentialError { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -2023,9 +2017,9 @@ - (void)testlinkCredentialError { FIRAuthCredential *linkGoogleCredential = [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken]; [authResult.user linkWithCredential:linkGoogleCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { - XCTAssertNil(user); + XCTAssertNil(result.user); XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled); [expectation fulfill]; }]; @@ -2050,9 +2044,9 @@ - (void)testlinkCredentialProviderAlreadyLinkedError { [[FIRAuth auth] signOut:NULL]; FIRAuthCredential *facebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken]; - [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [[FIRAuth auth] signInWithCredential:facebookCredential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); [self assertUserFacebook:authResult.user]; XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]); @@ -2063,9 +2057,9 @@ - (void)testlinkCredentialProviderAlreadyLinkedError { FIRAuthCredential *linkFacebookCredential = [FIRFacebookAuthProvider credentialWithAccessToken:kGoogleAccessToken]; [authResult.user linkWithCredential:linkFacebookCredential - completion:^(FIRUser *_Nullable user, + completion:^(FIRAuthDataResult * _Nullable result, NSError *_Nullable error) { - XCTAssertNil(user); + XCTAssertNil(result.user); XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked); [expectation fulfill]; }]; @@ -2106,10 +2100,8 @@ - (void)testlinkPhoneAuthCredentialSuccess { FIRPhoneAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID verificationCode:kVerificationCode]; - [user linkAndRetrieveDataWithCredential:credential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [user linkWithCredential:credential completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertNil(error); XCTAssertEqualObjects([FIRAuth auth].currentUser.providerData.firstObject.providerID, FIRPhoneAuthProviderID); @@ -2171,10 +2163,8 @@ - (void)testUnlinkPhoneAuthCredentialSuccess { [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID verificationCode:kVerificationCode]; // Link phone credential. - [user linkAndRetrieveDataWithCredential:credential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [user linkWithCredential:credential completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertNil(error); XCTAssertEqualObjects([FIRAuth auth].currentUser.providerData.firstObject.providerID, FIRPhoneAuthProviderID); @@ -2217,10 +2207,8 @@ - (void)testlinkPhoneAuthCredentialFailure { FIRPhoneAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID verificationCode:kVerificationCode]; - [user linkAndRetrieveDataWithCredential:credential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [user linkWithCredential:credential completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked); [expectation fulfill]; }]; @@ -2275,10 +2263,9 @@ - (void)testlinkPhoneCredentialAlreadyExistsError { FIRPhoneAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID verificationCode:kVerificationCode]; - [user linkAndRetrieveDataWithCredential:credential - completion:^(FIRAuthDataResult *_Nullable - linkAuthResult, - NSError *_Nullable error) { + [user linkWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { XCTAssertNil(linkAuthResult); XCTAssertEqual(error.code, FIRAuthErrorCodeCredentialAlreadyInUse); FIRPhoneAuthCredential *credential = error.userInfo[FIRAuthErrorUserInfoUpdatedCredentialKey]; diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index d4f2342c782..b097e1ace96 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -3679,7 +3679,7 @@ DE26D25C1F7049F1004AE1D3 = { CreatedOnToolsVersion = 9.0; DevelopmentTeam = EQHXZ8M8AV; - LastSwiftMigration = 1010; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; TestTargetID = DE26D22D1F70398A004AE1D3; }; @@ -7034,7 +7034,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Auth/ApiTests/Auth_ApiTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; }; @@ -7069,7 +7069,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.google.Auth-ApiTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Auth/ApiTests/Auth_ApiTests-Bridging-Header.h"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; }; diff --git a/Firebase/Auth/Source/Auth/FIRAuth.m b/Firebase/Auth/Source/Auth/FIRAuth.m index 7fb0b5e9281..abf77061abd 100644 --- a/Firebase/Auth/Source/Auth/FIRAuth.m +++ b/Firebase/Auth/Source/Auth/FIRAuth.m @@ -598,18 +598,6 @@ - (void)signInWithEmail:(NSString *)email }]; } -- (void)signInAndRetrieveDataWithEmail:(NSString *)email - password:(NSString *)password - completion:(nullable FIRAuthDataResultCallback)completion { - dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - FIRAuthDataResultCallback decoratedCallback = - [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion]; - [self internalSignInAndRetrieveDataWithEmail:email - password:password - completion:decoratedCallback]; - }); -} - /** @fn internalSignInAndRetrieveDataWithEmail:password:callback: @brief Signs in using an email address and password. @param email The user's email address. @@ -737,14 +725,13 @@ - (void)internalSignInAndRetrieveDataWithEmail:(nonnull NSString *)email }]; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" - (void)signInWithCredential:(FIRAuthCredential *)credential - completion:(nullable FIRAuthResultCallback)completion { - dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - FIRAuthResultCallback callback = - [self signInFlowAuthResultCallbackByDecoratingCallback:completion]; - [self internalSignInWithCredential:credential callback:callback]; - }); + completion:(nullable FIRAuthDataResultCallback)completion { + [self signInAndRetrieveDataWithCredential:credential completion:completion]; } +#pragma clang diagnostic pop - (void)signInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential completion:(nullable FIRAuthDataResultCallback)completion { @@ -900,57 +887,6 @@ - (void)internalSignInAndRetrieveDataWithCredential:(FIRAuthCredential *)credent }]; } -- (void)signInWithCredential:(FIRAuthCredential *)credential - callback:(FIRAuthResultCallback)callback { - [self signInAndRetrieveDataWithCredential:credential - completion:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { - callback(authResult.user, error); - }]; -} - -- (void)signInAnonymouslyAndRetrieveDataWithCompletion: - (nullable FIRAuthDataResultCallback)completion { - dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - FIRAuthDataResultCallback decoratedCallback = - [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion]; - if (self->_currentUser.anonymous) { - FIRAdditionalUserInfo *additionalUserInfo = - [[FIRAdditionalUserInfo alloc] initWithProviderID:nil - profile:nil - username:nil - isNewUser:NO]; - FIRAuthDataResult *authDataResult = - [[FIRAuthDataResult alloc] initWithUser:self->_currentUser - additionalUserInfo:additionalUserInfo]; - decoratedCallback(authDataResult, nil); - return; - } - [self internalSignInAnonymouslyWithCompletion:^(FIRSignUpNewUserResponse *_Nullable response, - NSError *_Nullable error) { - if (error) { - decoratedCallback(nil, error); - return; - } - [self completeSignInWithAccessToken:response.IDToken - accessTokenExpirationDate:response.approximateExpirationDate - refreshToken:response.refreshToken - anonymous:YES - callback:^(FIRUser *_Nullable user, NSError *_Nullable error) { - FIRAdditionalUserInfo *additionalUserInfo = - [[FIRAdditionalUserInfo alloc] initWithProviderID:nil - profile:nil - username:nil - isNewUser:YES]; - FIRAuthDataResult *authDataResult = - [[FIRAuthDataResult alloc] initWithUser:user - additionalUserInfo:additionalUserInfo]; - decoratedCallback(authDataResult, nil); - }]; - }]; - }); -} - - (void)signInAnonymouslyWithCompletion:(nullable FIRAuthDataResultCallback)completion { dispatch_async(FIRAuthGlobalWorkQueue(), ^{ FIRAuthDataResultCallback decoratedCallback = @@ -999,15 +935,6 @@ - (void)signInWithCustomToken:(NSString *)token }); } -- (void)signInAndRetrieveDataWithCustomToken:(NSString *)token - completion:(nullable FIRAuthDataResultCallback)completion { - dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - FIRAuthDataResultCallback decoratedCallback = - [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion]; - [self internalSignInAndRetrieveDataWithCustomToken:token completion:decoratedCallback]; - }); -} - - (void)createUserWithEmail:(NSString *)email password:(NSString *)password completion:(nullable FIRAuthDataResultCallback)completion { @@ -1041,40 +968,6 @@ - (void)createUserWithEmail:(NSString *)email }); } -- (void)createUserAndRetrieveDataWithEmail:(NSString *)email - password:(NSString *)password - completion:(nullable FIRAuthDataResultCallback)completion { - dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - FIRAuthDataResultCallback decoratedCallback = - [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion]; - [self internalCreateUserWithEmail:email - password:password - completion:^(FIRSignUpNewUserResponse *_Nullable response, - NSError *_Nullable error) { - if (error) { - decoratedCallback(nil, error); - return; - } - - [self completeSignInWithAccessToken:response.IDToken - accessTokenExpirationDate:response.approximateExpirationDate - refreshToken:response.refreshToken - anonymous:NO - callback:^(FIRUser *_Nullable user, NSError *_Nullable error) { - FIRAdditionalUserInfo *additionalUserInfo = - [[FIRAdditionalUserInfo alloc] initWithProviderID:FIREmailAuthProviderID - profile:nil - username:nil - isNewUser:YES]; - FIRAuthDataResult *authDataResult = - [[FIRAuthDataResult alloc] initWithUser:user - additionalUserInfo:additionalUserInfo]; - decoratedCallback(authDataResult, nil); - }]; - }]; - }); -} - - (void)confirmPasswordResetWithCode:(NSString *)code newPassword:(NSString *)newPassword completion:(FIRConfirmPasswordResetCallback)completion { diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h index ca567a0112d..7d0847ea189 100644 --- a/Firebase/Auth/Source/Public/FIRAuth.h +++ b/Firebase/Auth/Source/Public/FIRAuth.h @@ -39,7 +39,8 @@ NS_ASSUME_NONNULL_BEGIN /** @typedef FIRUserUpdateCallback @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); +typedef void (^FIRUserUpdateCallback)(NSError *_Nullable error) + NS_SWIFT_NAME(UserUpdateCallback); /** @typedef FIRAuthStateDidChangeListenerHandle @brief The type of handle returned by `FIRAuth.addAuthStateDidChangeListener:`. @@ -409,68 +410,6 @@ NS_SWIFT_NAME(Auth) link:(NSString *)link completion:(nullable FIRAuthDataResultCallback)completion; -/** @fn signInAndRetrieveDataWithEmail:password:completion: - @brief Please use `signInWithEmail:password:completion:` for Objective-C or - `signIn(withEmail:password:completion:)` for Swift instead. - - @param email The user's email address. - @param password The user's password. - @param completion Optionally; a block which is invoked when the sign in flow finishes, or is - canceled. Invoked asynchronously on the main thread in the future. - */ -- (void)signInAndRetrieveDataWithEmail:(NSString *)email - password:(NSString *)password - completion:(nullable FIRAuthDataResultCallback)completion - DEPRECATED_MSG_ATTRIBUTE( - "Please use signInWithEmail:password:completion: for" - " Objective-C or signIn(withEmail:password:completion:) for" - " Swift instead."); - -/** @fn signInWithCredential:completion: - @brief Please use `signInAndRetrieveDataWithCredential:completion:` for Objective-C or - `signInAndRetrieveData(with:completion:)` for swift instead - - @param credential The credential supplied by the IdP. - @param completion Optionally; a block which is invoked when the sign in flow finishes, or is - canceled. Invoked asynchronously on the main thread in the future. - - @remarks Possible error codes: - - + `FIRAuthErrorCodeInvalidCredential` - Indicates the supplied credential is invalid. - This could happen if it has expired or it is malformed. - + `FIRAuthErrorCodeOperationNotAllowed` - Indicates that accounts - with the identity provider represented by the credential are not enabled. - Enable them in the Auth section of the Firebase console. - + `FIRAuthErrorCodeAccountExistsWithDifferentCredential` - Indicates the email asserted - by the credential (e.g. the email in a Facebook access token) is already in use by an - existing account, that cannot be authenticated with this sign-in method. Call - fetchProvidersForEmail for this user’s email and then prompt them to sign in with any of - the sign-in providers returned. This error will only be thrown if the "One account per - email address" setting is enabled in the Firebase console, under Auth settings. - + `FIRAuthErrorCodeUserDisabled` - Indicates the user's account is disabled. - + `FIRAuthErrorCodeWrongPassword` - Indicates the user attempted sign in with an - incorrect password, if credential is of the type EmailPasswordAuthCredential. - + `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is malformed. - + `FIRAuthErrorCodeMissingVerificationID` - Indicates that the phone auth credential was - created with an empty verification ID. - + `FIRAuthErrorCodeMissingVerificationCode` - Indicates that the phone auth credential - was created with an empty verification code. - + `FIRAuthErrorCodeInvalidVerificationCode` - Indicates that the phone auth credential - was created with an invalid verification Code. - + `FIRAuthErrorCodeInvalidVerificationID` - Indicates that the phone auth credential was - created with an invalid verification ID. - + `FIRAuthErrorCodeSessionExpired` - Indicates that the SMS code has expired. - - - - @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods - */ -- (void)signInWithCredential:(FIRAuthCredential *)credential - completion:(nullable FIRAuthResultCallback)completion DEPRECATED_MSG_ATTRIBUTE( - "Please use signInAndRetrieveDataWithCredential:completion:" - " for Objective-C or signInAndRetrieveData(with:completion:)" - " for Swift instead."); - /** @fn signInWithProvider:UIDelegate:completion: @brief Signs in using the provided auth provider instance. @@ -520,6 +459,15 @@ NS_SWIFT_NAME(Auth) completion:(nullable FIRAuthDataResultCallback)completion; /** @fn signInAndRetrieveDataWithCredential:completion: + @brief Please use signInWithCredential:completion: for Objective-C or " + "signIn(with:completion:) for Swift instead. + */ +- (void)signInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion +DEPRECATED_MSG_ATTRIBUTE("Please use signInWithCredential:completion: for Objective-C or " + "signIn(with:completion:) for Swift instead."); + +/** @fn signInWithCredential:completion: @brief Asynchronously signs in to Firebase with the given 3rd-party credentials (e.g. a Facebook login Access Token, a Google ID Token/Access Token pair, etc.) and returns additional identity provider data. @@ -530,37 +478,35 @@ NS_SWIFT_NAME(Auth) @remarks Possible error codes: - + `FIRAuthErrorCodeInvalidCredential` - Indicates the supplied credential is invalid. - This could happen if it has expired or it is malformed. - + `FIRAuthErrorCodeOperationNotAllowed` - Indicates that accounts - with the identity provider represented by the credential are not enabled. - Enable them in the Auth section of the Firebase console. - + `FIRAuthErrorCodeAccountExistsWithDifferentCredential` - Indicates the email asserted - by the credential (e.g. the email in a Facebook access token) is already in use by an - existing account, that cannot be authenticated with this sign-in method. Call - fetchProvidersForEmail for this user’s email and then prompt them to sign in with any of - the sign-in providers returned. This error will only be thrown if the "One account per - email address" setting is enabled in the Firebase console, under Auth settings. - + `FIRAuthErrorCodeUserDisabled` - Indicates the user's account is disabled. - + `FIRAuthErrorCodeWrongPassword` - Indicates the user attempted sign in with an - incorrect password, if credential is of the type EmailPasswordAuthCredential. - + `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is malformed. - + `FIRAuthErrorCodeMissingVerificationID` - Indicates that the phone auth credential was - created with an empty verification ID. - + `FIRAuthErrorCodeMissingVerificationCode` - Indicates that the phone auth credential - was created with an empty verification code. - + `FIRAuthErrorCodeInvalidVerificationCode` - Indicates that the phone auth credential - was created with an invalid verification Code. - + `FIRAuthErrorCodeInvalidVerificationID` - Indicates that the phone auth credential was - created with an invalid verification ID. - + `FIRAuthErrorCodeSessionExpired` - Indicates that the SMS code has expired. - - + + `FIRAuthErrorCodeInvalidCredential` - Indicates the supplied credential is invalid. + This could happen if it has expired or it is malformed. + + `FIRAuthErrorCodeOperationNotAllowed` - Indicates that accounts + with the identity provider represented by the credential are not enabled. + Enable them in the Auth section of the Firebase console. + + `FIRAuthErrorCodeAccountExistsWithDifferentCredential` - Indicates the email asserted + by the credential (e.g. the email in a Facebook access token) is already in use by an + existing account, that cannot be authenticated with this sign-in method. Call + fetchProvidersForEmail for this user’s email and then prompt them to sign in with any of + the sign-in providers returned. This error will only be thrown if the "One account per + email address" setting is enabled in the Firebase console, under Auth settings. + + `FIRAuthErrorCodeUserDisabled` - Indicates the user's account is disabled. + + `FIRAuthErrorCodeWrongPassword` - Indicates the user attempted sign in with an + incorrect password, if credential is of the type EmailPasswordAuthCredential. + + `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is malformed. + + `FIRAuthErrorCodeMissingVerificationID` - Indicates that the phone auth credential was + created with an empty verification ID. + + `FIRAuthErrorCodeMissingVerificationCode` - Indicates that the phone auth credential + was created with an empty verification code. + + `FIRAuthErrorCodeInvalidVerificationCode` - Indicates that the phone auth credential + was created with an invalid verification Code. + + `FIRAuthErrorCodeInvalidVerificationID` - Indicates that the phone auth credential was + created with an invalid verification ID. + + `FIRAuthErrorCodeSessionExpired` - Indicates that the SMS code has expired. @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods - */ -- (void)signInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential - completion:(nullable FIRAuthDataResultCallback)completion; +*/ +- (void)signInWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion; /** @fn signInAnonymouslyWithCompletion: @brief Asynchronously creates and becomes an anonymous user. @@ -579,17 +525,6 @@ NS_SWIFT_NAME(Auth) */ - (void)signInAnonymouslyWithCompletion:(nullable FIRAuthDataResultCallback)completion; -/** @fn signInAnonymouslyAndRetrieveDataWithCompletion: - @brief `Please use sign `signInAnonymouslyWithCompletion:` for Objective-C or - `signInAnonymously(Completion:)` for Swift instead. - @param completion Optionally; a block which is invoked when the sign in finishes, or is - canceled. Invoked asynchronously on the main thread in the future. - */ -- (void)signInAnonymouslyAndRetrieveDataWithCompletion: - (nullable FIRAuthDataResultCallback)completion - DEPRECATED_MSG_ATTRIBUTE("Please use signInAnonymouslyWithCompletion: for Objective-C or" - " signInAnonymously(Completion:) for swift instead."); - /** @fn signInWithCustomToken:completion: @brief Asynchronously signs in to Firebase with the given Auth token. @@ -604,28 +539,11 @@ NS_SWIFT_NAME(Auth) + `FIRAuthErrorCodeCustomTokenMismatch` - Indicates the service account and the API key belong to different projects. - - @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods. */ - (void)signInWithCustomToken:(NSString *)token completion:(nullable FIRAuthDataResultCallback)completion; -/** @fn signInAndRetrieveDataWithCustomToken:completion: - @brief Please use `signInWithCustomToken:completion:` or `signIn(withCustomToken:completion:)` - for Swift instead. - - @param token A self-signed custom auth token. - @param completion Optionally; a block which is invoked when the sign in finishes, or is - canceled. Invoked asynchronously on the main thread in the future. - */ -- (void)signInAndRetrieveDataWithCustomToken:(NSString *)token - completion:(nullable FIRAuthDataResultCallback)completion - DEPRECATED_MSG_ATTRIBUTE( - "Please use signInWithCustomToken:completion:" - "for Objective-C or signIn(withCustomToken:completion:) for" - " Swift instead."); - /** @fn createUserWithEmail:password:completion: @brief Creates and, on success, signs in a user with the given email address and password. @@ -652,23 +570,6 @@ NS_SWIFT_NAME(Auth) password:(NSString *)password completion:(nullable FIRAuthDataResultCallback)completion; -/** @fn createUserAndRetrieveDataWithEmail:password:completion: - @brief Please use `createUserAndRetrieveDataWithEmail:password:completion:` or - `createUser(withEmail:password:completion:)` for Swift instead. - - @param email The user's email address. - @param password The user's desired password. - @param completion Optionally; a block which is invoked when the sign up flow finishes, or is - canceled. Invoked asynchronously on the main thread in the future. - */ -- (void)createUserAndRetrieveDataWithEmail:(NSString *)email - password:(NSString *)password - completion:(nullable FIRAuthDataResultCallback)completion - DEPRECATED_MSG_ATTRIBUTE( - "Please use createUserWithEmail:password:completion: for" - " Objective-C or createUser(withEmail:password:completion:)" - " for Swift instead."); - /** @fn confirmPasswordResetWithCode:newPassword:completion: @brief Resets the password given a code sent to the user outside of the app and a new password for the user. diff --git a/Firebase/Auth/Source/Public/FIRAuthErrors.h b/Firebase/Auth/Source/Public/FIRAuthErrors.h index 2c90274e5b8..63cd3432b60 100644 --- a/Firebase/Auth/Source/Public/FIRAuthErrors.h +++ b/Firebase/Auth/Source/Public/FIRAuthErrors.h @@ -44,20 +44,6 @@ NS_SWIFT_NAME(AuthErrors) */ extern NSString *const FIRAuthErrorDomain NS_SWIFT_NAME(AuthErrorDomain); -/** - @brief Please use `FIRAuthErrorUserInfoUpdatedCredentialKey` for Objective C or - `AuthErrorUserInfoUpdatedCredentialKey` for Swift instead. - */ -extern NSString *const FIRAuthUpdatedCredentialKey - NS_SWIFT_NAME(AuthUpdatedCredentialKey) __attribute__((deprecated)); - -/** - @brief Please use `FIRAuthErrorUserInfoNameKey` for Objective C or - `AuthErrorUserInfoNameKey` for Swift instead. - */ -extern NSString *const FIRAuthErrorNameKey - NS_SWIFT_NAME(AuthErrorNameKey) __attribute__((deprecated)); - /** @brief The name of the key for the error short string of an error code. */ diff --git a/Firebase/Auth/Source/Public/FIREmailAuthProvider.h b/Firebase/Auth/Source/Public/FIREmailAuthProvider.h index b6375bd2a69..bb8f85dc2f4 100644 --- a/Firebase/Auth/Source/Public/FIREmailAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIREmailAuthProvider.h @@ -33,13 +33,7 @@ extern NSString *const FIREmailLinkAuthSignInMethod NS_SWIFT_NAME(EmailLinkAuthS /** @brief A string constant identifying the email & password sign-in method. */ -extern NSString *const FIREmailPasswordAuthSignInMethod - NS_SWIFT_NAME(EmailPasswordAuthSignInMethod); - -/** - @brief Please use `FIREmailAuthProviderID` for Objective-C or `EmailAuthProviderID` for Swift instead. - */ -extern NSString *const FIREmailPasswordAuthProviderID __attribute__((deprecated)); +extern NSString *const FIREmailPasswordAuthSignInMethod NS_SWIFT_NAME(EmailPasswordAuthSignInMethod); /** @class FIREmailAuthProvider @brief A concrete implementation of `FIRAuthProvider` for Email & Password Sign In. @@ -47,12 +41,6 @@ extern NSString *const FIREmailPasswordAuthProviderID __attribute__((deprecated) NS_SWIFT_NAME(EmailAuthProvider) @interface FIREmailAuthProvider : NSObject -/** @typedef FIREmailPasswordAuthProvider - @brief Please use `FIREmailAuthProvider` instead. - */ -typedef FIREmailAuthProvider FIREmailPasswordAuthProvider __attribute__((deprecated)); - - /** @fn credentialWithEmail:password: @brief Creates an `FIRAuthCredential` for an email & password sign in. diff --git a/Firebase/Auth/Source/Public/FIROAuthProvider.h b/Firebase/Auth/Source/Public/FIROAuthProvider.h index e921e27286d..57635e94e20 100644 --- a/Firebase/Auth/Source/Public/FIROAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIROAuthProvider.h @@ -23,18 +23,6 @@ NS_ASSUME_NONNULL_BEGIN -/** - @brief A string constant identifying the Microsoft identity provider. - */ -extern NSString *const FIRMicrosoftAuthProviderID NS_SWIFT_NAME(MicrosoftAuthProviderID) - DEPRECATED_MSG_ATTRIBUTE("Please use \"microsoft.com\" instead."); - -/** - @brief A string constant identifying the Yahoo identity provider. - */ -extern NSString *const FIRYahooAuthProviderID NS_SWIFT_NAME(YahooAuthProviderID) - DEPRECATED_MSG_ATTRIBUTE("Please use \"yahoo.com\" instead."); - /** @class FIROAuthProvider @brief A concrete implementation of `FIRAuthProvider` for generic OAuth Providers. */ diff --git a/Firebase/Auth/Source/Public/FIRUser.h b/Firebase/Auth/Source/Public/FIRUser.h index cc5d0a00120..caf05e1006f 100644 --- a/Firebase/Auth/Source/Public/FIRUser.h +++ b/Firebase/Auth/Source/Public/FIRUser.h @@ -221,18 +221,6 @@ NS_SWIFT_NAME(User) - (void)reloadWithCompletion:(nullable FIRUserProfileChangeCallback)completion; /** @fn reauthenticateWithCredential:completion: - @brief Please use reauthenticateAndRetrieveDataWithCredential:completion: for Objective-C or - reauthenticateAndRetrieveData(WithCredential:completion:) for Swift instead. - */ -- (void)reauthenticateWithCredential:(FIRAuthCredential *)credential - completion:(nullable FIRUserProfileChangeCallback)completion - DEPRECATED_MSG_ATTRIBUTE( "Please use" - " reauthenticateAndRetrieveDataWithCredential:completion: for" - " Objective-C or" - " reauthenticateAndRetrieveData(WithCredential:completion:)" - " for Swift instead."); - -/** @fn reauthenticateAndRetrieveDataWithCredential:completion: @brief Renews the user's authentication tokens by validating a fresh set of credentials supplied by the user and returns additional identity provider data. @@ -268,8 +256,18 @@ NS_SWIFT_NAME(User) @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods. */ -- (void)reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *) credential - completion:(nullable FIRAuthDataResultCallback) completion; +- (void)reauthenticateWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion; + +/** @fn reauthenticateAndRetrieveDataWithCredential:completion: + @brief Please use linkWithCredential:completion: for Objective-C " + "or link(withCredential:completion:) for Swift instead. + */ +- (void)reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion +DEPRECATED_MSG_ATTRIBUTE( "Please use reauthenticateWithCredential:completion: for" + " Objective-C or reauthenticate(withCredential:completion:)" + " for Swift instead."); /** @fn getIDTokenResultWithCompletion: @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired. @@ -326,20 +324,18 @@ NS_SWIFT_NAME(User) - (void)getIDTokenForcingRefresh:(BOOL)forceRefresh completion:(nullable FIRAuthTokenCallback)completion; -/** @fn linkWithCredential:completion: - @brief Please use linkAndRetrieveDataWithCredential:completion: for Objective-C or - linkAndRetrieveData(WithCredential:completion:) for Swift instead. +/** @fn linkAndRetrieveDataWithCredential:completion: + @brief Please use linkWithCredential:completion: for Objective-C " + "or link(withCredential:completion:) for Swift instead. */ -- (void)linkWithCredential:(FIRAuthCredential *)credential - completion:(nullable FIRAuthResultCallback)completion DEPRECATED_MSG_ATTRIBUTE( - "Please use linkAndRetrieveDataWithCredential:completion: for" - " Objective-C or" - " linkAndRetrieveData(WithCredential:completion:) for" - " Swift instead."); +- (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion +DEPRECATED_MSG_ATTRIBUTE("Please use linkWithCredential:completion: for Objective-C " + "or link(withCredential:completion:) for Swift instead."); -/** @fn linkAndRetrieveDataWithCredential:completion: +/** @fn linkWithCredential:completion: @brief Associates a user account from a third-party identity provider with this user and - returns additional identity provider data. + returns additional identity provider data. @param credential The credential for the identity provider. @param completion Optionally; the block invoked when the unlinking is complete, or fails. @@ -350,19 +346,18 @@ NS_SWIFT_NAME(User) + `FIRAuthErrorCodeProviderAlreadyLinked` - Indicates an attempt to link a provider of a type already linked to this account. + `FIRAuthErrorCodeCredentialAlreadyInUse` - Indicates an attempt to link with a - credential - that has already been linked with a different Firebase account. + credential that has already been linked with a different Firebase account. + `FIRAuthErrorCodeOperationNotAllowed` - Indicates that accounts with the identity provider represented by the credential are not enabled. Enable them in the Auth section of the Firebase console. @remarks This method may also return error codes associated with updateEmail:completion: and - updatePassword:completion: on FIRUser. + updatePassword:completion: on FIRUser. @remarks See `FIRAuthErrors` for a list of error codes that are common to all FIRUser methods. */ -- (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *) credential - completion:(nullable FIRAuthDataResultCallback) completion; +- (void)linkWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion; /** @fn unlinkFromProvider:completion: @brief Disassociates a user account from a third-party identity provider with this user. diff --git a/Firebase/Auth/Source/User/FIRUser.m b/Firebase/Auth/Source/User/FIRUser.m index f0c4e7c992e..3529ff2f58e 100644 --- a/Firebase/Auth/Source/User/FIRUser.m +++ b/Firebase/Auth/Source/User/FIRUser.m @@ -751,18 +751,16 @@ - (void)reloadWithCompletion:(nullable FIRUserProfileChangeCallback)completion { #pragma mark - -- (void)reauthenticateWithCredential:(FIRAuthCredential *)credential - completion:(nullable FIRUserProfileChangeCallback)completion { - FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { - completion(error); - }; - [self reauthenticateAndRetrieveDataWithCredential:credential completion:callback]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (void)reauthenticateWithCredential:(FIRAuthCredential *) credential + completion:(nullable FIRAuthDataResultCallback) completion { + [self reauthenticateAndRetrieveDataWithCredential:credential completion:completion]; } +#pragma clang diagnostic pop -- (void) - reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *) credential - completion:(nullable FIRAuthDataResultCallback) completion { +- (void)reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *) credential + completion:(nullable FIRAuthDataResultCallback) completion { dispatch_async(FIRAuthGlobalWorkQueue(), ^{ [self->_auth internalSignInAndRetrieveDataWithCredential:credential isReauthentication:YES @@ -973,14 +971,13 @@ - (void)internalGetTokenForcingRefresh:(BOOL)forceRefresh }]; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" - (void)linkWithCredential:(FIRAuthCredential *)credential - completion:(nullable FIRAuthResultCallback)completion { - FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { - completion(authResult.user, error); - }; - [self linkAndRetrieveDataWithCredential:credential completion:callback]; + completion:(nullable FIRAuthDataResultCallback)completion { + [self linkAndRetrieveDataWithCredential:credential completion:completion]; } +#pragma clang diagnostic pop - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential completion:(nullable FIRAuthDataResultCallback)completion { From 43b3dc629e990adf5222f1e4c0af4cb97f6f23aa Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Fri, 5 Apr 2019 16:46:49 -0700 Subject: [PATCH 148/214] Set Swift back to version 4.2 (#2742) --- Example/Firebase.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index b097e1ace96..d4f2342c782 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -3679,7 +3679,7 @@ DE26D25C1F7049F1004AE1D3 = { CreatedOnToolsVersion = 9.0; DevelopmentTeam = EQHXZ8M8AV; - LastSwiftMigration = 1020; + LastSwiftMigration = 1010; ProvisioningStyle = Automatic; TestTargetID = DE26D22D1F70398A004AE1D3; }; @@ -7034,7 +7034,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Auth/ApiTests/Auth_ApiTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; }; @@ -7069,7 +7069,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.google.Auth-ApiTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Auth/ApiTests/Auth_ApiTests-Bridging-Header.h"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; }; From e05d9111eebb5ced9021d2d5aebb18a2db5f8b18 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Sun, 7 Apr 2019 07:31:57 -0700 Subject: [PATCH 149/214] Remove implicit Analytics dependency from FIAM (#2739) --- FirebaseInAppMessaging.podspec | 1 - InAppMessaging/Example/Podfile | 1 - 2 files changed, 2 deletions(-) diff --git a/FirebaseInAppMessaging.podspec b/FirebaseInAppMessaging.podspec index cf2740a8bf1..e5073920517 100644 --- a/FirebaseInAppMessaging.podspec +++ b/FirebaseInAppMessaging.podspec @@ -33,7 +33,6 @@ See more product details at https://firebase.google.com/products/in-app-messagin } s.dependency 'FirebaseCore' - s.ios.dependency 'FirebaseAnalytics' s.ios.dependency 'FirebaseAnalyticsInterop' s.dependency 'FirebaseInstanceID' end diff --git a/InAppMessaging/Example/Podfile b/InAppMessaging/Example/Podfile index 04a75ebcee4..b4b0f26b64a 100644 --- a/InAppMessaging/Example/Podfile +++ b/InAppMessaging/Example/Podfile @@ -12,7 +12,6 @@ target 'InAppMessaging_Example_iOS' do pod 'FirebaseInAppMessagingDisplay', :path => '../..' pod 'FirebaseInAppMessaging', :path => '../..' pod 'FirebaseAnalyticsInterop', :path => '../..' - pod 'FirebaseAnalytics' pod 'FirebaseDynamicLinks', :path => '../..' end From 491cec5ed5144ea2e681ba8e2ca80f285b894e0a Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Sun, 7 Apr 2019 18:05:14 -0700 Subject: [PATCH 150/214] Auth and Messaging require GoogleUtilities 5.6 (#2744) --- FirebaseAuth.podspec | 4 ++-- FirebaseMessaging.podspec | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index a2b9a7f8f1a..d73470ccf4c 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -63,7 +63,7 @@ supports email and password accounts, as well as several 3rd party authenticatio s.ios.framework = 'SafariServices' s.dependency 'FirebaseAuthInterop', '~> 1.0' s.dependency 'FirebaseCore', '~> 5.2' - s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.2' - s.dependency 'GoogleUtilities/Environment', '~> 5.2' + s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.6' + s.dependency 'GoogleUtilities/Environment', '~> 5.6' s.dependency 'GTMSessionFetcher/Core', '~> 1.1' end diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index e1d36c8e70c..808c77542ed 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -41,9 +41,9 @@ device, and it is completely free. s.dependency 'FirebaseAnalyticsInterop', '~> 1.1' s.dependency 'FirebaseCore', '~> 5.2' s.dependency 'FirebaseInstanceID', '~> 3.6' - s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.3' - s.dependency 'GoogleUtilities/Reachability', '~> 5.3' - s.dependency 'GoogleUtilities/Environment', '~> 5.3' - s.dependency 'GoogleUtilities/UserDefaults', '~> 5.3' + s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.6' + s.dependency 'GoogleUtilities/Reachability', '~> 5.6' + s.dependency 'GoogleUtilities/Environment', '~> 5.6' + s.dependency 'GoogleUtilities/UserDefaults', '~> 5.6' s.dependency 'Protobuf', '~> 3.1' end From 626bfb513cdd45e4e1bfd599abd094887e4d3c44 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Sun, 7 Apr 2019 22:49:54 -0400 Subject: [PATCH 151/214] Remove FIRAnalyticsConfiguration from Public header. (#2728) * Remove FIRAnalyticsConfiguration from Public header. We haven't removed it entirely yet because we need to make changes within Analytics to verify that the global data collection switch is still honored and tested well. This can wait until after the breaking change because the header and code will be internal after this change. * Fixed incomplete comment. * Remove self importing header. --- .../Tests/FIRAnalyticsConfigurationTest.m | 68 ------------------- Example/Core/Tests/FIRAppTest.m | 2 +- Example/Core/Tests/FIRConfigurationTest.m | 2 +- Firebase/Core/FIRAnalyticsConfiguration.m | 18 +---- Firebase/Core/FIRApp.m | 4 +- Firebase/Core/FIRConfiguration.m | 8 +-- ...Internal.h => FIRAnalyticsConfiguration.h} | 7 +- .../Core/Private/FIRConfigurationInternal.h | 29 ++++++++ .../Core/Public/FIRAnalyticsConfiguration.h | 56 --------------- Firebase/Core/Public/FIRConfiguration.h | 9 +-- Firebase/Core/Public/FirebaseCore.h | 1 - 11 files changed, 45 insertions(+), 159 deletions(-) rename Firebase/Core/Private/{FIRAnalyticsConfiguration+Internal.h => FIRAnalyticsConfiguration.h} (91%) create mode 100644 Firebase/Core/Private/FIRConfigurationInternal.h delete mode 100644 Firebase/Core/Public/FIRAnalyticsConfiguration.h diff --git a/Example/Core/Tests/FIRAnalyticsConfigurationTest.m b/Example/Core/Tests/FIRAnalyticsConfigurationTest.m index 43babb0f544..7fb579f1172 100644 --- a/Example/Core/Tests/FIRAnalyticsConfigurationTest.m +++ b/Example/Core/Tests/FIRAnalyticsConfigurationTest.m @@ -16,7 +16,6 @@ #import "FIRTestCase.h" -#import #import @interface FIRAnalyticsConfigurationTest : FIRTestCase @@ -48,73 +47,6 @@ - (void)testSharedInstance { XCTAssertNotNil(analyticsConfig); } -/// Test that setting the minimum session interval on the singleton fires a notification. -- (void)testMinimumSessionIntervalNotification { - // Pick a value to set as the session interval and verify it's in the userInfo dictionary of the - // posted notification. - NSNumber *sessionInterval = @2601; - - // Set up the expectation for the notification. - FIRAnalyticsConfiguration *config = [FIRAnalyticsConfiguration sharedInstance]; - NSString *notificationName = kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotification; - [self expectNotificationForObserver:self.observerMock - notificationName:notificationName - object:config - userInfo:@{notificationName : sessionInterval}]; - - // Trigger the notification. - [config setMinimumSessionInterval:[sessionInterval integerValue]]; - - // Verify the observer mock. - OCMVerifyAll(self.observerMock); -} - -/// Test that setting the minimum session timeout interval on the singleton fires a notification. -- (void)testSessionTimeoutIntervalNotification { - // Pick a value to set as the timeout interval and verify it's in the userInfo dictionary of the - // posted notification. - NSNumber *timeoutInterval = @1000; - - // Set up the expectation for the notification. - FIRAnalyticsConfiguration *config = [FIRAnalyticsConfiguration sharedInstance]; - NSString *notificationName = kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification; - [self expectNotificationForObserver:self.observerMock - notificationName:notificationName - object:config - userInfo:@{notificationName : timeoutInterval}]; - - // Trigger the notification. - [config setSessionTimeoutInterval:[timeoutInterval integerValue]]; - - /// Verify the observer mock. - OCMVerifyAll(self.observerMock); -} - -- (void)testSettingAnalyticsCollectionEnabled { - // Test setting to enabled. The ordering matters for these notifications. - FIRAnalyticsConfiguration *config = [FIRAnalyticsConfiguration sharedInstance]; - NSString *notificationName = kFIRAnalyticsConfigurationSetEnabledNotification; - [self.notificationCenter addMockObserver:self.observerMock name:notificationName object:config]; - - [self.observerMock setExpectationOrderMatters:YES]; - [[self.observerMock expect] notificationWithName:notificationName - object:config - userInfo:@{notificationName : @YES}]; - - // Test setting to enabled. - [config setAnalyticsCollectionEnabled:YES]; - - // Expect the second notification. - [[self.observerMock expect] notificationWithName:notificationName - object:config - userInfo:@{notificationName : @NO}]; - - // Test setting to disabled. - [config setAnalyticsCollectionEnabled:NO]; - - OCMVerifyAll(self.observerMock); -} - - (void)testSettingAnalyticsCollectionPersistence { id userDefaultsMock = OCMPartialMock([NSUserDefaults standardUserDefaults]); FIRAnalyticsConfiguration *config = [FIRAnalyticsConfiguration sharedInstance]; diff --git a/Example/Core/Tests/FIRAppTest.m b/Example/Core/Tests/FIRAppTest.m index d688e3a0c88..fef375f5bac 100644 --- a/Example/Core/Tests/FIRAppTest.m +++ b/Example/Core/Tests/FIRAppTest.m @@ -15,7 +15,7 @@ #import "FIRTestCase.h" #import "FIRTestComponents.h" -#import +#import #import #import diff --git a/Example/Core/Tests/FIRConfigurationTest.m b/Example/Core/Tests/FIRConfigurationTest.m index 2b3ff463139..8045104b8bb 100644 --- a/Example/Core/Tests/FIRConfigurationTest.m +++ b/Example/Core/Tests/FIRConfigurationTest.m @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import "FIRConfiguration.h" +#import #import "FIRTestCase.h" diff --git a/Firebase/Core/FIRAnalyticsConfiguration.m b/Firebase/Core/FIRAnalyticsConfiguration.m index e584839efca..3ade594dff6 100644 --- a/Firebase/Core/FIRAnalyticsConfiguration.m +++ b/Firebase/Core/FIRAnalyticsConfiguration.m @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import "FIRAnalyticsConfiguration.h" +#import -#import "Private/FIRAnalyticsConfiguration+Internal.h" +#import "FIRAnalyticsConfiguration.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-implementations" @@ -39,20 +39,6 @@ - (void)postNotificationName:(NSString *)name value:(id)value { userInfo:@{name : value}]; } -- (void)setMinimumSessionInterval:(NSTimeInterval)minimumSessionInterval { - [self postNotificationName:kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotification - value:@(minimumSessionInterval)]; -} - -- (void)setSessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval { - [self postNotificationName:kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification - value:@(sessionTimeoutInterval)]; -} - -- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled { - [self setAnalyticsCollectionEnabled:analyticsCollectionEnabled persistSetting:YES]; -} - - (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled persistSetting:(BOOL)shouldPersist { // Persist the measurementEnabledState. Use FIRAnalyticsEnabledState values instead of YES/NO. diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index 520dcbcb865..d3b6c460ece 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -15,11 +15,11 @@ #include #import "FIRApp.h" -#import "FIRConfiguration.h" -#import "Private/FIRAnalyticsConfiguration+Internal.h" +#import "Private/FIRAnalyticsConfiguration.h" #import "Private/FIRAppInternal.h" #import "Private/FIRBundleUtil.h" #import "Private/FIRComponentContainerInternal.h" +#import "Private/FIRConfigurationInternal.h" #import "Private/FIRLibrary.h" #import "Private/FIRLogger.h" #import "Private/FIROptionsInternal.h" diff --git a/Firebase/Core/FIRConfiguration.m b/Firebase/Core/FIRConfiguration.m index f8378d77790..607757440bd 100644 --- a/Firebase/Core/FIRConfiguration.m +++ b/Firebase/Core/FIRConfiguration.m @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import "FIRConfiguration.h" +#import "FIRConfigurationInternal.h" +#import "Private/FIRConfigurationInternal.h" + +#import "FIRAnalyticsConfiguration.h" extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel); @@ -30,10 +33,7 @@ + (instancetype)sharedInstance { - (instancetype)init { self = [super init]; if (self) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" _analyticsConfiguration = [FIRAnalyticsConfiguration sharedInstance]; -#pragma clang diagnostic pop } return self; } diff --git a/Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h b/Firebase/Core/Private/FIRAnalyticsConfiguration.h similarity index 91% rename from Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h rename to Firebase/Core/Private/FIRAnalyticsConfiguration.h index be624b49410..13b2af34fb3 100644 --- a/Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h +++ b/Firebase/Core/Private/FIRAnalyticsConfiguration.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRAnalyticsConfiguration.h" +#import /// Values stored in analyticsEnabledState. Never alter these constants since they must match with /// values persisted to disk. @@ -38,7 +38,10 @@ static NSString *const kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotifi static NSString *const kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification = @"FIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification"; -@interface FIRAnalyticsConfiguration (Internal) +@interface FIRAnalyticsConfiguration : NSObject + +/// Returns the shared instance of FIRAnalyticsConfiguration. ++ (FIRAnalyticsConfiguration *)sharedInstance; /// Sets whether analytics collection is enabled for this app on this device, and a flag to persist /// the value or not. The setting should not be persisted if being set by the global data collection diff --git a/Firebase/Core/Private/FIRConfigurationInternal.h b/Firebase/Core/Private/FIRConfigurationInternal.h new file mode 100644 index 00000000000..ee168867079 --- /dev/null +++ b/Firebase/Core/Private/FIRConfigurationInternal.h @@ -0,0 +1,29 @@ +/* + * Copyright 2019 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 "FIRConfiguration.h" + +@class FIRAnalyticsConfiguration; + +@interface FIRConfiguration () + +/** + * The configuration class for Firebase Analytics. This should be removed once the logic for + * enabling and disabling Analytics is moved to Analytics. + */ +@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration; + +@end diff --git a/Firebase/Core/Public/FIRAnalyticsConfiguration.h b/Firebase/Core/Public/FIRAnalyticsConfiguration.h deleted file mode 100644 index add4a38ee7b..00000000000 --- a/Firebase/Core/Public/FIRAnalyticsConfiguration.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 - -NS_ASSUME_NONNULL_BEGIN - -/** - * This class provides configuration fields for Firebase Analytics. - */ -NS_SWIFT_NAME(AnalyticsConfiguration) -DEPRECATED_MSG_ATTRIBUTE("Use these methods directly on the `Analytics` class.") -@interface FIRAnalyticsConfiguration : NSObject - -/** - * Returns the shared instance of FIRAnalyticsConfiguration. - */ -+ (FIRAnalyticsConfiguration *)sharedInstance NS_SWIFT_NAME(shared()); - -/** - * Deprecated. - * Sets the minimum engagement time in seconds required to start a new session. The default value - * is 10 seconds. - */ -- (void)setMinimumSessionInterval:(NSTimeInterval)minimumSessionInterval - DEPRECATED_MSG_ATTRIBUTE( - "Sessions are started immediately. More information at https://bit.ly/2FU46av"); - -/** - * Sets the interval of inactivity in seconds that terminates the current session. The default - * value is 1800 seconds (30 minutes). - */ -- (void)setSessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval; - -/** - * Sets whether analytics collection is enabled for this app on this device. This setting is - * persisted across app sessions. By default it is enabled. - */ -- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firebase/Core/Public/FIRConfiguration.h b/Firebase/Core/Public/FIRConfiguration.h index b88fcaf9f8d..2b8e678ceb8 100644 --- a/Firebase/Core/Public/FIRConfiguration.h +++ b/Firebase/Core/Public/FIRConfiguration.h @@ -16,14 +16,12 @@ #import -#import "FIRAnalyticsConfiguration.h" #import "FIRLoggerLevel.h" NS_ASSUME_NONNULL_BEGIN /** - * This interface provides global level properties that the developer can tweak, and the singleton - * of the Firebase Analytics configuration class. + * This interface provides global level properties that the developer can tweak. */ NS_SWIFT_NAME(FirebaseConfiguration) @interface FIRConfiguration : NSObject @@ -31,11 +29,6 @@ NS_SWIFT_NAME(FirebaseConfiguration) /** Returns the shared configuration object. */ @property(class, nonatomic, readonly) FIRConfiguration *sharedInstance NS_SWIFT_NAME(shared); -/** The configuration class for Firebase Analytics. */ -@property(nonatomic, readwrite) - FIRAnalyticsConfiguration *analyticsConfiguration DEPRECATED_MSG_ATTRIBUTE( - "Use the methods available here directly on the `Analytics` class."); - /** * 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/FirebaseCore.h b/Firebase/Core/Public/FirebaseCore.h index fa26f694b75..95119aed924 100644 --- a/Firebase/Core/Public/FirebaseCore.h +++ b/Firebase/Core/Public/FirebaseCore.h @@ -14,7 +14,6 @@ * limitations under the License. */ -#import "FIRAnalyticsConfiguration.h" #import "FIRApp.h" #import "FIRConfiguration.h" #import "FIRLoggerLevel.h" From 6f040b31ae0609bf92bdceaef3ad89580480074f Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Sun, 7 Apr 2019 19:56:18 -0700 Subject: [PATCH 152/214] Stop requiring multiple Core version updates (#2745) --- Example/Core/Tests/FIROptionsTest.m | 14 +++++++++++ Firebase/Core/FIROptions.m | 37 ++++++++++++++++------------- Releases/update-versions.py | 19 --------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Example/Core/Tests/FIROptionsTest.m b/Example/Core/Tests/FIROptionsTest.m index 3c078d93a43..b20d6dace69 100644 --- a/Example/Core/Tests/FIROptionsTest.m +++ b/Example/Core/Tests/FIROptionsTest.m @@ -575,6 +575,7 @@ - (void)testVersionFormat { XCTAssertEqual(numberOfMatches, 1, @"Incorrect library version format."); } +// TODO: The version test will break when the Firebase major version hits 10. - (void)testVersionConsistency { const char *versionString = [kFIRLibraryVersionID UTF8String]; int major = versionString[0] - '0'; @@ -584,4 +585,17 @@ - (void)testVersionConsistency { XCTAssertEqualObjects(str, [NSString stringWithUTF8String:(const char *)FIRCoreVersionString]); } +// Repeat test with more Objective C. +// TODO: The version test will break when the Firebase major version hits 10. +- (void)testVersionConsistency2 { + NSRange major = NSMakeRange(0, 1); + NSRange minor = NSMakeRange(1, 2); + NSRange patch = NSMakeRange(3, 2); + NSString *str = + [NSString stringWithFormat:@"%@.%d.%d", [kFIRLibraryVersionID substringWithRange:major], + [[kFIRLibraryVersionID substringWithRange:minor] intValue], + [[kFIRLibraryVersionID substringWithRange:patch] intValue]]; + XCTAssertEqualObjects(str, [NSString stringWithUTF8String:(const char *)FIRCoreVersionString]); +} + @end diff --git a/Firebase/Core/FIROptions.m b/Firebase/Core/FIROptions.m index 72901f51a37..3e6a3a0458f 100644 --- a/Firebase/Core/FIROptions.m +++ b/Firebase/Core/FIROptions.m @@ -17,6 +17,7 @@ #import "Private/FIRErrors.h" #import "Private/FIRLogger.h" #import "Private/FIROptionsInternal.h" +#import "Private/FIRVersion.h" // Keys for the strings in the plist file. NSString *const kFIRAPIKey = @"API_KEY"; @@ -39,11 +40,13 @@ NSString *const kFIRIsAnalyticsEnabled = @"IS_ANALYTICS_ENABLED"; NSString *const kFIRIsSignInEnabled = @"IS_SIGNIN_ENABLED"; -// Library version ID. -NSString *const kFIRLibraryVersionID = @"5" // Major version (one or more digits) - @"04" // Minor version (exactly 2 digits) - @"01" // Build number (exactly 2 digits) - @"000"; // Fixed "000" +// Library version ID formatted like: +// @"5" // Major version (one or more digits) +// @"04" // Minor version (exactly 2 digits) +// @"01" // Build number (exactly 2 digits) +// @"000"; // Fixed "000" +NSString *kFIRLibraryVersionID; + // Plist file name. NSString *const kServiceInfoFileName = @"GoogleService-Info"; // Plist file type. @@ -109,17 +112,9 @@ + (FIROptions *)defaultOptions { + (void)initialize { // Report FirebaseCore version for useragent string - NSRange major = NSMakeRange(0, 1); - NSRange minor = NSMakeRange(1, 2); - NSRange patch = NSMakeRange(3, 2); - [FIRApp - registerLibrary:@"fire-ios" - withVersion:[NSString stringWithFormat:@"%@.%d.%d", - [kFIRLibraryVersionID substringWithRange:major], - [[kFIRLibraryVersionID substringWithRange:minor] - intValue], - [[kFIRLibraryVersionID substringWithRange:patch] - intValue]]]; + [FIRApp registerLibrary:@"fire-ios" + withVersion:[NSString stringWithUTF8String:FIRCoreVersionString]]; + NSDictionary *info = [[NSBundle mainBundle] infoDictionary]; NSString *xcodeVersion = info[@"DTXcodeBuild"]; NSString *sdkVersion = info[@"DTSDKBuild"]; @@ -295,6 +290,16 @@ - (void)setGoogleAppID:(NSString *)googleAppID { } - (NSString *)libraryVersionID { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // The unit tests are set up to catch anything that does not properly convert. + NSString *version = [NSString stringWithUTF8String:FIRCoreVersionString]; + NSArray *components = [version componentsSeparatedByString:@"."]; + NSString *major = [components objectAtIndex:0]; + NSString *minor = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:1] intValue]]; + NSString *patch = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:2] intValue]]; + kFIRLibraryVersionID = [NSString stringWithFormat:@"%@%@%@000", major, minor, patch]; + }); return kFIRLibraryVersionID; } diff --git a/Releases/update-versions.py b/Releases/update-versions.py index 097828f3798..c05f30156dc 100755 --- a/Releases/update-versions.py +++ b/Releases/update-versions.py @@ -114,24 +114,6 @@ def CreateReleaseBranch(release_branch, base_branch): release_branch)) -def UpdateFIROptions(git_root, version_data): - """Update version specifier in FIROptions.m. - - Args: - git_root: root of git checkout. - version_data: dictionary of versions to be updated. - """ - core_version = version_data['FirebaseCore'] - major, minor, patch = core_version.split('.') - path = os.path.join(git_root, 'Firebase', 'Core', 'FIROptions.m') - os.system("sed -E -i.bak 's/[[:digit:]]+\"[[:space:]]*\\/\\/ Major/" - "{}\" \\/\\/ Major/' {}".format(major, path)) - os.system("sed -E -i.bak 's/[[:digit:]]+\"[[:space:]]*\\/\\/ Minor/" - "{}\" \\/\\/ Minor/' {}".format(minor.zfill(2), path)) - os.system("sed -E -i.bak 's/[[:digit:]]+\"[[:space:]]*\\/\\/ Build/" - "{}\" \\/\\/ Build/' {}".format(patch.zfill(2), path)) - - def UpdatePodSpecs(git_root, version_data, firebase_version): """Update the podspecs with the right version. @@ -253,7 +235,6 @@ def UpdateVersions(): release_branch = 'release-{}'.format(args.version) CreateReleaseBranch(release_branch, args.base_branch) - UpdateFIROptions(git_root, version_data) UpdatePodSpecs(git_root, version_data, args.version) UpdatePodfiles(git_root, args.version) From 76a003f4c8f84413be687391af1bcb3f7ba3183e Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 06:58:49 -0700 Subject: [PATCH 153/214] Clean up unused Core error codes (#2737) * Core patches can only be 2 digits --- Example/Core/Tests/FIRAppTest.m | 10 +++++----- Example/Podfile | 6 +++--- Firebase/Core/FIRErrors.m | 7 ------- Firebase/Core/Private/FIRErrorCode.h | 17 ----------------- Firebase/Core/Private/FIRErrors.h | 8 -------- .../FDLURLComponents/FDLURLComponents.m | 2 +- Firebase/DynamicLinks/FIRDynamicLinks+Private.h | 5 +++++ Firebase/DynamicLinks/FIRDynamicLinks.m | 6 ++++++ Firebase/InstanceID/FIRInstanceID.m | 1 - FirebaseCore.podspec | 2 +- Firestore/Example/Podfile | 6 +++--- 11 files changed, 24 insertions(+), 46 deletions(-) diff --git a/Example/Core/Tests/FIRAppTest.m b/Example/Core/Tests/FIRAppTest.m index fef375f5bac..1a273e37f01 100644 --- a/Example/Core/Tests/FIRAppTest.m +++ b/Example/Core/Tests/FIRAppTest.m @@ -258,13 +258,13 @@ - (void)testDeleteApp { } - (void)testErrorForSubspecConfigurationFailure { - NSError *error = [FIRApp errorForSubspecConfigurationFailureWithDomain:kFirebaseAdMobErrorDomain - errorCode:FIRErrorCodeAdMobFailed - service:kFIRServiceAdMob + NSError *error = [FIRApp errorForSubspecConfigurationFailureWithDomain:kFirebaseCoreErrorDomain + errorCode:-38 + service:kFIRServiceAuth reason:@"some reason"]; XCTAssertNotNil(error); - XCTAssert([error.domain isEqualToString:kFirebaseAdMobErrorDomain]); - XCTAssert(error.code == FIRErrorCodeAdMobFailed); + XCTAssert([error.domain isEqualToString:kFirebaseCoreErrorDomain]); + XCTAssert(error.code == -38); XCTAssert([error.description containsString:@"Configuration failed for"]); } diff --git a/Example/Podfile b/Example/Podfile index b18cf150ca2..251a70e4c05 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -3,8 +3,8 @@ #source 'https://github.com/CocoaPods/Specs.git' # Uncomment the next two lines for pre-release testing on public repo -#source 'https://github.com/Firebase/SpecsStaging.git' -#source 'https://github.com/CocoaPods/Specs.git' +source 'https://github.com/Firebase/SpecsStaging.git' +source 'https://github.com/CocoaPods/Specs.git' use_frameworks! @@ -19,7 +19,7 @@ target 'Core_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.20.1' + pod 'Firebase/CoreOnly', '5.20.990' target 'Core_Tests_iOS' do inherit! :search_paths diff --git a/Firebase/Core/FIRErrors.m b/Firebase/Core/FIRErrors.m index 914de4bfccb..72120c5c98f 100644 --- a/Firebase/Core/FIRErrors.m +++ b/Firebase/Core/FIRErrors.m @@ -15,14 +15,7 @@ #import "Private/FIRErrors.h" NSString *const kFirebaseErrorDomain = @"com.firebase"; -NSString *const kFirebaseAdMobErrorDomain = @"com.firebase.admob"; -NSString *const kFirebaseAppInviteErrorDomain = @"com.firebase.appinvite"; -NSString *const kFirebaseAuthErrorDomain = @"com.firebase.auth"; -NSString *const kFirebaseCloudMessagingErrorDomain = @"com.firebase.cloudmessaging"; NSString *const kFirebaseConfigErrorDomain = @"com.firebase.config"; NSString *const kFirebaseCoreErrorDomain = @"com.firebase.core"; -NSString *const kFirebaseDatabaseErrorDomain = @"com.firebase.database"; -NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink"; -NSString *const kFirebaseInstanceIDErrorDomain = @"com.firebase.instanceid"; NSString *const kFirebasePerfErrorDomain = @"com.firebase.perf"; NSString *const kFirebaseStorageErrorDomain = @"com.firebase.storage"; diff --git a/Firebase/Core/Private/FIRErrorCode.h b/Firebase/Core/Private/FIRErrorCode.h index 01d3c56e36b..f77b3d00246 100644 --- a/Firebase/Core/Private/FIRErrorCode.h +++ b/Firebase/Core/Private/FIRErrorCode.h @@ -34,22 +34,5 @@ typedef NS_ENUM(NSInteger, FIRErrorCode) { /** * Error code for failing to configure a specific service. */ - FIRErrorCodeAdMobFailed = -110, - FIRErrorCodeAppInviteFailed = -112, - FIRErrorCodeCloudMessagingFailed = -113, FIRErrorCodeConfigFailed = -114, - FIRErrorCodeDatabaseFailed = -115, - FIRErrorCodeCrashReportingFailed = -118, - FIRErrorCodeDurableDeepLinkFailed = -119, - FIRErrorCodeAuthFailed = -120, - FIRErrorCodeInstanceIDFailed = -121, - FIRErrorCodeStorageFailed = -123, - - /** - * Error codes returned by Dynamic Links - */ - FIRErrorCodeDynamicLinksStrongMatchNotAvailable = -124, - FIRErrorCodeDynamicLinksManualRetrievalNotEnabled = -125, - FIRErrorCodeDynamicLinksPendingLinkOnlyAvailableAtFirstLaunch = -126, - FIRErrorCodeDynamicLinksPendingLinkRetrievalAlreadyRunning = -127, }; diff --git a/Firebase/Core/Private/FIRErrors.h b/Firebase/Core/Private/FIRErrors.h index 464435a5d34..19e47328ace 100644 --- a/Firebase/Core/Private/FIRErrors.h +++ b/Firebase/Core/Private/FIRErrors.h @@ -19,14 +19,6 @@ #include "FIRErrorCode.h" extern NSString *const kFirebaseErrorDomain; -extern NSString *const kFirebaseAdMobErrorDomain; -extern NSString *const kFirebaseAppInviteErrorDomain; -extern NSString *const kFirebaseAuthErrorDomain; -extern NSString *const kFirebaseCloudMessagingErrorDomain; extern NSString *const kFirebaseConfigErrorDomain; extern NSString *const kFirebaseCoreErrorDomain; -extern NSString *const kFirebaseDatabaseErrorDomain; -extern NSString *const kFirebaseDurableDeepLinkErrorDomain; -extern NSString *const kFirebaseInstanceIDErrorDomain; extern NSString *const kFirebasePerfErrorDomain; -extern NSString *const kFirebaseStorageErrorDomain; diff --git a/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m b/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m index 5b7cf853777..47f6b5814b0 100644 --- a/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m +++ b/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m @@ -18,6 +18,7 @@ #import "DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h" #import "DynamicLinks/FDLURLComponents/FIRDynamicLinkComponentsKeyProvider.h" +#import "DynamicLinks/FIRDynamicLinks+Private.h" #import "DynamicLinks/Public/FDLURLComponents.h" #import "DynamicLinks/Logging/FDLLogging.h" @@ -45,7 +46,6 @@ @implementation FIRDynamicLinkGoogleAnalyticsParameters { static NSString *const kFDLUTMCampaignKey = @"utm_campaign"; static NSString *const kFDLUTMTermKey = @"utm_term"; static NSString *const kFDLUTMContentKey = @"utm_content"; -static NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink"; + (instancetype)parameters { return [[self alloc] init]; diff --git a/Firebase/DynamicLinks/FIRDynamicLinks+Private.h b/Firebase/DynamicLinks/FIRDynamicLinks+Private.h index f1e3c28cdda..e771a5ec67f 100644 --- a/Firebase/DynamicLinks/FIRDynamicLinks+Private.h +++ b/Firebase/DynamicLinks/FIRDynamicLinks+Private.h @@ -30,6 +30,11 @@ FOUNDATION_EXPORT NSString *const kFIRDLVersion; */ FOUNDATION_EXPORT NSString *const kFIRDLReadDeepLinkAfterInstallKey; +/** + * Label exceptions from FDL. + */ +FOUNDATION_EXPORT NSString *const kFirebaseDurableDeepLinkErrorDomain; + @interface FIRDynamicLinks (Private) /** diff --git a/Firebase/DynamicLinks/FIRDynamicLinks.m b/Firebase/DynamicLinks/FIRDynamicLinks.m index 5b940015abf..53f5fed0e7b 100644 --- a/Firebase/DynamicLinks/FIRDynamicLinks.m +++ b/Firebase/DynamicLinks/FIRDynamicLinks.m @@ -55,6 +55,12 @@ NSString *const kFIRDLReadDeepLinkAfterInstallKey = @"com.google.appinvite.readDeeplinkAfterInstall"; +// Label exceptions from FDL. +NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink"; + +// Error code from FDL. +static const NSInteger FIRErrorCodeDurableDeepLinkFailed = -119; + // We should only open url once. We use the following key to store the state in the user defaults. static NSString *const kFIRDLOpenURLKey = @"com.google.appinvite.openURL"; diff --git a/Firebase/InstanceID/FIRInstanceID.m b/Firebase/InstanceID/FIRInstanceID.m index f5c4a6eb551..96c0574bd15 100644 --- a/Firebase/InstanceID/FIRInstanceID.m +++ b/Firebase/InstanceID/FIRInstanceID.m @@ -71,7 +71,6 @@ static NSString *const kFIRIIDErrorDomain = @"com.firebase.instanceid"; static NSString *const kFIRIIDServiceInstanceID = @"InstanceID"; -// This should be the same value as FIRErrorCodeInstanceIDFailed, which we can't import directly static NSInteger const kFIRIIDErrorCodeInstanceIDFailed = -121; typedef void (^FIRInstanceIDKeyPairHandler)(FIRInstanceIDKeyPair *keyPair, NSError *error); diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 47a714ba1e7..9689dbe101f 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCore' - s.version = '5.4.1' + s.version = '5.4.90' s.summary = 'Firebase Core for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index be25897e37c..5085ca47c90 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -3,8 +3,8 @@ #source 'https://github.com/CocoaPods/Specs.git' # Uncomment the next two lines for pre-release testing on public repo -#source 'https://github.com/Firebase/SpecsStaging.git' -#source 'https://github.com/CocoaPods/Specs.git' +source 'https://github.com/Firebase/SpecsStaging.git' +source 'https://github.com/CocoaPods/Specs.git' use_frameworks! @@ -14,7 +14,7 @@ target 'Firestore_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.20.1' + pod 'Firebase/CoreOnly', '5.20.990' pod 'FirebaseAuth', :path => '../../' pod 'FirebaseAuthInterop', :path => '../../' From fd6f93d89dc57fa44bd81349066de50ee21f8fef Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 08:54:21 -0700 Subject: [PATCH 154/214] Begin migrating FIRLoggerService declarations to clients (#2720) --- Example/Core/Tests/FIRLoggerTest.m | 6 ------ Example/Podfile | 2 +- Firebase/Core/FIRLogger.m | 11 +++-------- Firebase/Core/Private/FIRLogger.h | 7 ------- Firebase/Database/Utilities/FUtilities.h | 3 +++ Firebase/Database/Utilities/FUtilities.m | 1 + Firebase/DynamicLinks/Logging/FDLLogging.m | 2 ++ Firebase/Messaging/FIRMessagingLogger.m | 2 ++ FirebaseCore.podspec | 2 +- Firestore/Example/Podfile | 2 +- .../core/src/firebase/firestore/util/log_apple.mm | 2 ++ 11 files changed, 16 insertions(+), 24 deletions(-) diff --git a/Example/Core/Tests/FIRLoggerTest.m b/Example/Core/Tests/FIRLoggerTest.m index 797a133cdd4..7f9e1a783c0 100644 --- a/Example/Core/Tests/FIRLoggerTest.m +++ b/Example/Core/Tests/FIRLoggerTest.m @@ -76,15 +76,9 @@ - (void)testStableVariables { XCTAssertEqualObjects(kFIRLoggerABTesting, @"[Firebase/ABTesting]"); XCTAssertEqualObjects(kFIRLoggerAdMob, @"[Firebase/AdMob]"); XCTAssertEqualObjects(kFIRLoggerAnalytics, @"[Firebase/Analytics]"); - XCTAssertEqualObjects(kFIRLoggerAuth, @"[Firebase/Auth]"); XCTAssertEqualObjects(kFIRLoggerCore, @"[Firebase/Core]"); - XCTAssertEqualObjects(kFIRLoggerDatabase, @"[Firebase/Database]"); - XCTAssertEqualObjects(kFIRLoggerDynamicLinks, @"[Firebase/DynamicLinks]"); - XCTAssertEqualObjects(kFIRLoggerInstanceID, @"[Firebase/InstanceID]"); XCTAssertEqualObjects(kFIRLoggerMLKit, @"[Firebase/MLKit]"); - XCTAssertEqualObjects(kFIRLoggerMessaging, @"[Firebase/Messaging]"); XCTAssertEqualObjects(kFIRLoggerRemoteConfig, @"[Firebase/RemoteConfig]"); - XCTAssertEqualObjects(kFIRLoggerStorage, @"[Firebase/Storage]"); } - (void)testInitializeASLForNonDebugMode { diff --git a/Example/Podfile b/Example/Podfile index 251a70e4c05..93886d48f07 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -19,7 +19,7 @@ target 'Core_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.20.990' + pod 'Firebase/CoreOnly', '5.20.991' target 'Core_Tests_iOS' do inherit! :search_paths diff --git a/Firebase/Core/FIRLogger.m b/Firebase/Core/FIRLogger.m index 7fe30196309..6994a107694 100644 --- a/Firebase/Core/FIRLogger.m +++ b/Firebase/Core/FIRLogger.m @@ -20,21 +20,16 @@ #import "Private/FIRVersion.h" +FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]"; + +// All the FIRLoggerService definitions should be migrated to clients. Do not add new ones! FIRLoggerService kFIRLoggerABTesting = @"[Firebase/ABTesting]"; FIRLoggerService kFIRLoggerAdMob = @"[Firebase/AdMob]"; FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]"; FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]"; -FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]"; -FIRLoggerService kFIRLoggerDatabase = @"[Firebase/Database]"; -FIRLoggerService kFIRLoggerDynamicLinks = @"[Firebase/DynamicLinks]"; -FIRLoggerService kFIRLoggerFirestore = @"[Firebase/Firestore]"; -FIRLoggerService kFIRLoggerInstanceID = @"[Firebase/InstanceID]"; FIRLoggerService kFIRLoggerMLKit = @"[Firebase/MLKit]"; -FIRLoggerService kFIRLoggerMessaging = @"[Firebase/Messaging]"; FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]"; FIRLoggerService kFIRLoggerRemoteConfig = @"[Firebase/RemoteConfig]"; -FIRLoggerService kFIRLoggerStorage = @"[Firebase/Storage]"; -FIRLoggerService kFIRLoggerSwizzler = @"[FirebaseSwizzlingUtilities]"; /// Arguments passed on launch. NSString *const kFIRDisableDebugModeApplicationArgument = @"-FIRDebugDisabled"; diff --git a/Firebase/Core/Private/FIRLogger.h b/Firebase/Core/Private/FIRLogger.h index f9523ab5e90..41f37480b8e 100644 --- a/Firebase/Core/Private/FIRLogger.h +++ b/Firebase/Core/Private/FIRLogger.h @@ -30,16 +30,9 @@ extern FIRLoggerService kFIRLoggerAdMob; extern FIRLoggerService kFIRLoggerAnalytics; extern FIRLoggerService kFIRLoggerAuth; extern FIRLoggerService kFIRLoggerCore; -extern FIRLoggerService kFIRLoggerDatabase; -extern FIRLoggerService kFIRLoggerDynamicLinks; -extern FIRLoggerService kFIRLoggerFirestore; -extern FIRLoggerService kFIRLoggerInstanceID; extern FIRLoggerService kFIRLoggerMLKit; -extern FIRLoggerService kFIRLoggerMessaging; extern FIRLoggerService kFIRLoggerPerf; extern FIRLoggerService kFIRLoggerRemoteConfig; -extern FIRLoggerService kFIRLoggerStorage; -extern FIRLoggerService kFIRLoggerSwizzler; /** * The key used to store the logger's error count. diff --git a/Firebase/Database/Utilities/FUtilities.h b/Firebase/Database/Utilities/FUtilities.h index f7fe7a54455..2f2c154fa2f 100644 --- a/Firebase/Database/Utilities/FUtilities.h +++ b/Firebase/Database/Utilities/FUtilities.h @@ -15,6 +15,8 @@ */ #import +#import + #import "FParsedUrl.h" @interface FUtilities : NSObject @@ -70,6 +72,7 @@ FOUNDATION_EXPORT NSString *const kFPersistenceLogTag; } \ } while(0) +extern FIRLoggerService kFIRLoggerDatabase; BOOL FFIsLoggingEnabled(FLogLevel logLevel); void firebaseUncaughtExceptionHandler(NSException *exception); void firebaseJobsTroll(void); diff --git a/Firebase/Database/Utilities/FUtilities.m b/Firebase/Database/Utilities/FUtilities.m index d0a9a437fb8..21704ca9eef 100644 --- a/Firebase/Database/Utilities/FUtilities.m +++ b/Firebase/Database/Utilities/FUtilities.m @@ -27,6 +27,7 @@ #pragma mark - #pragma mark C functions +FIRLoggerService kFIRLoggerDatabase = @"[Firebase/Database]"; static FLogLevel logLevel = FLogLevelInfo; // Default log level is info static NSMutableDictionary* options = nil; diff --git a/Firebase/DynamicLinks/Logging/FDLLogging.m b/Firebase/DynamicLinks/Logging/FDLLogging.m index 1e6934fa43f..ca968d6cfc6 100644 --- a/Firebase/DynamicLinks/Logging/FDLLogging.m +++ b/Firebase/DynamicLinks/Logging/FDLLogging.m @@ -18,6 +18,8 @@ #ifdef GIN_SCION_LOGGING #import + +FIRLoggerService kFIRLoggerDynamicLinks = @"[Firebase/DynamicLinks]"; #endif // GIN_SCION_LOGGING #ifdef GIN_SCION_LOGGING diff --git a/Firebase/Messaging/FIRMessagingLogger.m b/Firebase/Messaging/FIRMessagingLogger.m index 62eb8dae33a..d3c96d21135 100644 --- a/Firebase/Messaging/FIRMessagingLogger.m +++ b/Firebase/Messaging/FIRMessagingLogger.m @@ -18,6 +18,8 @@ #import +FIRLoggerService kFIRLoggerMessaging = @"[Firebase/Messaging]"; + @implementation FIRMessagingLogger + (instancetype)standardLogger { diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 9689dbe101f..c3f7f368b82 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCore' - s.version = '5.4.90' + s.version = '5.4.91' s.summary = 'Firebase Core for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index 5085ca47c90..e0b8a0758ee 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -14,7 +14,7 @@ target 'Firestore_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.20.990' + pod 'Firebase/CoreOnly', '5.20.991' pod 'FirebaseAuth', :path => '../../' pod 'FirebaseAuthInterop', :path => '../../' diff --git a/Firestore/core/src/firebase/firestore/util/log_apple.mm b/Firestore/core/src/firebase/firestore/util/log_apple.mm index f1745e204ce..808dac92449 100644 --- a/Firestore/core/src/firebase/firestore/util/log_apple.mm +++ b/Firestore/core/src/firebase/firestore/util/log_apple.mm @@ -32,6 +32,8 @@ namespace { +const FIRLoggerService kFIRLoggerFirestore = @"[Firebase/Firestore]"; + // Translates a C++ LogLevel to the equivalent Objective-C FIRLoggerLevel FIRLoggerLevel ToFIRLoggerLevel(LogLevel level) { switch (level) { From 3ad75ff35f3cd9628aeac0a08d4bec337855def6 Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 8 Apr 2019 19:59:18 +0200 Subject: [PATCH 155/214] Fix Xcode's interactive error highlighting (#2752) * Fix Xcode's interactive error highlighting * Update project for Firebase 5.20 --- .../Firestore.xcodeproj/project.pbxproj | 4 +-- Firestore/Example/Podfile | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index b3ecfa8f1ed..7b76b035360 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -2411,7 +2411,7 @@ "${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-tvOS/openssl_grpc.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-tvOS/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities-Environment-Logger-tvOS/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities-85d94889/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/Protobuf-tvOS10.0/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/gRPC-C++-tvOS/grpcpp.framework", "${BUILT_PRODUCTS_DIR}/gRPC-Core-tvOS/grpc.framework", @@ -2553,7 +2553,7 @@ "${PODS_ROOT}/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC-macOS/openssl_grpc.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher-macOS/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities-Environment-Logger-macOS/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities-65885a2c/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/Protobuf-macOS10.11/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/gRPC-C++-macOS/grpcpp.framework", "${BUILT_PRODUCTS_DIR}/gRPC-Core-macOS/grpc.framework", diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index e0b8a0758ee..cf4875c9a37 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -8,6 +8,35 @@ source 'https://github.com/CocoaPods/Specs.git' use_frameworks! + +# Appends the given +paths+ to the HEADER_SEARCH_PATHS setting for the given +# target in all build configurations. +def append_header_search_path(target, *paths) + setting = 'HEADER_SEARCH_PATHS' + target.build_configurations.each do |config| + config.build_settings[setting] ||= '$(inherited)' + paths.each do |path| + config.build_settings[setting] << ' ' + config.build_settings[setting] << path + end + end +end + + +post_install do |installer| + installer.pods_project.targets.each do |target| + # Building the FirebaseFirestore framework proper seems fragile in Xcode: + # changes to public headers seem to cause weird cascading failures in the + # IDE's pre-build error detection. The issue seems to be that the Pod's + # public headers are found by clang at build time through a copy headers + # phase, but the interactive editor won't have those in place, especially + # when the build is broken by current edits. + if target.name =~ /^FirebaseFirestore/ + append_header_search_path(target, '$(PODS_ROOT)/../../../Firestore/Source/Public') + end + end +end + target 'Firestore_Example_iOS' do platform :ios, '8.0' From c3f9340f0771e7514fcd0951dd18c5e727c59713 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Mon, 8 Apr 2019 11:20:05 -0700 Subject: [PATCH 156/214] Deprecate fetchProvidersForEmail (#2756) --- Firebase/Auth/Source/Public/FIRAuth.h | 19 +++++-------------- Firebase/Auth/Source/Public/FIRUser.h | 8 ++++---- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h index 7d0847ea189..a5e7f752b8f 100644 --- a/Firebase/Auth/Source/Public/FIRAuth.h +++ b/Firebase/Auth/Source/Public/FIRAuth.h @@ -326,22 +326,13 @@ NS_SWIFT_NAME(Auth) - (void)updateCurrentUser:(FIRUser *)user completion:(nullable FIRUserUpdateCallback)completion; /** @fn fetchProvidersForEmail:completion: - @brief Fetches the list of IdPs that can be used for signing in with the provided email address. - Useful for an "identifier-first" sign-in flow. - - @param email The email address for which to obtain a list of identity providers. - @param completion Optionally; a block which is invoked when the list of providers for the - specified email address is ready or an error was encountered. Invoked asynchronously on the - main thread in the future. - - @remarks Possible error codes: - - + `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is malformed. - - @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods. + @brief Please use fetchSignInMethodsForEmail:completion: for Objective-C or + fetchSignInMethods(forEmail:completion:) for Swift instead. */ - (void)fetchProvidersForEmail:(NSString *)email - completion:(nullable FIRProviderQueryCallback)completion; + completion:(nullable FIRProviderQueryCallback)completion +DEPRECATED_MSG_ATTRIBUTE("Please use fetchSignInMethodsForEmail:completion: for Objective-C or " + "fetchSignInMethods(forEmail:completion:) for Swift instead."); /** @fn fetchSignInMethodsForEmail:completion: @brief Fetches the list of all sign-in methods previously used for the provided email address. diff --git a/Firebase/Auth/Source/Public/FIRUser.h b/Firebase/Auth/Source/Public/FIRUser.h index caf05e1006f..5055ea64531 100644 --- a/Firebase/Auth/Source/Public/FIRUser.h +++ b/Firebase/Auth/Source/Public/FIRUser.h @@ -260,8 +260,8 @@ NS_SWIFT_NAME(User) completion:(nullable FIRAuthDataResultCallback)completion; /** @fn reauthenticateAndRetrieveDataWithCredential:completion: - @brief Please use linkWithCredential:completion: for Objective-C " - "or link(withCredential:completion:) for Swift instead. + @brief Please use linkWithCredential:completion: for Objective-C + or link(withCredential:completion:) for Swift instead. */ - (void)reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *)credential completion:(nullable FIRAuthDataResultCallback)completion @@ -325,8 +325,8 @@ DEPRECATED_MSG_ATTRIBUTE( "Please use reauthenticateWithCredential:completion: f completion:(nullable FIRAuthTokenCallback)completion; /** @fn linkAndRetrieveDataWithCredential:completion: - @brief Please use linkWithCredential:completion: for Objective-C " - "or link(withCredential:completion:) for Swift instead. + @brief Please use linkWithCredential:completion: for Objective-C + or link(withCredential:completion:) for Swift instead. */ - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential completion:(nullable FIRAuthDataResultCallback)completion From 58e36e15cb5c813c47070be283364cfa0336fbe9 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Mon, 8 Apr 2019 16:16:04 -0400 Subject: [PATCH 157/214] Migrate FSTStringValue to FSTDelegateValue (#2719) --- .../Tests/Integration/FSTDatastoreTests.mm | 2 +- .../Example/Tests/Model/FSTDocumentTests.mm | 4 +- .../Example/Tests/Model/FSTFieldValueTests.mm | 14 ++-- .../Tests/Remote/FSTSerializerBetaTests.mm | 2 +- Firestore/Source/API/FSTUserDataConverter.mm | 2 +- Firestore/Source/Model/FSTFieldValue.h | 7 -- Firestore/Source/Model/FSTFieldValue.mm | 78 ++++--------------- Firestore/Source/Remote/FSTSerializerBeta.mm | 2 +- .../firebase/firestore/model/field_value.cc | 4 + 9 files changed, 31 insertions(+), 84 deletions(-) diff --git a/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm index e3d3199ef42..325abd95c33 100644 --- a/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm +++ b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm @@ -246,7 +246,7 @@ - (FSTSetMutation *)setMutation { return [[FSTSetMutation alloc] initWithKey:DocumentKey::FromPathString("rooms/eros") value:[[FSTObjectValue alloc] - initWithDictionary:@{@"name" : [FSTStringValue stringValue:@"Eros"]}] + initWithDictionary:@{@"name" : FieldValue::FromString("Eros").Wrap()}] precondition:Precondition::None()]; } diff --git a/Firestore/Example/Tests/Model/FSTDocumentTests.mm b/Firestore/Example/Tests/Model/FSTDocumentTests.mm index ae8db452d95..6db2a6c842e 100644 --- a/Firestore/Example/Tests/Model/FSTDocumentTests.mm +++ b/Firestore/Example/Tests/Model/FSTDocumentTests.mm @@ -64,9 +64,9 @@ - (void)testExtractsFields { state:FSTDocumentStateSynced]; XCTAssertEqualObjects([doc fieldForPath:testutil::Field("desc")], - [FSTStringValue stringValue:@"Discuss all the project related stuff"]); + FieldValue::FromString("Discuss all the project related stuff").Wrap()); XCTAssertEqualObjects([doc fieldForPath:testutil::Field("owner.title")], - [FSTStringValue stringValue:@"scallywag"]); + FieldValue::FromString("scallywag").Wrap()); } - (void)testIsEqual { diff --git a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm index 9bbc18481d3..05a49313640 100644 --- a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm +++ b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm @@ -223,7 +223,7 @@ - (void)testWrapStrings { NSArray *values = @[ @"", @"abc" ]; for (id value in values) { FSTFieldValue *wrapped = FSTTestFieldValue(value); - XCTAssertEqualObjects([wrapped class], [FSTStringValue class]); + XCTAssertEqualObjects([wrapped class], [FSTDelegateValue class]); XCTAssertEqualObjects([wrapped value], value); XCTAssertEqual(wrapped.type, FieldValue::Type::String); } @@ -285,7 +285,7 @@ - (void)testWrapsSimpleObjects { FSTObjectValue *actual = FSTTestObjectValue(@{@"a" : @"foo", @"b" : @(1L), @"c" : @YES, @"d" : [NSNull null]}); FSTObjectValue *expected = [[FSTObjectValue alloc] initWithDictionary:@{ - @"a" : [FSTStringValue stringValue:@"foo"], + @"a" : FieldValue::FromString("foo").Wrap(), @"b" : [FSTIntegerValue integerValue:1LL], @"c" : FieldValue::True().Wrap(), @"d" : [FSTNullValue nullValue] @@ -298,8 +298,8 @@ - (void)testWrapsNestedObjects { FSTObjectValue *actual = FSTTestObjectValue(@{@"a" : @{@"b" : @{@"c" : @"foo"}, @"d" : @YES}}); FSTObjectValue *expected = [[FSTObjectValue alloc] initWithDictionary:@{ @"a" : [[FSTObjectValue alloc] initWithDictionary:@{ - @"b" : - [[FSTObjectValue alloc] initWithDictionary:@{@"c" : [FSTStringValue stringValue:@"foo"]}], + @"b" : [[FSTObjectValue alloc] + initWithDictionary:@{@"c" : FieldValue::FromString("foo").Wrap()}], @"d" : FieldValue::True().Wrap() }] }]; @@ -314,7 +314,7 @@ - (void)testExtractsFields { FSTAssertIsKindOfClass([obj valueForPath:testutil::Field("foo")], FSTObjectValue); XCTAssertEqualObjects([obj valueForPath:testutil::Field("foo.a")], FieldValue::True().Wrap()); XCTAssertEqualObjects([obj valueForPath:testutil::Field("foo.b")], - [FSTStringValue stringValue:@"string"]); + FieldValue::FromString("string").Wrap()); XCTAssertNil([obj valueForPath:testutil::Field("foo.a.b")]); XCTAssertNil([obj valueForPath:testutil::Field("bar")]); @@ -424,7 +424,7 @@ - (void)testDeletesNestedKeys { - (void)testArrays { FSTArrayValue *expected = [[FSTArrayValue alloc] - initWithValueNoCopy:@[ [FSTStringValue stringValue:@"value"], FieldValue::True().Wrap() ]]; + initWithValueNoCopy:@[ FieldValue::FromString("value").Wrap(), FieldValue::True().Wrap() ]]; FSTArrayValue *actual = (FSTArrayValue *)FSTTestFieldValue(@[ @"value", @YES ]); XCTAssertEqualObjects(actual, expected); @@ -448,7 +448,7 @@ - (void)testValueEquality { FSTTestFieldValue(FSTTestData(0, 1, 2, -1)), [FSTBlobValue blobValue:FSTTestData(0, 1, 2, -1)] ], @[ FSTTestFieldValue(FSTTestData(0, 1, -1)) ], - @[ FSTTestFieldValue(@"string"), [FSTStringValue stringValue:@"string"] ], + @[ FSTTestFieldValue(@"string"), FieldValue::FromString("string").Wrap() ], @[ FSTTestFieldValue(@"strin") ], @[ FSTTestFieldValue(@"e\u0301b") ], // latin small letter e + combining acute accent @[ FSTTestFieldValue(@"\u00e9a") ], // latin small letter e with acute accent diff --git a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm index c3215086081..b9d3af60ff3 100644 --- a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm +++ b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm @@ -476,7 +476,7 @@ - (void)testDecodesMutationResult { commitVersion:commitVersion]; XCTAssertEqual(result.version, updateVersion); - XCTAssertEqualObjects(result.transformResults, @[ [FSTStringValue stringValue:@"result"] ]); + XCTAssertEqualObjects(result.transformResults, @[ FieldValue::FromString("result").Wrap() ]); } - (void)testDecodesDeleteMutationResult { diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm index a0f5ff85afa..22ba5396b30 100644 --- a/Firestore/Source/API/FSTUserDataConverter.mm +++ b/Firestore/Source/API/FSTUserDataConverter.mm @@ -434,7 +434,7 @@ - (nullable FSTFieldValue *)parseScalarValue:(nullable id)input context:(ParseCo } } else if ([input isKindOfClass:[NSString class]]) { - return [FSTStringValue stringValue:input]; + return FieldValue::FromString(util::MakeString(input)).Wrap(); } else if ([input isKindOfClass:[NSDate class]]) { return [FSTTimestampValue timestampValue:[FIRTimestamp timestampWithDate:input]]; diff --git a/Firestore/Source/Model/FSTFieldValue.h b/Firestore/Source/Model/FSTFieldValue.h index 98bd07f156e..8eebde5b747 100644 --- a/Firestore/Source/Model/FSTFieldValue.h +++ b/Firestore/Source/Model/FSTFieldValue.h @@ -149,13 +149,6 @@ enum class ServerTimestampBehavior { None, Estimate, Previous }; - (double)internalValue; @end -/** - * A string stored in Firestore. - */ -@interface FSTStringValue : FSTFieldValue -+ (instancetype)stringValue:(NSString *)value; -@end - /** * A timestamp value stored in Firestore. */ diff --git a/Firestore/Source/Model/FSTFieldValue.mm b/Firestore/Source/Model/FSTFieldValue.mm index 75fb0995a19..8d931fef8d7 100644 --- a/Firestore/Source/Model/FSTFieldValue.mm +++ b/Firestore/Source/Model/FSTFieldValue.mm @@ -305,70 +305,6 @@ - (NSUInteger)hash { @end -#pragma mark - FSTStringValue - -/** - * Specialization of Comparator for NSStrings. - */ -template <> -struct Comparator { - bool operator()(NSString *left, NSString *right) const { - Comparator lessThan; - return lessThan(MakeString(left), MakeString(right)); - } -}; - -@interface FSTStringValue () -@property(nonatomic, copy, readonly) NSString *internalValue; -@end - -// TODO(b/37267885): Add truncation support -@implementation FSTStringValue - -+ (instancetype)stringValue:(NSString *)value { - return [[FSTStringValue alloc] initWithValue:value]; -} - -- (id)initWithValue:(NSString *)value { - self = [super init]; - if (self) { - _internalValue = [value copy]; - } - return self; -} - -- (FieldValue::Type)type { - return FieldValue::Type::String; -} - -- (FSTTypeOrder)typeOrder { - return FSTTypeOrderString; -} - -- (id)value { - return self.internalValue; -} - -- (BOOL)isEqual:(id)other { - return [other isKindOfClass:[FSTFieldValue class]] && - ((FSTFieldValue *)other).type == FieldValue::Type::String && - [self.internalValue isEqualToString:((FSTStringValue *)other).internalValue]; -} - -- (NSUInteger)hash { - return self.internalValue ? 1 : 0; -} - -- (NSComparisonResult)compare:(FSTFieldValue *)other { - if (other.type == FieldValue::Type::String) { - return WrapCompare(self.internalValue, ((FSTStringValue *)other).internalValue); - } else { - return [self defaultCompare:other]; - } -} - -@end - #pragma mark - FSTTimestampValue @interface FSTTimestampValue () @@ -694,6 +630,18 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { #pragma mark - FSTObjectValue +/** + * Specialization of Comparator for NSStrings. + */ +// TODO(b/37267885): Add truncation support +template <> +struct Comparator { + bool operator()(NSString *left, NSString *right) const { + Comparator lessThan; + return lessThan(MakeString(left), MakeString(right)); + } +}; + static const NSComparator StringComparator = ^NSComparisonResult(NSString *left, NSString *right) { return WrapCompare(left, right); }; @@ -1037,7 +985,9 @@ - (id)value { case FieldValue::Type::Double: case FieldValue::Type::Timestamp: case FieldValue::Type::ServerTimestamp: + HARD_FAIL("TODO(rsgowman): implement"); case FieldValue::Type::String: + return util::WrapNSString(self.internalValue.string_value()); case FieldValue::Type::Blob: case FieldValue::Type::Reference: case FieldValue::Type::GeoPoint: diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm index 4537c8a4775..ffa8d916037 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.mm +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -255,7 +255,7 @@ - (FSTFieldValue *)decodedFieldValue:(GCFSValue *)valueProto { return [FSTDoubleValue doubleValue:valueProto.doubleValue]; case GCFSValue_ValueType_OneOfCase_StringValue: - return [FSTStringValue stringValue:valueProto.stringValue]; + return FieldValue::FromString(util::MakeString(valueProto.stringValue)).Wrap(); case GCFSValue_ValueType_OneOfCase_TimestampValue: { Timestamp value = [self decodedTimestamp:valueProto.timestampValue]; diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc index a018f8a3911..e5064479c0e 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.cc +++ b/Firestore/core/src/firebase/firestore/model/field_value.cc @@ -360,7 +360,11 @@ size_t FieldValue::Hash() const { case FieldValue::Type::Double: case FieldValue::Type::Timestamp: case FieldValue::Type::ServerTimestamp: + HARD_FAIL("TODO(rsgowman): Implement"); + case FieldValue::Type::String: + return util::Hash(string_value()); + case FieldValue::Type::Blob: case FieldValue::Type::Reference: case FieldValue::Type::GeoPoint: From da3c019f8e5954bc727a379ce3cb1ef41f546cda Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 13:25:08 -0700 Subject: [PATCH 158/214] Remove implicit Analytics dependency from DynamicLinks (#2738) --- FirebaseDynamicLinks.podspec | 1 - 1 file changed, 1 deletion(-) diff --git a/FirebaseDynamicLinks.podspec b/FirebaseDynamicLinks.podspec index 3402a4f1d78..5427ce6ed9f 100644 --- a/FirebaseDynamicLinks.podspec +++ b/FirebaseDynamicLinks.podspec @@ -27,7 +27,6 @@ Firebase Dynamic Links are deep links that enhance user experience and increase s.frameworks = 'AssetsLibrary', 'MessageUI', 'QuartzCore' s.weak_framework = 'WebKit' s.dependency 'FirebaseCore', '~> 5.2' - s.ios.dependency 'FirebaseAnalytics', '~> 5.1' s.dependency 'FirebaseAnalyticsInterop', '~> 1.0' s.pod_target_xcconfig = { From aaddd565312e9ebf160825d7b64d2246219a7f8e Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Mon, 8 Apr 2019 13:51:32 -0700 Subject: [PATCH 159/214] Remove support for deprecated API for Firebase 6 (#2759) --- Example/Auth/Tests/FIRAuthTests.m | 75 ------------------- .../Email/FIREmailAuthProvider.m | 2 - .../Source/Auth Provider/FIRAuthProvider.m | 3 - .../Auth/Source/Utilities/FIRAuthErrorUtils.m | 14 ---- 4 files changed, 94 deletions(-) diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m index 9e6252934ec..7273b85cda5 100644 --- a/Example/Auth/Tests/FIRAuthTests.m +++ b/Example/Auth/Tests/FIRAuthTests.m @@ -451,42 +451,6 @@ - (void)testFetchSignInMethodsForEmailSuccess { OCMVerifyAll(_mockBackend); } -/** @fn testFetchProvidersForEmailSuccessDeprecatedProviderID - @brief Tests the flow of a successful @c fetchProvidersForEmail:completion: call using the - deprecated FIREmailPasswordAuthProviderID. - */ -- (void)testFetchProvidersForEmailSuccessDeprecatedProviderID { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSArray *allProviders = - @[ FIRGoogleAuthProviderID, FIREmailAuthProviderID ]; -#pragma clang diagnostic pop - OCMExpect([_mockBackend createAuthURI:[OCMArg any] - callback:[OCMArg any]]) - .andCallBlock2(^(FIRCreateAuthURIRequest *_Nullable request, - FIRCreateAuthURIResponseCallback callback) { - XCTAssertEqualObjects(request.identifier, kEmail); - XCTAssertNotNil(request.endpoint); - XCTAssertEqualObjects(request.APIKey, kAPIKey); - dispatch_async(FIRAuthGlobalWorkQueue(), ^() { - id mockCreateAuthURIResponse = OCMClassMock([FIRCreateAuthURIResponse class]); - OCMStub([mockCreateAuthURIResponse allProviders]).andReturn(allProviders); - callback(mockCreateAuthURIResponse, nil); - }); - }); - XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; - [[FIRAuth auth] fetchProvidersForEmail:kEmail - completion:^(NSArray *_Nullable providers, - NSError *_Nullable error) { - XCTAssertTrue([NSThread isMainThread]); - XCTAssertEqualObjects(providers, allProviders); - XCTAssertNil(error); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; - OCMVerifyAll(_mockBackend); -} - /** @fn testFetchProvidersForEmailFailure @brief Tests the flow of a failed @c fetchProvidersForEmail:completion: call. */ @@ -1135,45 +1099,6 @@ - (void)testSignInWithEmailCredentialSuccess { OCMVerifyAll(_mockBackend); } -/** @fn testSignInWithEmailCredentialSuccess - @brief Tests the flow of a successfully @c signInWithCredential:completion: call with an - email-password credential using the deprecated FIREmailPasswordAuthProvider. - */ -- (void)testSignInWithEmailCredentialSuccessWithDepricatedProvider { - OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]]) - .andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request, - FIRVerifyPasswordResponseCallback callback) { - XCTAssertEqualObjects(request.APIKey, kAPIKey); - XCTAssertEqualObjects(request.email, kEmail); - XCTAssertEqualObjects(request.password, kFakePassword); - XCTAssertTrue(request.returnSecureToken); - dispatch_async(FIRAuthGlobalWorkQueue(), ^() { - id mockVeriyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]); - [self stubTokensWithMockResponse:mockVeriyPasswordResponse]; - callback(mockVeriyPasswordResponse, nil); - }); - }); - [self expectGetAccountInfo]; - XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; - [[FIRAuth auth] signOut:NULL]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - FIRAuthCredential *emailCredential = - [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword]; -#pragma clang diagnostic pop - [[FIRAuth auth] signInWithCredential:emailCredential - completion:^(FIRAuthDataResult * _Nullable result, - NSError *_Nullable error) { - XCTAssertTrue([NSThread isMainThread]); - [self assertUser:result.user]; - XCTAssertNil(error); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; - [self assertUser:[FIRAuth auth].currentUser]; - OCMVerifyAll(_mockBackend); -} - /** @fn testSignInWithEmailCredentialFailure @brief Tests the flow of a failed @c signInWithCredential:completion: call with an email-password credential. diff --git a/Firebase/Auth/Source/Auth Provider/Email/FIREmailAuthProvider.m b/Firebase/Auth/Source/Auth Provider/Email/FIREmailAuthProvider.m index ff7e0042ba0..373c0b141c1 100644 --- a/Firebase/Auth/Source/Auth Provider/Email/FIREmailAuthProvider.m +++ b/Firebase/Auth/Source/Auth Provider/Email/FIREmailAuthProvider.m @@ -18,8 +18,6 @@ #import "FIREmailPasswordAuthCredential.h" -// FIREmailPasswordAuthProviderID is defined in FIRAuthProvider.m. - NS_ASSUME_NONNULL_BEGIN @implementation FIREmailAuthProvider diff --git a/Firebase/Auth/Source/Auth Provider/FIRAuthProvider.m b/Firebase/Auth/Source/Auth Provider/FIRAuthProvider.m index e382d4e751a..33618505ae2 100644 --- a/Firebase/Auth/Source/Auth Provider/FIRAuthProvider.m +++ b/Firebase/Auth/Source/Auth Provider/FIRAuthProvider.m @@ -27,9 +27,6 @@ // Declared 'extern' in FIREmailAuthProvider.h NSString *const FIREmailAuthProviderID = @"password"; -// Declared 'extern' in FIREmailAuthProvider.h -NSString *const FIREmailPasswordAuthProviderID = @"password"; - // Declared 'extern' in FIRTwitterAuthProvider.h NSString *const FIRTwitterAuthProviderID = @"twitter.com"; diff --git a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m index 5355a9f0749..b6988b799d7 100644 --- a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m +++ b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m @@ -37,10 +37,6 @@ NSString *const FIRAuthErrorUserInfoNameKey = @"FIRAuthErrorUserInfoNameKey"; -NSString *const FIRAuthErrorNameKey = @"error_name"; - -NSString *const FIRAuthUpdatedCredentialKey = @"FIRAuthUpdatedCredentialKey"; - /** @var kServerErrorDetailMarker @brief This marker indicates that the server error message contains a detail error message which should be used instead of the hardcoded client error message. @@ -753,11 +749,6 @@ + (NSError *)errorWithCode:(FIRAuthInternalErrorCode)code if (!errorUserInfo[NSLocalizedDescriptionKey]) { errorUserInfo[NSLocalizedDescriptionKey] = FIRAuthErrorDescription(errorCode); } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // TODO(wangyue): Remove the deprecated code on next breaking change. - errorUserInfo[FIRAuthErrorNameKey] = FIRAuthErrorCodeString(errorCode); -#pragma clang diagnostic pop errorUserInfo[FIRAuthErrorUserInfoNameKey] = FIRAuthErrorCodeString(errorCode); return [NSError errorWithDomain:FIRAuthErrorDomain code:errorCode userInfo:errorUserInfo]; } else { @@ -957,11 +948,6 @@ + (NSError *)credentialAlreadyInUseErrorWithMessage:(nullable NSString *)message email:(nullable NSString *)email { NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; if (credential) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // TODO(wangyue): Remove the deprecated code on next breaking change. - userInfo[FIRAuthUpdatedCredentialKey] = credential; -#pragma clang diagnostic pop userInfo[FIRAuthErrorUserInfoUpdatedCredentialKey] = credential; } if (email.length) { From beef575d4c95c9e5fb02425de0ee4bbf779869b7 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Mon, 8 Apr 2019 17:07:19 -0400 Subject: [PATCH 160/214] Changelog updates for Messaging, Auth and InstanceID (#2758) --- Firebase/Auth/CHANGELOG.md | 3 +++ Firebase/InstanceID/CHANGELOG.md | 3 +++ Firebase/Messaging/CHANGELOG.md | 3 +++ 3 files changed, 9 insertions(+) diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md index a4e3c9612ce..1a549506ae4 100644 --- a/Firebase/Auth/CHANGELOG.md +++ b/Firebase/Auth/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- `GULAppDelegateSwizzler` is used for the app delegate swizzling (#2591) + # v5.4.2 - Support new error code ERROR_INVALID_PROVIDER_ID. (#2629) diff --git a/Firebase/InstanceID/CHANGELOG.md b/Firebase/InstanceID/CHANGELOG.md index ce69a2cb9f8..6b685626588 100644 --- a/Firebase/InstanceID/CHANGELOG.md +++ b/Firebase/InstanceID/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- Send `firebaseUserAgent` with a register request (#2679) + # 2019-04-02 -- v3.8.1 - Fixed handling the multiple calls of instanceIDWithHandler. (#2445) - Fixed a race condition where token kept getting refreshed at app start. (#2438) diff --git a/Firebase/Messaging/CHANGELOG.md b/Firebase/Messaging/CHANGELOG.md index 8e6c901cac0..c3d93375c00 100644 --- a/Firebase/Messaging/CHANGELOG.md +++ b/Firebase/Messaging/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- `GULAppDelegateSwizzler` is used for the app delegate swizzling (#2683) + # 2019-04-02 -- v3.5.0 - Add image support for notification. (#2644) From 67e7941deadfde71caf5643d4b69832bb17c1e91 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Mon, 8 Apr 2019 17:24:33 -0400 Subject: [PATCH 161/214] `pod lib lint GoogleUtilities.podspec` warnings fixes (#2754) --- .../GULAppDelegateSwizzler.m | 76 ++++++++++++------- .../third_party/GULAppEnvironmentUtil.h | 3 + .../third_party/GULAppEnvironmentUtil.m | 12 +++ 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m index 368e7b71dbc..cf05a16cf16 100644 --- a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m +++ b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m @@ -56,8 +56,14 @@ typedef void (*GULRealDidFailToRegisterForRemoteNotificationsIMP)(id, typedef void (*GULRealDidReceiveRemoteNotificationIMP)(id, SEL, UIApplication *, NSDictionary *); +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +// This is needed to for the library to be warning free on iOS versions < 7. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" typedef void (*GULRealDidReceiveRemoteNotificationWithCompletionIMP)( id, SEL, UIApplication *, NSDictionary *, void (^)(UIBackgroundFetchResult)); +#pragma clang diagnostic pop +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 typedef void (^GULAppDelegateInterceptorCallback)(id); @@ -72,8 +78,12 @@ typedef void (*GULRealDidReceiveRemoteNotificationWithCompletionIMP)( "GUL_didFailToRegisterForRemoteNotificationsIMP"; static char const *const kGULRealDidReceiveRemoteNotificationIMPKey = "GUL_didReceiveRemoteNotificationIMP"; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 static char const *const kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey = "GUL_didReceiveRemoteNotificationWithCompletionIMP"; +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + #if TARGET_OS_IOS // The method application:openURL:sourceApplication:annotation: is not available on tvOS static char const *const kGULOpenURLOptionsSourceAnnotationsIMPKey = @@ -409,30 +419,6 @@ + (void)createSubclassWithObject:(id)anObject { NSValue *didReceiveRemoteNotificationIMPPointer = [NSValue valueWithPointer:didReceiveRemoteNotificationIMP]; - // For application:didReceiveRemoteNotification:fetchCompletionHandler: - NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer; - SEL didReceiveRemoteNotificationWithCompletionSEL = - @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); - if ([anObject respondsToSelector:didReceiveRemoteNotificationWithCompletionSEL]) { - // Only add the application:didReceiveRemoteNotification:fetchCompletionHandler: method if - // the original AppDelegate implements it. - // This fixes a bug if an app only implements application:didReceiveRemoteNotification: - // (if we add the method with completion, iOS sees that one exists and does not call - // the method without the completion, which in this case is the only one the app implements). - - [GULAppDelegateSwizzler - addInstanceMethodWithSelector:didReceiveRemoteNotificationWithCompletionSEL - fromClass:[GULAppDelegateSwizzler class] - toClass:appDelegateSubClass]; - GULRealDidReceiveRemoteNotificationWithCompletionIMP - didReceiveRemoteNotificationWithCompletionIMP = - (GULRealDidReceiveRemoteNotificationWithCompletionIMP)[GULAppDelegateSwizzler - implementationOfMethodSelector:didReceiveRemoteNotificationWithCompletionSEL - fromClass:realClass]; - didReceiveRemoteNotificationWithCompletionIMPPointer = - [NSValue valueWithPointer:didReceiveRemoteNotificationWithCompletionIMP]; - } - #if TARGET_OS_IOS // For application:openURL:sourceApplication:annotation: SEL openURLSourceApplicationAnnotationSEL = @selector(application: @@ -473,9 +459,7 @@ + (void)createSubclassWithObject:(id)anObject { objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationIMPKey, didReceiveRemoteNotificationIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey, - didReceiveRemoteNotificationWithCompletionIMPPointer, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); + #if TARGET_OS_IOS objc_setAssociatedObject(anObject, &kGULOpenURLOptionsSourceAnnotationsIMPKey, openURLSourceAppAnnotationIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); @@ -484,6 +468,38 @@ + (void)createSubclassWithObject:(id)anObject { objc_setAssociatedObject(anObject, &kGULRealClassKey, realClass, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + // For application:didReceiveRemoteNotification:fetchCompletionHandler: +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + if ([GULAppEnvironmentUtil isIOS7OrHigher]) { + NSValue *didReceiveRemoteNotificationWithCompletionIMPPointer; + SEL didReceiveRemoteNotificationWithCompletionSEL = + @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); + if ([anObject respondsToSelector:didReceiveRemoteNotificationWithCompletionSEL]) { + // Only add the application:didReceiveRemoteNotification:fetchCompletionHandler: method if + // the original AppDelegate implements it. + // This fixes a bug if an app only implements application:didReceiveRemoteNotification: + // (if we add the method with completion, iOS sees that one exists and does not call + // the method without the completion, which in this case is the only one the app implements). + + [GULAppDelegateSwizzler + addInstanceMethodWithSelector:didReceiveRemoteNotificationWithCompletionSEL + fromClass:[GULAppDelegateSwizzler class] + toClass:appDelegateSubClass]; + GULRealDidReceiveRemoteNotificationWithCompletionIMP + didReceiveRemoteNotificationWithCompletionIMP = + (GULRealDidReceiveRemoteNotificationWithCompletionIMP)[GULAppDelegateSwizzler + implementationOfMethodSelector:didReceiveRemoteNotificationWithCompletionSEL + fromClass:realClass]; + didReceiveRemoteNotificationWithCompletionIMPPointer = + [NSValue valueWithPointer:didReceiveRemoteNotificationWithCompletionIMP]; + } + + objc_setAssociatedObject(anObject, &kGULRealDidReceiveRemoteNotificationWithCompletionIMPKey, + didReceiveRemoteNotificationWithCompletionIMPPointer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + // The subclass size has to be exactly the same size with the original class size. The subclass // cannot have more ivars/properties than its superclass since it will cause an offset in memory // that can lead to overwriting the isa of an object in the next frame. @@ -801,6 +817,10 @@ - (void)application:(UIApplication *)application } } +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +// This is needed to for the library to be warning free on iOS versions < 7. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { @@ -825,6 +845,8 @@ - (void)application:(UIApplication *)application completionHandler); } } +#pragma clang diagnostic pop +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { diff --git a/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h b/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h index 5b562719cb4..d5502647c20 100644 --- a/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h +++ b/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h @@ -40,4 +40,7 @@ /// Indicates whether it is running inside an extension or an app. + (BOOL)isAppExtension; +/// @return Returns @YES when is run on iOS version greater or equal to 7.0 ++ (BOOL)isIOS7OrHigher; + @end diff --git a/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m b/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m index 1fa767b1856..8327438ad8d 100644 --- a/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m +++ b/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m @@ -247,4 +247,16 @@ + (BOOL)isAppExtension { #endif } ++ (BOOL)isIOS7OrHigher { +#if __has_builtin(__builtin_available) + if (@available(iOS 7.0, *)) { +#else + if ([[UIDevice currentDevice].systemVersion integerValue] >= 7) { +#endif + return YES; + } + + return NO; +} + @end From 4ae6843156b1441e06ed9c81c60c745fcaf1c51a Mon Sep 17 00:00:00 2001 From: Yue-Wang-Google Date: Mon, 8 Apr 2019 14:47:35 -0700 Subject: [PATCH 162/214] Revert Twitter Auth deprecation because devepler may want to build their own sign in flow (#2765) --- .../Auth Provider/Twitter/FIRTwitterAuthCredential.h | 1 - .../Auth Provider/Twitter/FIRTwitterAuthCredential.m | 5 ----- .../Auth Provider/Twitter/FIRTwitterAuthProvider.m | 5 ----- Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h | 9 ++------- 4 files changed, 2 insertions(+), 18 deletions(-) diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h index a8fdfbe8e8e..423d595a4e5 100644 --- a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h +++ b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h @@ -23,7 +23,6 @@ NS_ASSUME_NONNULL_BEGIN /** @class FIRTwitterAuthCredential @brief Internal implementation of FIRAuthCredential for Twitter credentials. */ -DEPRECATED_MSG_ATTRIBUTE("Please use FIROAuthCredential instead of FIRTwitterAuthCredential.") @interface FIRTwitterAuthCredential : FIRAuthCredential /** @property token diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m index 6d7ce66be05..cb466155850 100644 --- a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m +++ b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m @@ -22,9 +22,6 @@ NS_ASSUME_NONNULL_BEGIN -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" - @interface FIRTwitterAuthCredential () - (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; @@ -73,6 +70,4 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { @end -#pragma clang diagnostic pop - NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m index 004242b8ea2..33771b723ca 100644 --- a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m +++ b/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m @@ -23,9 +23,6 @@ NS_ASSUME_NONNULL_BEGIN -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" - @implementation FIRTwitterAuthProvider - (instancetype)init { @@ -40,6 +37,4 @@ + (FIRAuthCredential *)credentialWithToken:(NSString *)token secret:(NSString *) @end -#pragma clang diagnostic pop - NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h b/Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h index a5bdc0a08da..0f1b28d7377 100644 --- a/Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIRTwitterAuthProvider.h @@ -23,20 +23,15 @@ NS_ASSUME_NONNULL_BEGIN /** @brief A string constant identifying the Twitter identity provider. */ -extern NSString *const FIRTwitterAuthProviderID NS_SWIFT_NAME(TwitterAuthProviderID) - DEPRECATED_MSG_ATTRIBUTE("Please use \"twitter.com\" instead."); - +extern NSString *const FIRTwitterAuthProviderID NS_SWIFT_NAME(TwitterAuthProviderID); /** @brief A string constant identifying the Twitter sign-in method. */ -extern NSString *const _Nonnull FIRTwitterAuthSignInMethod NS_SWIFT_NAME(TwitterAuthSignInMethod) - DEPRECATED_MSG_ATTRIBUTE("Please use \"twitter.com\" instead."); - +extern NSString *const _Nonnull FIRTwitterAuthSignInMethod NS_SWIFT_NAME(TwitterAuthSignInMethod); /** @class FIRTwitterAuthProvider @brief Utility class for constructing Twitter credentials. */ -DEPRECATED_MSG_ATTRIBUTE("Please use FIROAuthProvider instead of FIRTwitterAuthProvider.") NS_SWIFT_NAME(TwitterAuthProvider) @interface FIRTwitterAuthProvider : NSObject From 9da9f866d8ce0560cb6cbb128ecef3e5c3e4bfc3 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 14:56:46 -0700 Subject: [PATCH 163/214] Add license files for Google Utilities 3rd party code (#2767) --- .../Environment/third_party/LICENSE | 36 +++++++++++++++++++ .../Example/Tests/Network/third_party/LICENSE | 30 ++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 GoogleUtilities/Environment/third_party/LICENSE create mode 100644 GoogleUtilities/Example/Tests/Network/third_party/LICENSE diff --git a/GoogleUtilities/Environment/third_party/LICENSE b/GoogleUtilities/Environment/third_party/LICENSE new file mode 100644 index 00000000000..78dd6c9770b --- /dev/null +++ b/GoogleUtilities/Environment/third_party/LICENSE @@ -0,0 +1,36 @@ +The following copyright from Landon J. Fuller applies to the isAppEncrypted function. + +Copyright (c) 2017 Landon J. Fuller +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Comment from iPhone Dev Wiki +Crack Prevention: +App Store binaries are signed by both their developer and Apple. This encrypts the binary so +that decryption keys are needed in order to make the binary readable. When iOS executes the +binary, the decryption keys are used to decrypt the binary into a readable state where it is +then loaded into memory and executed. iOS can tell the encryption status of a binary via the +cryptid structure member of LC_ENCRYPTION_INFO MachO load command. If cryptid is a non-zero +value then the binary is encrypted. + +'Cracking' works by letting the kernel decrypt the binary then siphoning the decrypted data into +a new binary file, resigning, and repackaging. This will only work on jailbroken devices as +codesignature validation has been removed. Resigning takes place because while the codesignature +doesn't have to be valid thanks to the jailbreak, it does have to be in place unless you have +AppSync or similar to disable codesignature checks. + +More information at Landon Fuller's blog diff --git a/GoogleUtilities/Example/Tests/Network/third_party/LICENSE b/GoogleUtilities/Example/Tests/Network/third_party/LICENSE new file mode 100644 index 00000000000..bbc55fc8c82 --- /dev/null +++ b/GoogleUtilities/Example/Tests/Network/third_party/LICENSE @@ -0,0 +1,30 @@ +Based a little on HTTPServer, part of the CocoaHTTPServer sample code found at +https://opensource.apple.com/source/HTTPServer/HTTPServer-11/CocoaHTTPServer/ +License for the CocoaHTTPServer sample code: + +Software License Agreement (BSD License) + +Copyright (c) 2011, Deusty, LLC +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Neither the name of Deusty nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of Deusty, LLC. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. From cd8eff522b0c70ee6b003d08ef30ac7bfab49235 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Mon, 8 Apr 2019 19:09:14 -0400 Subject: [PATCH 164/214] Use `if: type = cron` instead of `if_cron.sh` (#2764) --- .travis.yml | 42 +++++++++++++++++++++++------------------- scripts/if_cron.sh | 25 ------------------------- 2 files changed, 23 insertions(+), 44 deletions(-) delete mode 100755 scripts/if_cron.sh diff --git a/.travis.yml b/.travis.yml index 366acb2491d..e42689c23af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -87,43 +87,46 @@ jobs: # pod lib lint to check build and warnings for static library build - only on cron jobs - stage: test + if: type = cron env: - PROJECT=Firebase PLATFORM=iOS METHOD=pod-lib-lint before_install: - - ./scripts/if_cron.sh ./scripts/install_prereqs.sh + - ./scripts/install_prereqs.sh script: - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh GoogleUtilities.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseCore.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseAnalyticsInterop.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseAuth.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseAuthInterop.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseDatabase.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseDynamicLinks.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseInstanceID.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh GoogleUtilities.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseCore.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseAnalyticsInterop.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseAuth.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseAuthInterop.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseDatabase.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseDynamicLinks.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseInstanceID.podspec --use-libraries # The Protobuf dependency of FirebaseMessaging has warnings with --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseMessaging.podspec --use-libraries --allow-warnings - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseStorage.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseInAppMessaging.podspec --use-libraries - - travis_retry ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseInAppMessagingDisplay.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseMessaging.podspec --use-libraries --allow-warnings + - travis_retry ./scripts/pod_lib_lint.sh FirebaseStorage.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseInAppMessaging.podspec --use-libraries + - travis_retry ./scripts/pod_lib_lint.sh FirebaseInAppMessagingDisplay.podspec --use-libraries - stage: test + if: type = cron env: - PROJECT=Firestore PLATFORM=iOS METHOD=pod-lib-lint before_install: - - ./scripts/if_cron.sh ./scripts/install_prereqs.sh + - ./scripts/install_prereqs.sh script: # TBD - non-portable path warnings # The travis_wait is necessary because the command takes more than 10 minutes. - - travis_wait 45 ./scripts/if_cron.sh ./scripts/pod_lib_lint.sh FirebaseFirestore.podspec --use-libraries --allow-warnings --no-subspecs + - travis_wait 45 ./scripts/pod_lib_lint.sh FirebaseFirestore.podspec --use-libraries --allow-warnings --no-subspecs # Daily test for symbol collisions between Firebase and CocoaPods. - stage: test + if: type = cron env: - PROJECT=SymbolCollision PLATFORM=iOS METHOD=xcodebuild before_install: - - ./scripts/if_cron.sh ./scripts/install_prereqs.sh + - ./scripts/install_prereqs.sh script: - - travis_retry ./scripts/if_cron.sh ./scripts/build.sh $PROJECT $PLATFORM $METHOD + - travis_retry ./scripts/build.sh $PROJECT $PLATFORM $METHOD # Alternative platforms @@ -212,13 +215,14 @@ jobs: allow_failures: # Run fuzz tests only on cron jobs. - stage: test + if: type = cron env: - PROJECT=Firestore PLATFORM=iOS METHOD=fuzz before_install: - - ./scripts/if_cron.sh ./scripts/install_prereqs.sh + - ./scripts/install_prereqs.sh script: # The travis_wait is necessary because fuzzing runs for 40 minutes. - - travis_wait 45 ./scripts/if_cron.sh ./scripts/fuzzing_ci.sh + - travis_wait 45 ./scripts/fuzzing_ci.sh # TODO(varconst): UBSan for CMake. UBSan failures are non-fatal by default, # need to make them fatal for the purposes of the test run. diff --git a/scripts/if_cron.sh b/scripts/if_cron.sh deleted file mode 100755 index c13a374a238..00000000000 --- a/scripts/if_cron.sh +++ /dev/null @@ -1,25 +0,0 @@ -# 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. - -# Within Travis, check if running in a cron job. -# -# Examines the following Travis-supplied environment variables: -# - TRAVIS_EVENT_TYPE - to check if this is a cron job -# - -if [[ "$TRAVIS_EVENT_TYPE" == "cron" ]]; then - "$@" -else - echo "skipped $*" -fi From 0212c46f7c291c239ac4ea5131e8399d63738715 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Mon, 8 Apr 2019 16:19:21 -0700 Subject: [PATCH 165/214] remove deprecated token API (#2741) --- Example/InstanceID/Tests/FIRInstanceIDTest.m | 11 +------ Firebase/InstanceID/FIRInstanceID+Private.h | 3 +- Firebase/InstanceID/FIRInstanceID.m | 9 ------ .../Private/FIRInstanceID_Private.h | 32 +++++++++++++++++++ Firebase/InstanceID/Public/FIRInstanceID.h | 8 ----- Firebase/Messaging/FIRMessaging.m | 10 +----- FirebaseInstanceID.podspec | 1 + 7 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 Firebase/InstanceID/Private/FIRInstanceID_Private.h diff --git a/Example/InstanceID/Tests/FIRInstanceIDTest.m b/Example/InstanceID/Tests/FIRInstanceIDTest.m index 2bc67cf659d..498c0926e13 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDTest.m @@ -18,6 +18,7 @@ #import #import +#import #import #import "Firebase/InstanceID/FIRInstanceID+Testing.h" @@ -535,7 +536,6 @@ - (void)testDefaultToken_validCachedToken { [[[self.mockTokenManager stub] andReturn:sTokenInfo] cachedTokenInfoWithAuthorizedEntity:kAuthorizedEntity scope:@"*"]; - [[self.mockInstanceID reject] defaultTokenWithHandler:nil]; NSString *token = [self.mockInstanceID token]; XCTAssertEqualObjects(token, kToken); @@ -590,10 +590,7 @@ - (void)testDefaultTokenFetch_returnValidToken { cachedTokenInfo = sTokenInfo; notificationPostCount++; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" notificationToken = [[self.instanceID token] copy]; -#pragma clang diagnostic pop [defaultTokenExpectation fulfill]; }]; @@ -670,10 +667,7 @@ - (void)testDefaultTokenFetch_retryFetchToken { cachedTokenInfo = sTokenInfo; notificationPostCount++; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" notificationToken = [[self.instanceID token] copy]; -#pragma clang diagnostic pop [defaultTokenExpectation fulfill]; }]; @@ -741,10 +735,7 @@ - (void)testDefaultToken_multipleInvocations { cachedTokenInfo = sTokenInfo; notificationPostCount++; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" notificationToken = [[self.instanceID token] copy]; -#pragma clang diagnostic pop [defaultTokenExpectation fulfill]; }]; diff --git a/Firebase/InstanceID/FIRInstanceID+Private.h b/Firebase/InstanceID/FIRInstanceID+Private.h index 524441778e2..3e3ec175f9c 100644 --- a/Firebase/InstanceID/FIRInstanceID+Private.h +++ b/Firebase/InstanceID/FIRInstanceID+Private.h @@ -19,8 +19,9 @@ #import "FIRInstanceIDCheckinService.h" /** - * Internal API used by other Firebase SDK teams, including Messaging, Analytics and Remote config. + * Internal API used by Firebase SDK teams by calling in reflection or internal teams. */ +// TODO(chliangGoogle) Rename this to Internal. @interface FIRInstanceID (Private) /** diff --git a/Firebase/InstanceID/FIRInstanceID.m b/Firebase/InstanceID/FIRInstanceID.m index 96c0574bd15..e5b39e344db 100644 --- a/Firebase/InstanceID/FIRInstanceID.m +++ b/Firebase/InstanceID/FIRInstanceID.m @@ -489,10 +489,7 @@ - (void)getIDWithHandler:(FIRInstanceIDHandler)handler { // When getID is explicitly called, trigger getToken to make sure token always exists. // This is to avoid ID conflict (ID is not checked for conflict until we generate a token) if (appIdentity) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self token]; -#pragma clang diagnostic pop } callHandlerOnMainThread(appIdentity, error); }); @@ -706,20 +703,14 @@ - (void)didCompleteConfigure { [self defaultTokenWithHandler:nil]; } // Notify FCM with the default token. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.defaultFCMToken = [self token]; -#pragma clang diagnostic pop } else if ([self isFCMAutoInitEnabled]) { // When there is no cached token, must check auto init is enabled. // If it's disabled, don't initiate token generation/refresh. // If no cache token and auto init is enabled, fetch a token from server. [self defaultTokenWithHandler:nil]; // Notify FCM with the default token. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.defaultFCMToken = [self token]; -#pragma clang diagnostic pop } // ONLY checkin when auto data collection is turned on. if ([self isFCMAutoInitEnabled]) { diff --git a/Firebase/InstanceID/Private/FIRInstanceID_Private.h b/Firebase/InstanceID/Private/FIRInstanceID_Private.h new file mode 100644 index 00000000000..683dd4ed8c7 --- /dev/null +++ b/Firebase/InstanceID/Private/FIRInstanceID_Private.h @@ -0,0 +1,32 @@ +/* + * Copyright 2019 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 + +/** + * Private API used by other Firebase SDKs. + */ +@interface FIRInstanceID () + +/** + * Returns a Firebase Messaging scoped token for the firebase app. + * + * @return Returns the stored token if the device has registered with Firebase Messaging, otherwise + * returns nil. + */ +- (nullable NSString *)token; + +@end diff --git a/Firebase/InstanceID/Public/FIRInstanceID.h b/Firebase/InstanceID/Public/FIRInstanceID.h index d95995acfa3..c187d5593cf 100644 --- a/Firebase/InstanceID/Public/FIRInstanceID.h +++ b/Firebase/InstanceID/Public/FIRInstanceID.h @@ -208,14 +208,6 @@ NS_SWIFT_NAME(InstanceID) */ - (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler; -/** - * Returns a Firebase Messaging scoped token for the firebase app. - * - * @return Returns the stored token if the device has registered with Firebase Messaging, otherwise - * returns nil. - */ -- (nullable NSString *)token __deprecated_msg("Use instanceIDWithHandler: instead."); - /** * Returns a token that authorizes an Entity (example: cloud service) to perform * an action on behalf of the application identified by Instance ID. diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 3fa64c81e82..7e6653b1551 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -47,6 +47,7 @@ #import #import #import +#import #import #import @@ -556,10 +557,7 @@ - (void)setAutoInitEnabled:(BOOL)autoInitEnabled { forKey:kFIRMessagingUserDefaultsKeyAutoInitEnabled]; [_messagingUserDefaults synchronize]; if (!isFCMAutoInitEnabled && autoInitEnabled) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.defaultFcmToken = self.instanceID.token; -#pragma clang diagnostic pop } } @@ -567,10 +565,7 @@ - (NSString *)FCMToken { NSString *token = self.defaultFcmToken; if (!token) { // We may not have received it from Instance ID yet (via NSNotification), so extract it directly -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" token = self.instanceID.token; -#pragma clang diagnostic pop } return token; } @@ -918,10 +913,7 @@ - (void)didReceiveDefaultInstanceIDToken:(NSNotification *)notification { - (void)defaultInstanceIDTokenWasRefreshed:(NSNotification *)notification { // Retrieve the Instance ID default token, and if it is non-nil, post it -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSString *token = self.instanceID.token; -#pragma clang diagnostic pop // Sometimes Instance ID doesn't yet have a token, so wait until the default // token is fetched, and then notify. This ensures that this token should not // be nil when the developer accesses it. diff --git a/FirebaseInstanceID.podspec b/FirebaseInstanceID.podspec index d2d8eeebba7..fbebddc471f 100644 --- a/FirebaseInstanceID.podspec +++ b/FirebaseInstanceID.podspec @@ -29,6 +29,7 @@ services. s.source_files = base_dir + '**/*.[mh]' s.requires_arc = base_dir + '*.m' s.public_header_files = base_dir + 'Public/*.h' + s.private_header_files = base_dir + 'Private/*.h' s.pod_target_xcconfig = { 'GCC_C_LANGUAGE_STANDARD' => 'c99', 'GCC_PREPROCESSOR_DEFINITIONS' => From 417cfd7cd7d9b4749da556bdd7b5b442358bc0d1 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 16:33:01 -0700 Subject: [PATCH 166/214] Bump FirebaseCore to 6.0.0 (#2755) * Bump FirebaseCore to 6.0.0 * FirebaseInstanceID to 4.0.0 * FirebaseInAppMessaging is a dep too --- Example/Podfile | 2 +- FirebaseAuth.podspec | 2 +- FirebaseCore.podspec | 2 +- FirebaseDatabase.podspec | 2 +- FirebaseDynamicLinks.podspec | 2 +- FirebaseFirestore.podspec | 2 +- FirebaseFunctions.podspec | 2 +- FirebaseInAppMessaging.podspec | 8 ++++---- FirebaseInAppMessagingDisplay.podspec | 4 ++-- FirebaseInstanceID.podspec | 4 ++-- FirebaseMessaging.podspec | 4 ++-- FirebaseStorage.podspec | 2 +- Firestore/Example/Podfile | 2 +- 13 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Example/Podfile b/Example/Podfile index 93886d48f07..450af7ff50e 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -19,7 +19,7 @@ target 'Core_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.20.991' + pod 'Firebase/CoreOnly', '6.0.0-pre' target 'Core_Tests_iOS' do inherit! :search_paths diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index d73470ccf4c..f079b7f5da8 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -62,7 +62,7 @@ supports email and password accounts, as well as several 3rd party authenticatio s.framework = 'Security' s.ios.framework = 'SafariServices' s.dependency 'FirebaseAuthInterop', '~> 1.0' - s.dependency 'FirebaseCore', '~> 5.2' + s.dependency 'FirebaseCore', '~> 6.0' s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.6' s.dependency 'GoogleUtilities/Environment', '~> 5.6' s.dependency 'GTMSessionFetcher/Core', '~> 1.1' diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index c3f7f368b82..65364f6fa03 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCore' - s.version = '5.4.91' + s.version = '6.0.0' s.summary = 'Firebase Core for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC diff --git a/FirebaseDatabase.podspec b/FirebaseDatabase.podspec index e9ddac19386..5804c2637a2 100644 --- a/FirebaseDatabase.podspec +++ b/FirebaseDatabase.podspec @@ -33,7 +33,7 @@ Simplify your iOS development, grow your user base, and monetize more effectivel s.frameworks = 'CFNetwork', 'Security', 'SystemConfiguration' s.dependency 'leveldb-library', '~> 1.18' s.dependency 'FirebaseAuthInterop', '~> 1.0' - s.dependency 'FirebaseCore', '~> 5.2' + s.dependency 'FirebaseCore', '~> 6.0' s.pod_target_xcconfig = { 'GCC_C_LANGUAGE_STANDARD' => 'c99', 'GCC_PREPROCESSOR_DEFINITIONS' => diff --git a/FirebaseDynamicLinks.podspec b/FirebaseDynamicLinks.podspec index 5427ce6ed9f..e9ab3b58953 100644 --- a/FirebaseDynamicLinks.podspec +++ b/FirebaseDynamicLinks.podspec @@ -26,7 +26,7 @@ Firebase Dynamic Links are deep links that enhance user experience and increase s.public_header_files = 'Firebase/DynamicLinks/Public/*.h' s.frameworks = 'AssetsLibrary', 'MessageUI', 'QuartzCore' s.weak_framework = 'WebKit' - s.dependency 'FirebaseCore', '~> 5.2' + s.dependency 'FirebaseCore', '~> 6.0' s.dependency 'FirebaseAnalyticsInterop', '~> 1.0' s.pod_target_xcconfig = { diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 97477b46e4d..522918f1971 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -52,7 +52,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.public_header_files = 'Firestore/Source/Public/*.h' s.dependency 'FirebaseAuthInterop', '~> 1.0' - s.dependency 'FirebaseCore', '~> 5.2' + s.dependency 'FirebaseCore', '~> 6.0' s.dependency 'gRPC-C++', '0.0.8' s.dependency 'leveldb-library', '~> 1.20' s.dependency 'Protobuf', '~> 3.1' diff --git a/FirebaseFunctions.podspec b/FirebaseFunctions.podspec index a708b44b5b0..50cdf7f0852 100644 --- a/FirebaseFunctions.podspec +++ b/FirebaseFunctions.podspec @@ -27,7 +27,7 @@ iOS SDK for Cloud Functions for Firebase. s.public_header_files = 'Functions/FirebaseFunctions/Public/*.h' s.dependency 'FirebaseAuthInterop', '~> 1.0' - s.dependency 'FirebaseCore', '~> 5.2' + s.dependency 'FirebaseCore', '~> 6.0' s.dependency 'GTMSessionFetcher/Core', '~> 1.1' s.pod_target_xcconfig = { diff --git a/FirebaseInAppMessaging.podspec b/FirebaseInAppMessaging.podspec index e5073920517..4454018e6e0 100644 --- a/FirebaseInAppMessaging.podspec +++ b/FirebaseInAppMessaging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInAppMessaging' - s.version = '0.13.0' + s.version = '0.14.0' s.summary = 'Firebase In-App Messaging for iOS' s.description = <<-DESC @@ -32,7 +32,7 @@ See more product details at https://firebase.google.com/products/in-app-messagin 'FIRInAppMessaging_LIB_VERSION=' + String(s.version) } - s.dependency 'FirebaseCore' - s.ios.dependency 'FirebaseAnalyticsInterop' - s.dependency 'FirebaseInstanceID' + s.dependency 'FirebaseCore', '~> 6.0' + s.ios.dependency 'FirebaseAnalyticsInterop', '~> 1.2' + s.dependency 'FirebaseInstanceID', '~> 4.0' end diff --git a/FirebaseInAppMessagingDisplay.podspec b/FirebaseInAppMessagingDisplay.podspec index 50f5eb76725..9e19525d964 100644 --- a/FirebaseInAppMessagingDisplay.podspec +++ b/FirebaseInAppMessagingDisplay.podspec @@ -40,6 +40,6 @@ Firebase In-App Messaging SDK. 'FIRInAppMessagingDisplay_LIB_VERSION=' + String(s.version) } - s.dependency 'FirebaseCore' - s.dependency 'FirebaseInAppMessaging', '>=0.12.0' + s.dependency 'FirebaseCore', '~> 6.0' + s.dependency 'FirebaseInAppMessaging', '>=0.14.0' end diff --git a/FirebaseInstanceID.podspec b/FirebaseInstanceID.podspec index fbebddc471f..0ca733b3b5f 100644 --- a/FirebaseInstanceID.podspec +++ b/FirebaseInstanceID.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInstanceID' - s.version = '3.8.1' + s.version = '4.0.0' s.summary = 'Firebase InstanceID for iOS' s.description = <<-DESC @@ -36,7 +36,7 @@ services. 'FIRInstanceID_LIB_VERSION=' + String(s.version) } s.framework = 'Security' - s.dependency 'FirebaseCore', '~> 5.2' + s.dependency 'FirebaseCore', '~> 6.0' s.dependency 'GoogleUtilities/UserDefaults', '~> 5.2' s.dependency 'GoogleUtilities/Environment', '~> 5.2' end diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index 808c77542ed..3a04fd19a62 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -39,8 +39,8 @@ device, and it is completely free. } s.framework = 'SystemConfiguration' s.dependency 'FirebaseAnalyticsInterop', '~> 1.1' - s.dependency 'FirebaseCore', '~> 5.2' - s.dependency 'FirebaseInstanceID', '~> 3.6' + s.dependency 'FirebaseCore', '~> 6.0' + s.dependency 'FirebaseInstanceID', '~> 4.0' s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.6' s.dependency 'GoogleUtilities/Reachability', '~> 5.6' s.dependency 'GoogleUtilities/Environment', '~> 5.6' diff --git a/FirebaseStorage.podspec b/FirebaseStorage.podspec index 01c9152ea21..f97fad4d889 100644 --- a/FirebaseStorage.podspec +++ b/FirebaseStorage.podspec @@ -30,7 +30,7 @@ Firebase Storage provides robust, secure file uploads and downloads from Firebas s.osx.framework = 'CoreServices' s.dependency 'FirebaseAuthInterop', '~> 1.0' - s.dependency 'FirebaseCore', '~> 5.2' + s.dependency 'FirebaseCore', '~> 6.0' s.dependency 'GTMSessionFetcher/Core', '~> 1.1' s.pod_target_xcconfig = { 'GCC_C_LANGUAGE_STANDARD' => 'c99', diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index cf4875c9a37..09b8150fe13 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -43,7 +43,7 @@ target 'Firestore_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/CoreOnly', '5.20.991' + pod 'Firebase/CoreOnly', '6.0.0-pre' pod 'FirebaseAuth', :path => '../../' pod 'FirebaseAuthInterop', :path => '../../' From 827566cc20be7defc3d1afcce45ed44949df7faf Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 17:22:40 -0700 Subject: [PATCH 167/214] Fix minor typo (#2769) --- Example/Core/Tests/FIROptionsTest.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/Core/Tests/FIROptionsTest.m b/Example/Core/Tests/FIROptionsTest.m index b20d6dace69..e1f1406924b 100644 --- a/Example/Core/Tests/FIROptionsTest.m +++ b/Example/Core/Tests/FIROptionsTest.m @@ -585,7 +585,7 @@ - (void)testVersionConsistency { XCTAssertEqualObjects(str, [NSString stringWithUTF8String:(const char *)FIRCoreVersionString]); } -// Repeat test with more Objective C. +// Repeat test with more Objective-C. // TODO: The version test will break when the Firebase major version hits 10. - (void)testVersionConsistency2 { NSRange major = NSMakeRange(0, 1); From f590c9317563f7319b6f665d674daee08692c394 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 18:29:56 -0700 Subject: [PATCH 168/214] Private headers need to also be part of public (#2771) --- FirebaseInstanceID.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseInstanceID.podspec b/FirebaseInstanceID.podspec index 0ca733b3b5f..1f69bc26fef 100644 --- a/FirebaseInstanceID.podspec +++ b/FirebaseInstanceID.podspec @@ -28,7 +28,7 @@ services. base_dir = "Firebase/InstanceID/" s.source_files = base_dir + '**/*.[mh]' s.requires_arc = base_dir + '*.m' - s.public_header_files = base_dir + 'Public/*.h' + s.public_header_files = base_dir + 'Public/*.h', base_dir + 'Private/*.h' s.private_header_files = base_dir + 'Private/*.h' s.pod_target_xcconfig = { 'GCC_C_LANGUAGE_STANDARD' => 'c99', From 55814996a1a0d1bc5efdb666ec88278fea57229e Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Mon, 8 Apr 2019 22:38:51 -0400 Subject: [PATCH 169/214] Cocoapods 1.7.0 multiproject support (b/128794527) (#2751) --- .travis.yml | 46 ++ .../project.pbxproj | 411 ++++++++++++++++++ .../CocoapodsIntegrationTest.xcscheme | 91 ++++ .../CocoapodsIntegrationTest/AppDelegate.h | 23 + .../CocoapodsIntegrationTest/AppDelegate.m | 26 ++ .../AppIcon.appiconset/Contents.json | 98 +++++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 ++ .../Base.lproj/Main.storyboard | 24 + .../CocoapodsIntegrationTest/Info.plist | 45 ++ .../CocoapodsIntegrationTest/ViewController.h | 21 + .../CocoapodsIntegrationTest/ViewController.m | 25 ++ .../CocoapodsIntegrationTest/main.m | 24 + CocoapodsIntegrationTest/Gemfile | 5 + CocoapodsIntegrationTest/Gemfile.lock | 76 ++++ CocoapodsIntegrationTest/Podfile | 26 ++ .../Cocoapods1_6_1_frameworks/Gemfile | 5 + .../Cocoapods1_6_1_frameworks/Podfile | 19 + .../Cocoapods1_6_1_staticLibs/Gemfile | 5 + .../Cocoapods1_6_1_staticLibs/Podfile | 17 + .../Cocoapods1_7_0_frameworks/Gemfile | 5 + .../Cocoapods1_7_0_frameworks/Podfile | 19 + .../Gemfile | 5 + .../Podfile | 26 ++ .../Gemfile | 5 + .../Podfile | 23 + .../Cocoapods1_7_0_staticLibs/Gemfile | 5 + .../Cocoapods1_7_0_staticLibs/Podfile | 17 + .../scripts/build_all_environments.sh | 43 ++ .../scripts/build_with_environment.sh | 106 +++++ .../Auth Provider/OAuth/FIROAuthProvider.m | 5 +- Firebase/Core/Private/FIRAppInternal.h | 4 +- Firebase/Core/Private/FIRLogger.h | 2 +- Firebase/Core/Private/FIROptionsInternal.h | 2 +- Firebase/Core/Public/FIRConfiguration.h | 2 +- .../Runtime/FIRInAppMessaging+Bootstrap.m | 1 - Firebase/Messaging/FIRMessagingSecureSocket.m | 6 +- Firebase/Storage/FIRStorageReference.m | 2 +- Firebase/Storage/FIRStorageTask.m | 2 +- Firebase/Storage/FIRStorageUploadTask.m | 2 +- Firebase/Storage/FIRStorageUtils.m | 2 +- Firestore/Example/App/macOS/AppDelegate.m | 4 +- Functions/FirebaseFunctions/FIRFunctions.m | 8 +- Gemfile.lock | 12 +- GoogleUtilities/CHANGELOG.md | 2 + 45 files changed, 1301 insertions(+), 27 deletions(-) create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest.xcodeproj/project.pbxproj create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest.xcodeproj/xcshareddata/xcschemes/CocoapodsIntegrationTest.xcscheme create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/AppDelegate.h create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/AppDelegate.m create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/Assets.xcassets/Contents.json create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/Base.lproj/LaunchScreen.storyboard create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/Base.lproj/Main.storyboard create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/Info.plist create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/ViewController.h create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/ViewController.m create mode 100644 CocoapodsIntegrationTest/CocoapodsIntegrationTest/main.m create mode 100644 CocoapodsIntegrationTest/Gemfile create mode 100644 CocoapodsIntegrationTest/Gemfile.lock create mode 100644 CocoapodsIntegrationTest/Podfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_frameworks/Gemfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_frameworks/Podfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_staticLibs/Gemfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_staticLibs/Podfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_frameworks/Gemfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_frameworks/Podfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_frameworks/Gemfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_frameworks/Podfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_staticLibs/Gemfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_staticLibs/Podfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_staticLibs/Gemfile create mode 100644 CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_staticLibs/Podfile create mode 100755 CocoapodsIntegrationTest/scripts/build_all_environments.sh create mode 100755 CocoapodsIntegrationTest/scripts/build_with_environment.sh diff --git a/.travis.yml b/.travis.yml index e42689c23af..5c93cd05aff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,12 @@ cache: - bundler - cocoapods +stages: + - checks + - test + - name: cocoapods_compatibility_check + if: type = cron + jobs: include: - stage: checks @@ -212,6 +218,46 @@ jobs: script: - travis_retry ./scripts/if_changed.sh ./scripts/build.sh $PROJECT $PLATFORM $METHOD + # Validate Cocoapods configurations + # This may take long time, so we would like to run it only once all other tests pass + # Validate Cocoapods 1.7.0 compatibility + - stage: cocoapods_compatibility_check + env: + - POD_CONFIG_DIR=Cocoapods1_7_0_multiprojects_frameworks + script: + - travis_retry ./CocoapodsIntegrationTest/scripts/build_with_environment.sh --gemfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Gemfile --podfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Podfile + + - stage: cocoapods_compatibility_check + env: + - POD_CONFIG_DIR=Cocoapods1_7_0_frameworks + script: + - travis_retry ./CocoapodsIntegrationTest/scripts/build_with_environment.sh --gemfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Gemfile --podfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Podfile + + - stage: cocoapods_compatibility_check + env: + - POD_CONFIG_DIR=Cocoapods1_7_0_multiprojects_staticLibs + script: + - travis_retry ./CocoapodsIntegrationTest/scripts/build_with_environment.sh --gemfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Gemfile --podfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Podfile + + - stage: cocoapods_compatibility_check + env: + - POD_CONFIG_DIR=Cocoapods1_7_0_staticLibs + script: + - travis_retry ./CocoapodsIntegrationTest/scripts/build_with_environment.sh --gemfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Gemfile --podfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Podfile + + # Validate Cocoapods 1.6.1 compatibility + - stage: cocoapods_compatibility_check + env: + - POD_CONFIG_DIR=Cocoapods1_6_1_frameworks + script: + - travis_retry ./CocoapodsIntegrationTest/scripts/build_with_environment.sh --gemfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Gemfile --podfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Podfile + + - stage: cocoapods_compatibility_check + env: + - POD_CONFIG_DIR=Cocoapods1_6_1_staticLibs + script: + - travis_retry ./CocoapodsIntegrationTest/scripts/build_with_environment.sh --gemfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Gemfile --podfile=./CocoapodsIntegrationTest/TestEnvironments/${POD_CONFIG_DIR}/Podfile + allow_failures: # Run fuzz tests only on cron jobs. - stage: test diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest.xcodeproj/project.pbxproj b/CocoapodsIntegrationTest/CocoapodsIntegrationTest.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..c1224d0f529 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest.xcodeproj/project.pbxproj @@ -0,0 +1,411 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 9A9CEFAD22564DAB00C32C5B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CEFAC22564DAB00C32C5B /* AppDelegate.m */; }; + 9A9CEFB022564DAB00C32C5B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CEFAF22564DAB00C32C5B /* ViewController.m */; }; + 9A9CEFB322564DAB00C32C5B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A9CEFB122564DAB00C32C5B /* Main.storyboard */; }; + 9A9CEFB522564DAC00C32C5B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9A9CEFB422564DAC00C32C5B /* Assets.xcassets */; }; + 9A9CEFB822564DAC00C32C5B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A9CEFB622564DAC00C32C5B /* LaunchScreen.storyboard */; }; + 9A9CEFBB22564DAC00C32C5B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CEFBA22564DAC00C32C5B /* main.m */; }; + A1D7F89167D4EDC7356BA0F1 /* Pods_CocoapodsIntegrationTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B465C9A8AC12C2E1B44B6A6 /* Pods_CocoapodsIntegrationTest.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 8B465C9A8AC12C2E1B44B6A6 /* Pods_CocoapodsIntegrationTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CocoapodsIntegrationTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A9CEFA822564DAB00C32C5B /* CocoapodsIntegrationTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CocoapodsIntegrationTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A9CEFAB22564DAB00C32C5B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 9A9CEFAC22564DAB00C32C5B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 9A9CEFAE22564DAB00C32C5B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 9A9CEFAF22564DAB00C32C5B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 9A9CEFB222564DAB00C32C5B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 9A9CEFB422564DAC00C32C5B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 9A9CEFB722564DAC00C32C5B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 9A9CEFB922564DAC00C32C5B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9A9CEFBA22564DAC00C32C5B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + B60CFECB6829D89F305E8225 /* Pods-CocoapodsIntegrationTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CocoapodsIntegrationTest.release.xcconfig"; path = "Target Support Files/Pods-CocoapodsIntegrationTest/Pods-CocoapodsIntegrationTest.release.xcconfig"; sourceTree = ""; }; + DEA28D2A6F2A50E15ADA23A7 /* Pods-CocoapodsIntegrationTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CocoapodsIntegrationTest.debug.xcconfig"; path = "Target Support Files/Pods-CocoapodsIntegrationTest/Pods-CocoapodsIntegrationTest.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 9A9CEFA522564DAB00C32C5B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A1D7F89167D4EDC7356BA0F1 /* Pods_CocoapodsIntegrationTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5262DE71BE536EC6B8E91815 /* Pods */ = { + isa = PBXGroup; + children = ( + DEA28D2A6F2A50E15ADA23A7 /* Pods-CocoapodsIntegrationTest.debug.xcconfig */, + B60CFECB6829D89F305E8225 /* Pods-CocoapodsIntegrationTest.release.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 9A9CEF9F22564DAB00C32C5B = { + isa = PBXGroup; + children = ( + 9A9CEFAA22564DAB00C32C5B /* CocoapodsIntegrationTest */, + 9A9CEFA922564DAB00C32C5B /* Products */, + 5262DE71BE536EC6B8E91815 /* Pods */, + A9C0E60CE8FE7029246BCCBF /* Frameworks */, + ); + sourceTree = ""; + }; + 9A9CEFA922564DAB00C32C5B /* Products */ = { + isa = PBXGroup; + children = ( + 9A9CEFA822564DAB00C32C5B /* CocoapodsIntegrationTest.app */, + ); + name = Products; + sourceTree = ""; + }; + 9A9CEFAA22564DAB00C32C5B /* CocoapodsIntegrationTest */ = { + isa = PBXGroup; + children = ( + 9A9CEFAB22564DAB00C32C5B /* AppDelegate.h */, + 9A9CEFAC22564DAB00C32C5B /* AppDelegate.m */, + 9A9CEFAE22564DAB00C32C5B /* ViewController.h */, + 9A9CEFAF22564DAB00C32C5B /* ViewController.m */, + 9A9CEFB122564DAB00C32C5B /* Main.storyboard */, + 9A9CEFB422564DAC00C32C5B /* Assets.xcassets */, + 9A9CEFB622564DAC00C32C5B /* LaunchScreen.storyboard */, + 9A9CEFB922564DAC00C32C5B /* Info.plist */, + 9A9CEFBA22564DAC00C32C5B /* main.m */, + ); + path = CocoapodsIntegrationTest; + sourceTree = ""; + }; + A9C0E60CE8FE7029246BCCBF /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8B465C9A8AC12C2E1B44B6A6 /* Pods_CocoapodsIntegrationTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 9A9CEFA722564DAB00C32C5B /* CocoapodsIntegrationTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9A9CEFBE22564DAC00C32C5B /* Build configuration list for PBXNativeTarget "CocoapodsIntegrationTest" */; + buildPhases = ( + 21DA73D41725957D85E38FC2 /* [CP] Check Pods Manifest.lock */, + 9A9CEFA422564DAB00C32C5B /* Sources */, + 9A9CEFA522564DAB00C32C5B /* Frameworks */, + 9A9CEFA622564DAB00C32C5B /* Resources */, + 840F09F7845F2F1AB96D6F7E /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CocoapodsIntegrationTest; + productName = CocoapodsIntegrationTest; + productReference = 9A9CEFA822564DAB00C32C5B /* CocoapodsIntegrationTest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 9A9CEFA022564DAB00C32C5B /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 9A9CEFA722564DAB00C32C5B = { + CreatedOnToolsVersion = 10.1; + }; + }; + }; + buildConfigurationList = 9A9CEFA322564DAB00C32C5B /* Build configuration list for PBXProject "CocoapodsIntegrationTest" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 9A9CEF9F22564DAB00C32C5B; + productRefGroup = 9A9CEFA922564DAB00C32C5B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 9A9CEFA722564DAB00C32C5B /* CocoapodsIntegrationTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 9A9CEFA622564DAB00C32C5B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A9CEFB822564DAC00C32C5B /* LaunchScreen.storyboard in Resources */, + 9A9CEFB522564DAC00C32C5B /* Assets.xcassets in Resources */, + 9A9CEFB322564DAB00C32C5B /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 21DA73D41725957D85E38FC2 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-CocoapodsIntegrationTest-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 840F09F7845F2F1AB96D6F7E /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CocoapodsIntegrationTest/Pods-CocoapodsIntegrationTest-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CocoapodsIntegrationTest/Pods-CocoapodsIntegrationTest-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CocoapodsIntegrationTest/Pods-CocoapodsIntegrationTest-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 9A9CEFA422564DAB00C32C5B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A9CEFB022564DAB00C32C5B /* ViewController.m in Sources */, + 9A9CEFBB22564DAC00C32C5B /* main.m in Sources */, + 9A9CEFAD22564DAB00C32C5B /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 9A9CEFB122564DAB00C32C5B /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 9A9CEFB222564DAB00C32C5B /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 9A9CEFB622564DAC00C32C5B /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 9A9CEFB722564DAC00C32C5B /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 9A9CEFBC22564DAC00C32C5B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 9A9CEFBD22564DAC00C32C5B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 9A9CEFBF22564DAC00C32C5B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DEA28D2A6F2A50E15ADA23A7 /* Pods-CocoapodsIntegrationTest.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CocoapodsIntegrationTest/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = google.com.CocoapodsIntegrationTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 9A9CEFC022564DAC00C32C5B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B60CFECB6829D89F305E8225 /* Pods-CocoapodsIntegrationTest.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CocoapodsIntegrationTest/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = google.com.CocoapodsIntegrationTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 9A9CEFA322564DAB00C32C5B /* Build configuration list for PBXProject "CocoapodsIntegrationTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A9CEFBC22564DAC00C32C5B /* Debug */, + 9A9CEFBD22564DAC00C32C5B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9A9CEFBE22564DAC00C32C5B /* Build configuration list for PBXNativeTarget "CocoapodsIntegrationTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A9CEFBF22564DAC00C32C5B /* Debug */, + 9A9CEFC022564DAC00C32C5B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 9A9CEFA022564DAB00C32C5B /* Project object */; +} diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest.xcodeproj/xcshareddata/xcschemes/CocoapodsIntegrationTest.xcscheme b/CocoapodsIntegrationTest/CocoapodsIntegrationTest.xcodeproj/xcshareddata/xcschemes/CocoapodsIntegrationTest.xcscheme new file mode 100644 index 00000000000..c7f4d3c3616 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest.xcodeproj/xcshareddata/xcschemes/CocoapodsIntegrationTest.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/AppDelegate.h b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/AppDelegate.h new file mode 100644 index 00000000000..b0b8e06e84c --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/AppDelegate.h @@ -0,0 +1,23 @@ +/* + * Copyright 2019 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 + +@interface AppDelegate : UIResponder + +@property(strong, nonatomic) UIWindow *window; + +@end diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/AppDelegate.m b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/AppDelegate.m new file mode 100644 index 00000000000..ae0efdf729f --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/AppDelegate.m @@ -0,0 +1,26 @@ +/* + * Copyright 2019 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 "AppDelegate.h" +#import + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +@end diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..d8db8d65fd7 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Assets.xcassets/Contents.json b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Base.lproj/LaunchScreen.storyboard b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000000..bfa36129419 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Base.lproj/Main.storyboard b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Base.lproj/Main.storyboard new file mode 100644 index 00000000000..942f0bc452d --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Info.plist b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Info.plist new file mode 100644 index 00000000000..16be3b68112 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/ViewController.h b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/ViewController.h new file mode 100644 index 00000000000..7f02e1a5218 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/ViewController.h @@ -0,0 +1,21 @@ +/* + * Copyright 2019 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 + +@interface ViewController : UIViewController + +@end diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/ViewController.m b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/ViewController.m new file mode 100644 index 00000000000..a4696c81665 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/ViewController.m @@ -0,0 +1,25 @@ +/* + * Copyright 2019 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 "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +@end diff --git a/CocoapodsIntegrationTest/CocoapodsIntegrationTest/main.m b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/main.m new file mode 100644 index 00000000000..efb3cc9e634 --- /dev/null +++ b/CocoapodsIntegrationTest/CocoapodsIntegrationTest/main.m @@ -0,0 +1,24 @@ +/* + * Copyright 2019 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 +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/CocoapodsIntegrationTest/Gemfile b/CocoapodsIntegrationTest/Gemfile new file mode 100644 index 00000000000..a4698322428 --- /dev/null +++ b/CocoapodsIntegrationTest/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'cocoapods', "~>1.7.0.beta" diff --git a/CocoapodsIntegrationTest/Gemfile.lock b/CocoapodsIntegrationTest/Gemfile.lock new file mode 100644 index 00000000000..bc83f51078c --- /dev/null +++ b/CocoapodsIntegrationTest/Gemfile.lock @@ -0,0 +1,76 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.0) + activesupport (4.2.11.1) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + atomos (0.1.3) + claide (1.0.2) + cocoapods (1.7.0.beta.3) + activesupport (>= 4.0.2, < 5) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.7.0.beta.3) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-stats (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.3.1, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.2.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.6.6) + nap (~> 1.0) + ruby-macho (~> 1.4) + xcodeproj (>= 1.8.2, < 2.0) + cocoapods-core (1.7.0.beta.3) + activesupport (>= 4.0.2, < 6) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + cocoapods-deintegrate (1.0.4) + cocoapods-downloader (1.2.2) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.0) + cocoapods-stats (1.1.0) + cocoapods-trunk (1.3.1) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.1.0) + colored2 (3.1.2) + concurrent-ruby (1.1.5) + escape (0.0.4) + fourflusher (2.2.0) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + minitest (5.11.3) + molinillo (0.6.6) + nanaimo (0.2.6) + nap (1.1.0) + netrc (0.11.0) + ruby-macho (1.4.0) + thread_safe (0.3.6) + tzinfo (1.2.5) + thread_safe (~> 0.1) + xcodeproj (1.8.2) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods (~> 1.7.0.beta) + +BUNDLED WITH + 1.17.1 diff --git a/CocoapodsIntegrationTest/Podfile b/CocoapodsIntegrationTest/Podfile new file mode 100644 index 00000000000..ed45548a20d --- /dev/null +++ b/CocoapodsIntegrationTest/Podfile @@ -0,0 +1,26 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '9.0' + +target 'CocoapodsIntegrationTest' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + pod 'FirebaseCore', :path => '../' + pod 'FirebaseAuth', :path => '../' + pod 'FirebaseCore', :path => '../' + pod 'FirebaseDatabase', :path => '../' + pod 'FirebaseDynamicLinks', :path => '../' + pod 'FirebaseFirestore', :path => '../' + pod 'FirebaseFunctions', :path => '../' + pod 'FirebaseInAppMessaging', :path => '../' + pod 'FirebaseInstanceID', :path => '../' + pod 'FirebaseMessaging', :path => '../' + pod 'FirebaseStorage', :path => '../' + pod 'GoogleUtilities', :path => '../' +end + +# Using the new speed-enhancing features available with CocoaPods 1.7+ +# [sudo] gem install cocoapods --pre +install! 'cocoapods', + :generate_multiple_pod_projects => true, + :incremental_installation => true diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_frameworks/Gemfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_frameworks/Gemfile new file mode 100644 index 00000000000..7d579adf0f1 --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_frameworks/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'cocoapods', "=1.6.1" diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_frameworks/Podfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_frameworks/Podfile new file mode 100644 index 00000000000..ba9f0da36ac --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_frameworks/Podfile @@ -0,0 +1,19 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '9.0' + +target 'CocoapodsIntegrationTest' do + use_frameworks! + + pod 'FirebaseCore', :path => '../' + pod 'FirebaseAuth', :path => '../' + pod 'FirebaseCore', :path => '../' + pod 'FirebaseDatabase', :path => '../' + pod 'FirebaseDynamicLinks', :path => '../' + pod 'FirebaseFirestore', :path => '../' + pod 'FirebaseFunctions', :path => '../' + pod 'FirebaseInAppMessaging', :path => '../' + pod 'FirebaseInstanceID', :path => '../' + pod 'FirebaseMessaging', :path => '../' + pod 'FirebaseStorage', :path => '../' + pod 'GoogleUtilities', :path => '../' +end diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_staticLibs/Gemfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_staticLibs/Gemfile new file mode 100644 index 00000000000..7d579adf0f1 --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_staticLibs/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'cocoapods', "=1.6.1" diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_staticLibs/Podfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_staticLibs/Podfile new file mode 100644 index 00000000000..5d49a5ba8dc --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_6_1_staticLibs/Podfile @@ -0,0 +1,17 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '9.0' + +target 'CocoapodsIntegrationTest' do + pod 'FirebaseCore', :path => '../' + pod 'FirebaseAuth', :path => '../' + pod 'FirebaseCore', :path => '../' + pod 'FirebaseDatabase', :path => '../' + pod 'FirebaseDynamicLinks', :path => '../' + pod 'FirebaseFirestore', :path => '../' + pod 'FirebaseFunctions', :path => '../' + pod 'FirebaseInAppMessaging', :path => '../' + pod 'FirebaseInstanceID', :path => '../' + pod 'FirebaseMessaging', :path => '../' + pod 'FirebaseStorage', :path => '../' + pod 'GoogleUtilities', :path => '../' +end diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_frameworks/Gemfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_frameworks/Gemfile new file mode 100644 index 00000000000..a4698322428 --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_frameworks/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'cocoapods', "~>1.7.0.beta" diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_frameworks/Podfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_frameworks/Podfile new file mode 100644 index 00000000000..1ad0177e5d0 --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_frameworks/Podfile @@ -0,0 +1,19 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '9.0' + +target 'CocoapodsIntegrationTest' do + use_frameworks! + + pod 'FirebaseCore', :path => '../' + pod 'FirebaseAuth', :path => '../' + pod 'FirebaseCore', :path => '../' + pod 'FirebaseDatabase', :path => '../' + pod 'FirebaseDynamicLinks', :path => '../' + pod 'FirebaseFirestore', :path => '../' + pod 'FirebaseFunctions', :path => '../' + pod 'FirebaseInAppMessaging', :path => '../' + pod 'FirebaseInstanceID', :path => '../' + pod 'FirebaseMessaging', :path => '../' + pod 'FirebaseStorage', :path => '../' + pod 'GoogleUtilities', :path => '../' +end \ No newline at end of file diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_frameworks/Gemfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_frameworks/Gemfile new file mode 100644 index 00000000000..a4698322428 --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_frameworks/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'cocoapods', "~>1.7.0.beta" diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_frameworks/Podfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_frameworks/Podfile new file mode 100644 index 00000000000..ed45548a20d --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_frameworks/Podfile @@ -0,0 +1,26 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '9.0' + +target 'CocoapodsIntegrationTest' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + pod 'FirebaseCore', :path => '../' + pod 'FirebaseAuth', :path => '../' + pod 'FirebaseCore', :path => '../' + pod 'FirebaseDatabase', :path => '../' + pod 'FirebaseDynamicLinks', :path => '../' + pod 'FirebaseFirestore', :path => '../' + pod 'FirebaseFunctions', :path => '../' + pod 'FirebaseInAppMessaging', :path => '../' + pod 'FirebaseInstanceID', :path => '../' + pod 'FirebaseMessaging', :path => '../' + pod 'FirebaseStorage', :path => '../' + pod 'GoogleUtilities', :path => '../' +end + +# Using the new speed-enhancing features available with CocoaPods 1.7+ +# [sudo] gem install cocoapods --pre +install! 'cocoapods', + :generate_multiple_pod_projects => true, + :incremental_installation => true diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_staticLibs/Gemfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_staticLibs/Gemfile new file mode 100644 index 00000000000..a4698322428 --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_staticLibs/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'cocoapods', "~>1.7.0.beta" diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_staticLibs/Podfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_staticLibs/Podfile new file mode 100644 index 00000000000..c20dc638114 --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_multiprojects_staticLibs/Podfile @@ -0,0 +1,23 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '9.0' + +target 'CocoapodsIntegrationTest' do + pod 'FirebaseCore', :path => '../' + pod 'FirebaseAuth', :path => '../' + pod 'FirebaseCore', :path => '../' + pod 'FirebaseDatabase', :path => '../' + pod 'FirebaseDynamicLinks', :path => '../' + pod 'FirebaseFirestore', :path => '../' + pod 'FirebaseFunctions', :path => '../' + pod 'FirebaseInAppMessaging', :path => '../' + pod 'FirebaseInstanceID', :path => '../' + pod 'FirebaseMessaging', :path => '../' + pod 'FirebaseStorage', :path => '../' + pod 'GoogleUtilities', :path => '../' +end + +# Using the new speed-enhancing features available with CocoaPods 1.7+ +# [sudo] gem install cocoapods --pre +install! 'cocoapods', + :generate_multiple_pod_projects => true, + :incremental_installation => true diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_staticLibs/Gemfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_staticLibs/Gemfile new file mode 100644 index 00000000000..a4698322428 --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_staticLibs/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'cocoapods', "~>1.7.0.beta" diff --git a/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_staticLibs/Podfile b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_staticLibs/Podfile new file mode 100644 index 00000000000..5d49a5ba8dc --- /dev/null +++ b/CocoapodsIntegrationTest/TestEnvironments/Cocoapods1_7_0_staticLibs/Podfile @@ -0,0 +1,17 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '9.0' + +target 'CocoapodsIntegrationTest' do + pod 'FirebaseCore', :path => '../' + pod 'FirebaseAuth', :path => '../' + pod 'FirebaseCore', :path => '../' + pod 'FirebaseDatabase', :path => '../' + pod 'FirebaseDynamicLinks', :path => '../' + pod 'FirebaseFirestore', :path => '../' + pod 'FirebaseFunctions', :path => '../' + pod 'FirebaseInAppMessaging', :path => '../' + pod 'FirebaseInstanceID', :path => '../' + pod 'FirebaseMessaging', :path => '../' + pod 'FirebaseStorage', :path => '../' + pod 'GoogleUtilities', :path => '../' +end diff --git a/CocoapodsIntegrationTest/scripts/build_all_environments.sh b/CocoapodsIntegrationTest/scripts/build_all_environments.sh new file mode 100755 index 00000000000..b992b76fae0 --- /dev/null +++ b/CocoapodsIntegrationTest/scripts/build_all_environments.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Copyright 2019 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. + +# The script uses combinations of Gemfile/Podfile to validate Firebase +# compatibility with Cocoapods versions and settings. +# To add a new configuration: +# - create a new directory in `CocoapodsIntegrationTest/TestEnvironments` +# - place Gemfile and Pod file to the created directory +# The script attempts to build the test project for each configuration from +# `CocoapodsIntegrationTest/TestEnvironments`. + +set -euo pipefail + +scriptsDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +buildScripts="$( realpath ${scriptsDir}/build_with_environment.sh )" +environmentsDir="$( realpath ${scriptsDir}/../TestEnvironments )" + +echo "Dir: ${environmentsDir}" + +pushd "${environmentsDir}" + +for environmentDir in `find . -type d -mindepth 1 -maxdepth 1` +do + echo "" + echo "--- Build for environment from ${environmentDir} ---" + source $buildScripts --gemfile="${environmentDir}/Gemfile" --podfile="${environmentDir}/Podfile" + echo "--- Build for environment from ${environmentDir} finished ---" +done + +popd \ No newline at end of file diff --git a/CocoapodsIntegrationTest/scripts/build_with_environment.sh b/CocoapodsIntegrationTest/scripts/build_with_environment.sh new file mode 100755 index 00000000000..efa366027f0 --- /dev/null +++ b/CocoapodsIntegrationTest/scripts/build_with_environment.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +# Copyright 2019 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. + +# The script uses combination of Gemfile/Podfile to validate Firebase +# compatibility with Cocoapods versions and settings. +# See `printUsage` for usage. + +set -euo pipefail + +# +function runXcodebuild() { + parameters=( + -workspace 'CocoapodsIntegrationTest.xcworkspace' + -scheme 'CocoapodsIntegrationTest' + -sdk 'iphonesimulator' + -destination 'platform=iOS Simulator,name=iPhone 7' + CODE_SIGNING_REQUIRED=NO + clean + build + ) + + echo xcodebuild "${parameters[@]}" + xcodebuild "${parameters[@]}" | xcpretty; result=$? +} + +# Configures bundler environment using Gemfile at the specified path. +function prepareBundle() { + cp -f "$@" ./Gemfile + + # Travis sets this variable. We should unset it otherwise `bundle exec` will + # use the Gemfile by the path from the variable. + unset BUNDLE_GEMFILE + + bundle update +} + +# Updates Cocoapods using Podfile at the specified path. +function prepareCocoapods() { + cp -f "$@" ./Podfile + echo "Cocoapods version: $(bundle exec pod --version)" + bundle exec pod deintegrate + bundle exec pod update +} + +function printUsage() { + echo "USAGE: build_with_environment.sh --gemfile= --podfile=" +} + +for i in "$@" +do + case $i in + --gemfile=*) + GEMFILE="${i#*=}" + shift + ;; + --podfile=*) + PODFILE="${i#*=}" + shift + ;; + *) + printUsage + exit -1 + ;; + esac +done + +if [ -z ${GEMFILE+x} ]; then + echo "--gemfile= is a required parameter" + exit -1 +fi + +if [ -z ${PODFILE+x} ]; then + echo "--podfile= is a required parameter" + exit -1 +fi + +# Convert path to absolute one in case the script is run from another directory. +RESOLVED_GEMFILE="$(realpath ${GEMFILE})" +RESLOVED_PODFILE="$(realpath ${PODFILE})" + +echo "Gemfile = ${RESOLVED_GEMFILE}" +echo "Podfile = ${RESLOVED_PODFILE}" + +# Make sure we build from the project root dir. +scriptDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +pushd "${scriptDir}/.." + +prepareBundle "${RESOLVED_GEMFILE}" +prepareCocoapods "${RESLOVED_PODFILE}" +runXcodebuild + +# Recover original directory just in case +popd diff --git a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m index dd322d6bcc5..6f2fa5092f2 100644 --- a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m +++ b/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m @@ -17,7 +17,9 @@ #include #import "FIROAuthProvider.h" -#import "FIRApp.h" +#import +#import + #import "FIRAuthBackend.h" #import "FIRAuth_Internal.h" #import "FIRAuthErrorUtils.h" @@ -26,7 +28,6 @@ #import "FIRAuthWebUtils.h" #import "FIROAuthCredential_Internal.h" #import "FIROAuthCredential.h" -#import "FIROptions.h" #if TARGET_OS_IOS #import "FIRAuthURLPresenter.h" diff --git a/Firebase/Core/Private/FIRAppInternal.h b/Firebase/Core/Private/FIRAppInternal.h index 3c346ffc4cb..09728cd33be 100644 --- a/Firebase/Core/Private/FIRAppInternal.h +++ b/Firebase/Core/Private/FIRAppInternal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#import "FIRApp.h" -#import "FIRErrors.h" +#import +#import @class FIRComponentContainer; @protocol FIRLibrary; diff --git a/Firebase/Core/Private/FIRLogger.h b/Firebase/Core/Private/FIRLogger.h index 41f37480b8e..a86fb9741b5 100644 --- a/Firebase/Core/Private/FIRLogger.h +++ b/Firebase/Core/Private/FIRLogger.h @@ -16,7 +16,7 @@ #import -#import "FIRLoggerLevel.h" +#import NS_ASSUME_NONNULL_BEGIN diff --git a/Firebase/Core/Private/FIROptionsInternal.h b/Firebase/Core/Private/FIROptionsInternal.h index 7bb40fc10d6..117efdaa0fa 100644 --- a/Firebase/Core/Private/FIROptionsInternal.h +++ b/Firebase/Core/Private/FIROptionsInternal.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIROptions.h" +#import /** * Keys for the strings in the plist file. diff --git a/Firebase/Core/Public/FIRConfiguration.h b/Firebase/Core/Public/FIRConfiguration.h index 2b8e678ceb8..8de3b076a62 100644 --- a/Firebase/Core/Public/FIRConfiguration.h +++ b/Firebase/Core/Public/FIRConfiguration.h @@ -16,7 +16,7 @@ #import -#import "FIRLoggerLevel.h" +#import NS_ASSUME_NONNULL_BEGIN diff --git a/Firebase/InAppMessaging/Runtime/FIRInAppMessaging+Bootstrap.m b/Firebase/InAppMessaging/Runtime/FIRInAppMessaging+Bootstrap.m index 1f3134adb44..c4c5509e5b0 100644 --- a/Firebase/InAppMessaging/Runtime/FIRInAppMessaging+Bootstrap.m +++ b/Firebase/InAppMessaging/Runtime/FIRInAppMessaging+Bootstrap.m @@ -26,7 +26,6 @@ #import "FIRIAMClearcutUploader.h" #import "FIRIAMRuntimeManager.h" #import "FIRIAMSDKSettings.h" -#import "FIROptionsInternal.h" #import "NSString+FIRInterlaceStrings.h" @implementation FIRInAppMessaging (Bootstrap) diff --git a/Firebase/Messaging/FIRMessagingSecureSocket.m b/Firebase/Messaging/FIRMessagingSecureSocket.m index 46d4a70be9e..dd78a76d2ab 100644 --- a/Firebase/Messaging/FIRMessagingSecureSocket.m +++ b/Firebase/Messaging/FIRMessagingSecureSocket.m @@ -16,9 +16,9 @@ #import "FIRMessagingSecureSocket.h" -#import "GPBMessage.h" -#import "GPBCodedOutputStream.h" -#import "GPBUtilities.h" +#import +#import +#import #import "FIRMessagingCodedInputStream.h" #import "FIRMessagingDefines.h" diff --git a/Firebase/Storage/FIRStorageReference.m b/Firebase/Storage/FIRStorageReference.m index 78c9dc64dee..8e70aaa2c73 100644 --- a/Firebase/Storage/FIRStorageReference.m +++ b/Firebase/Storage/FIRStorageReference.m @@ -33,7 +33,7 @@ #import #import -#import "GTMSessionFetcherService.h" +#import @implementation FIRStorageReference diff --git a/Firebase/Storage/FIRStorageTask.m b/Firebase/Storage/FIRStorageTask.m index c0848df259d..748bf2e3628 100644 --- a/Firebase/Storage/FIRStorageTask.m +++ b/Firebase/Storage/FIRStorageTask.m @@ -22,7 +22,7 @@ #import "FIRStorageTask_Private.h" #import "FIRStorage_Private.h" -#import "GTMSessionFetcherService.h" +#import @implementation FIRStorageTask diff --git a/Firebase/Storage/FIRStorageUploadTask.m b/Firebase/Storage/FIRStorageUploadTask.m index 0aba3ab431b..994aa21b659 100644 --- a/Firebase/Storage/FIRStorageUploadTask.m +++ b/Firebase/Storage/FIRStorageUploadTask.m @@ -20,7 +20,7 @@ #import "FIRStorageTask_Private.h" #import "FIRStorageUploadTask_Private.h" -#import "GTMSessionUploadFetcher.h" +#import @implementation FIRStorageUploadTask diff --git a/Firebase/Storage/FIRStorageUtils.m b/Firebase/Storage/FIRStorageUtils.m index 66869ecc7b5..c9ccc6f38ac 100644 --- a/Firebase/Storage/FIRStorageUtils.m +++ b/Firebase/Storage/FIRStorageUtils.m @@ -27,7 +27,7 @@ #import "FirebaseStorage.h" -#import "GTMSessionFetcher.h" +#import // This is the list at https://cloud.google.com/storage/docs/json_api/ without &, ; and +. NSString *const kGCSObjectAllowedCharacterSet = diff --git a/Firestore/Example/App/macOS/AppDelegate.m b/Firestore/Example/App/macOS/AppDelegate.m index 03c73970596..49f300a8d07 100644 --- a/Firestore/Example/App/macOS/AppDelegate.m +++ b/Firestore/Example/App/macOS/AppDelegate.m @@ -15,8 +15,8 @@ */ #import "AppDelegate.h" -#import "FirebaseCore.h" -#import "FirebaseFirestore.h" +#import +#import @interface AppDelegate () diff --git a/Functions/FirebaseFunctions/FIRFunctions.m b/Functions/FirebaseFunctions/FIRFunctions.m index 61a594c9eb1..402ee665e16 100644 --- a/Functions/FirebaseFunctions/FIRFunctions.m +++ b/Functions/FirebaseFunctions/FIRFunctions.m @@ -29,10 +29,10 @@ #import "FUNSerializer.h" #import "FUNUsageValidation.h" -#import "FIRApp.h" -#import "FIRAppInternal.h" -#import "FIROptions.h" -#import "GTMSessionFetcherService.h" +#import +#import +#import +#import // The following two macros supply the incantation so that the C // preprocessor does not try to parse the version as a floating diff --git a/Gemfile.lock b/Gemfile.lock index 19a8e920992..5b15e50861c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,7 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (3.0.0) - activesupport (4.2.11) + activesupport (4.2.11.1) i18n (~> 0.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) @@ -32,9 +32,9 @@ GEM activesupport (>= 4.0.2, < 6) fuzzy_match (~> 2.0.4) nap (~> 1.0) - cocoapods-deintegrate (1.0.3) + cocoapods-deintegrate (1.0.4) cocoapods-downloader (1.2.2) - cocoapods-generate (1.3.1) + cocoapods-generate (1.4.0) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.0) @@ -44,7 +44,7 @@ GEM netrc (~> 0.11) cocoapods-try (1.1.0) colored2 (3.1.2) - concurrent-ruby (1.1.4) + concurrent-ruby (1.1.5) escape (0.0.4) fourflusher (2.2.0) fuzzy_match (2.0.4) @@ -60,7 +60,7 @@ GEM thread_safe (0.3.6) tzinfo (1.2.5) thread_safe (~> 0.1) - xcodeproj (1.8.1) + xcodeproj (1.8.2) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -75,4 +75,4 @@ DEPENDENCIES cocoapods-generate BUNDLED WITH - 1.16.6 + 1.17.3 diff --git a/GoogleUtilities/CHANGELOG.md b/GoogleUtilities/CHANGELOG.md index 2bf1c603e2b..a4f452dfaf9 100644 --- a/GoogleUtilities/CHANGELOG.md +++ b/GoogleUtilities/CHANGELOG.md @@ -1,4 +1,6 @@ # Unreleased +- `GULAppDelegateSwizzler`: support of remote notification methods. (#2698) +- `GULAppDelegateSwizzler`: tvOS support. (#2698) # 5.5.0 - Revert 5.4.x changes restoring 5.3.7 version. From 8edb8f5735cba5b463939557fef2af62958a0c2a Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 19:57:31 -0700 Subject: [PATCH 170/214] Remove deprecated RTDB API (#2770) --- Firebase/Database/CHANGELOG.md | 3 +++ Firebase/Database/FIRDatabaseReference.m | 4 ---- Firebase/Database/Public/FIRDatabaseReference.h | 5 ----- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Firebase/Database/CHANGELOG.md b/Firebase/Database/CHANGELOG.md index cd2526ac4d1..092f4f2ea0e 100644 --- a/Firebase/Database/CHANGELOG.md +++ b/Firebase/Database/CHANGELOG.md @@ -1,3 +1,6 @@ +# v6.0.0 +- [removed] Remove deprecated `childByAppendingPath` API. (#2763) + # v5.1.1 - [fixed] Fixed crash in FSRWebSocket. (#2485) diff --git a/Firebase/Database/FIRDatabaseReference.m b/Firebase/Database/FIRDatabaseReference.m index 3ea5992f08f..9d49124e6f2 100644 --- a/Firebase/Database/FIRDatabaseReference.m +++ b/Firebase/Database/FIRDatabaseReference.m @@ -90,10 +90,6 @@ - (FIRDatabaseReference *) root { #pragma mark - #pragma mark Child methods -- (FIRDatabaseReference *)childByAppendingPath:(NSString *)pathString { - return [self child:pathString]; -} - - (FIRDatabaseReference *)child:(NSString *)pathString { if ([self.path getFront] == nil) { // we're at the root diff --git a/Firebase/Database/Public/FIRDatabaseReference.h b/Firebase/Database/Public/FIRDatabaseReference.h index b96585e65a4..e6a4891acdc 100644 --- a/Firebase/Database/Public/FIRDatabaseReference.h +++ b/Firebase/Database/Public/FIRDatabaseReference.h @@ -51,11 +51,6 @@ NS_SWIFT_NAME(DatabaseReference) */ - (FIRDatabaseReference *)child:(NSString *)pathString; -/** - * childByAppendingPath: is deprecated, use child: instead. - */ -- (FIRDatabaseReference *)childByAppendingPath:(NSString *)pathString __deprecated_msg("use child: instead"); - /** * childByAutoId generates a new child location using a unique key and returns a * FIRDatabaseReference to it. This is useful when the children of a Firebase Database From 36ea0a7b587d07d9d76d0479fdb73c316a030c84 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Mon, 8 Apr 2019 20:24:38 -0700 Subject: [PATCH 171/214] Use development version of IID for FIAM testing (#2772) * Use development version of IID for FIAM testing * FIAM tests need Analytics * Also FIAM Display --- InAppMessaging/Example/Podfile | 9 ++++++--- InAppMessagingDisplay/Example/Podfile | 5 +++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/InAppMessaging/Example/Podfile b/InAppMessaging/Example/Podfile index b4b0f26b64a..3931594447b 100644 --- a/InAppMessaging/Example/Podfile +++ b/InAppMessaging/Example/Podfile @@ -1,9 +1,11 @@ use_frameworks! # Uncomment the next two lines for pre-release testing on public repo -# source 'https://github.com/Firebase/SpecsStaging.git' -# source 'https://github.com/CocoaPods/Specs.git' +source 'https://github.com/Firebase/SpecsStaging.git' +source 'https://github.com/CocoaPods/Specs.git' +# Remove version from FirebaseAnalytics after 6.0 goes public. +pod 'FirebaseAnalytics', '6.0.0-pre' pod 'FirebaseCore', :path => '../..' target 'InAppMessaging_Example_iOS' do @@ -13,13 +15,14 @@ target 'InAppMessaging_Example_iOS' do pod 'FirebaseInAppMessaging', :path => '../..' pod 'FirebaseAnalyticsInterop', :path => '../..' pod 'FirebaseDynamicLinks', :path => '../..' + pod 'FirebaseInstanceID', :path => '../..' end target 'InAppMessaging_Tests_iOS' do platform :ios, '8.0' pod 'FirebaseInAppMessaging', :path => '../..' - pod 'FirebaseInstanceID' + pod 'FirebaseInstanceID', :path => '../..' pod 'FirebaseAnalyticsInterop', :path => '../..' pod 'OCMock' end diff --git a/InAppMessagingDisplay/Example/Podfile b/InAppMessagingDisplay/Example/Podfile index 239b3e94570..6ee7a4020e0 100644 --- a/InAppMessagingDisplay/Example/Podfile +++ b/InAppMessagingDisplay/Example/Podfile @@ -1,6 +1,11 @@ +# Uncomment the next two lines for pre-release testing on public repo +source 'https://github.com/Firebase/SpecsStaging.git' +source 'https://github.com/CocoaPods/Specs.git' use_frameworks! +pod 'FirebaseCore', :path => '../..' + target 'FiamDisplaySwiftExample' do platform :ios, '8.0' pod 'FirebaseInAppMessagingDisplay', :path => '../..' From d5cb5f7f985111ce4d65865f5c3bd0d80d5d45d3 Mon Sep 17 00:00:00 2001 From: dmandar Date: Mon, 8 Apr 2019 21:50:19 -0700 Subject: [PATCH 172/214] Remove deprecated API in FDLURLComponents. (#2768) * Remove deprecated API in FDLURLComponents. * Fix style. --- .../Tests/FDLURLComponentsTests.m | 82 +------------------ .../DynamicLinks/Public/FDLURLComponents.h | 32 -------- 2 files changed, 4 insertions(+), 110 deletions(-) diff --git a/Example/DynamicLinks/Tests/FDLURLComponentsTests.m b/Example/DynamicLinks/Tests/FDLURLComponentsTests.m index dee0874c6f5..e9d339a4c87 100644 --- a/Example/DynamicLinks/Tests/FDLURLComponentsTests.m +++ b/Example/DynamicLinks/Tests/FDLURLComponentsTests.m @@ -528,7 +528,7 @@ - (void)testFDLComponentsNotNilOnDomainWithHTTPScheme { NSURL *link = [NSURL URLWithString:linkString]; FIRDynamicLinkComponents *components = - [FIRDynamicLinkComponents componentsWithLink:link domain:@"http://xyz.page.link"]; + [FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:@"https://xyz.page.link"]; XCTAssertNotNil(components); } @@ -538,7 +538,7 @@ - (void)testFDLComponentsNotNilOnDomainWithHTTPSScheme { NSURL *link = [NSURL URLWithString:linkString]; FIRDynamicLinkComponents *components = - [FIRDynamicLinkComponents componentsWithLink:link domain:@"https://xyz.page.link"]; + [FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:@"https://xyz.page.link"]; XCTAssertNotNil(components); } @@ -549,10 +549,9 @@ - (void)testFDLComponentsFailsOnMalformedDomain { FIRDynamicLinkComponents *components = [FIRDynamicLinkComponents componentsWithLink:link - domain:@"this is invalid domain URI Prefix"]; + domainURIPrefix:@"this is invalid domain URI Prefix"]; - XCTAssertNotNil(components); - XCTAssertNil(components.url); + XCTAssertNil(components); } - (void)testFDLComponentsCreatesFullLinkCorrectly { @@ -708,79 +707,6 @@ - (void)testShortenURL { [componentsClassMock stopMocking]; } -- (void)testDeprecatedMethodComponentsWithLinkForDomain { - NSString *shortURLString = @"https://xyz.page.link/abcd"; - - // Mock key provider - id keyProviderClassMock = OCMClassMock([FIRDynamicLinkComponentsKeyProvider class]); - [[[keyProviderClassMock expect] andReturn:@"fake-api-key"] APIKey]; - - id componentsClassMock = OCMClassMock([FIRDynamicLinkComponents class]); - [[componentsClassMock expect] - sendHTTPRequest:OCMOCK_ANY - completion:[OCMArg checkWithBlock:^BOOL(id obj) { - void (^completion)(NSData *_Nullable, NSError *_Nullable) = obj; - NSDictionary *JSON = @{@"shortLink" : shortURLString}; - NSData *JSONData = [NSJSONSerialization dataWithJSONObject:JSON options:0 error:0]; - completion(JSONData, nil); - return YES; - }]]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; - NSURL *link = [NSURL URLWithString:@"https://google.com/abc"]; - FIRDynamicLinkComponents *components = - [FIRDynamicLinkComponents componentsWithLink:link domain:@"xyz.page.link"]; - [components - shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray *_Nullable warnings, - NSError *_Nullable error) { - XCTAssertEqualObjects(shortURL.absoluteString, shortURLString); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:0.1 handler:nil]; - - [keyProviderClassMock verify]; - [keyProviderClassMock stopMocking]; - [componentsClassMock verify]; - [componentsClassMock stopMocking]; -} - -- (void)testDeprecatedMethodComponentsWithLinkForDomainWithInvalidDomainScheme { - NSString *shortURLString = @"https://xyz.page.link/abcd"; - - // Mock key provider - id keyProviderClassMock = OCMClassMock([FIRDynamicLinkComponentsKeyProvider class]); - [[[keyProviderClassMock expect] andReturn:@"fake-api-key"] APIKey]; - - id componentsClassMock = OCMClassMock([FIRDynamicLinkComponents class]); - [[componentsClassMock expect] - sendHTTPRequest:OCMOCK_ANY - completion:[OCMArg checkWithBlock:^BOOL(id obj) { - void (^completion)(NSData *_Nullable, NSError *_Nullable) = obj; - NSDictionary *JSON = @{@"shortLink" : shortURLString}; - NSData *JSONData = [NSJSONSerialization dataWithJSONObject:JSON options:0 error:0]; - completion(JSONData, nil); - return YES; - }]]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; - NSURL *link = [NSURL URLWithString:@"https://google.com/abc"]; - FIRDynamicLinkComponents *components = - [FIRDynamicLinkComponents componentsWithLink:link domain:@"http://xyz.page.link"]; - XCTAssertNotNil(components); - [components - shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray *_Nullable warnings, - NSError *_Nullable error) { - XCTAssertEqualObjects(shortURL.absoluteString, shortURLString); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:0.1 handler:nil]; - - [keyProviderClassMock verify]; - [keyProviderClassMock stopMocking]; - [componentsClassMock verify]; - [componentsClassMock stopMocking]; -} - - (void)testShortenURLReturnsErrorWhenAPIKeyMissing { NSString *shortURLString = @"https://xyz.page.link/abcd"; diff --git a/Firebase/DynamicLinks/Public/FDLURLComponents.h b/Firebase/DynamicLinks/Public/FDLURLComponents.h index 1cc8397560c..3177859c55e 100644 --- a/Firebase/DynamicLinks/Public/FDLURLComponents.h +++ b/Firebase/DynamicLinks/Public/FDLURLComponents.h @@ -505,38 +505,6 @@ FIR_SWIFT_NAME(DynamicLinkComponents) */ @property(nonatomic, nullable, readonly) NSURL *url; -/** - * @method componentsWithLink:domain: - * @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters - * set to generate a fully-functional Dynamic Link. - * @param link Deep link to be stored in created Dynamic link. This link also called "payload" of - * the Dynamic link. - * @param domain Domain of your App. This value must be equal to your assigned domain from Firebase - * Console. (e.g. xyz.page.link). Note that the domain scheme is required to be https and is - * assumed as such by this API. - */ -+ (instancetype)componentsWithLink:(NSURL *)link - domain:(NSString *)domain - NS_SWIFT_UNAVAILABLE("Use init(link:domain:)")DEPRECATED_MSG_ATTRIBUTE( - "This method is deprecated. Please use the new method with support for " - "domainURIPrefix- init(link:domainURIPrefix:)."); - -/** - * @method initWithLink:domain: - * @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters - * set to generate a fully-functional Dynamic Link. - * @param link Deep link to be stored in created Dynamic link. This link also called "payload" of - * the Dynamic link. - * @param domain Domain of your App. This value must be equal to your assigned domain from Firebase - * Console. (e.g. xyz.page.link). Note that the domain scheme is required to be https and is - * assumed as such by this API. - */ -- (instancetype)initWithLink:(NSURL *)link - domain:(NSString *)domain - DEPRECATED_MSG_ATTRIBUTE( - "This method is deprecated. Please use the new method with support for " - "domainURIPrefix- init(link:domainURIPrefix:)."); - /** * @method componentsWithLink:domainURIPrefix: * @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters From edd4bd719ca3202816624059d1371e65c714a1a5 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 9 Apr 2019 07:07:20 -0700 Subject: [PATCH 173/214] Port FIRFirestoreSource to C++ Source (#2749) --- Firestore/Source/API/FIRDocumentReference.mm | 10 +++-- .../Source/API/FIRFirestoreSource+Internal.h | 29 +++++++++++++ Firestore/Source/API/FIRFirestoreSource.mm | 41 +++++++++++++++++++ Firestore/Source/API/FIRQuery.mm | 30 +++++++------- Firestore/Source/Core/FSTFirestoreClient.mm | 2 +- .../firestore/api/document_reference.h | 6 +-- .../firestore/api/document_reference.mm | 14 +++---- .../core/src/firebase/firestore/api/source.h | 39 ++++++++++++++++++ 8 files changed, 141 insertions(+), 30 deletions(-) create mode 100644 Firestore/Source/API/FIRFirestoreSource+Internal.h create mode 100644 Firestore/Source/API/FIRFirestoreSource.mm create mode 100644 Firestore/core/src/firebase/firestore/api/source.h diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index 501c94665b6..84c9a2b9ac4 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -20,11 +20,11 @@ #include #import "FIRFirestoreErrors.h" -#import "FIRFirestoreSource.h" #import "Firestore/Source/API/FIRCollectionReference+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/API/FIRFirestoreSource+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" #import "Firestore/Source/Core/FSTEventManager.h" @@ -34,6 +34,7 @@ #include "Firestore/core/src/firebase/firestore/api/document_reference.h" #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/api/input_validation.h" +#include "Firestore/core/src/firebase/firestore/api/source.h" #include "Firestore/core/src/firebase/firestore/core/event_listener.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" @@ -49,6 +50,8 @@ using firebase::firestore::api::DocumentReference; using firebase::firestore::api::DocumentSnapshot; using firebase::firestore::api::Firestore; +using firebase::firestore::api::Source; +using firebase::firestore::api::MakeSource; using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::EventListener; using firebase::firestore::core::ListenOptions; @@ -192,13 +195,12 @@ - (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error } - (void)getDocumentWithCompletion:(FIRDocumentSnapshotBlock)completion { - _documentReference.GetDocument(FIRFirestoreSourceDefault, - [self wrapDocumentSnapshotBlock:completion]); + _documentReference.GetDocument(Source::Default, [self wrapDocumentSnapshotBlock:completion]); } - (void)getDocumentWithSource:(FIRFirestoreSource)source completion:(FIRDocumentSnapshotBlock)completion { - _documentReference.GetDocument(source, [self wrapDocumentSnapshotBlock:completion]); + _documentReference.GetDocument(MakeSource(source), [self wrapDocumentSnapshotBlock:completion]); } - (id)addSnapshotListener:(FIRDocumentSnapshotBlock)listener { diff --git a/Firestore/Source/API/FIRFirestoreSource+Internal.h b/Firestore/Source/API/FIRFirestoreSource+Internal.h new file mode 100644 index 00000000000..694a7de0b99 --- /dev/null +++ b/Firestore/Source/API/FIRFirestoreSource+Internal.h @@ -0,0 +1,29 @@ +/* + * Copyright 2019 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 "FIRFirestoreSource.h" + +#include "Firestore/core/src/firebase/firestore/api/source.h" + +namespace firebase { +namespace firestore { +namespace api { + +Source MakeSource(FIRFirestoreSource source); + +} // namespace api +} // namespace firestore +} // namespace firebase diff --git a/Firestore/Source/API/FIRFirestoreSource.mm b/Firestore/Source/API/FIRFirestoreSource.mm new file mode 100644 index 00000000000..9d3444a7a89 --- /dev/null +++ b/Firestore/Source/API/FIRFirestoreSource.mm @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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 "Firestore/Source/API/FIRFirestoreSource+Internal.h" + +#include "Firestore/core/src/firebase/firestore/api/source.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + +namespace firebase { +namespace firestore { +namespace api { + +Source MakeSource(FIRFirestoreSource source) { + switch (source) { + case FIRFirestoreSourceDefault: + return Source::Default; + case FIRFirestoreSourceServer: + return Source::Server; + case FIRFirestoreSourceCache: + return Source::Cache; + } + + UNREACHABLE(); +} + +} // namespace api +} // namespace firestore +} // namespace firebase diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index affb6d68e5a..1efd60a9a31 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -21,12 +21,12 @@ #import "FIRDocumentReference.h" #import "FIRFirestoreErrors.h" -#import "FIRFirestoreSource.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFieldPath+Internal.h" #import "Firestore/Source/API/FIRFieldValue+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/API/FIRFirestoreSource+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" @@ -50,6 +50,8 @@ #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; +using firebase::firestore::api::MakeSource; +using firebase::firestore::api::Source; using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::AsyncEventListener; using firebase::firestore::core::EventListener; @@ -114,10 +116,11 @@ - (void)getDocumentsWithCompletion:(void (^)(FIRQuerySnapshot *_Nullable snapsho [self getDocumentsWithSource:FIRFirestoreSourceDefault completion:completion]; } -- (void)getDocumentsWithSource:(FIRFirestoreSource)source +- (void)getDocumentsWithSource:(FIRFirestoreSource)publicSource completion:(void (^)(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error))completion { - if (source == FIRFirestoreSourceCache) { + Source source = MakeSource(publicSource); + if (source == Source::Cache) { [self.firestore.client getDocumentsFromLocalCache:self completion:completion]; return; } @@ -140,17 +143,16 @@ ListenOptions listenOptions( dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER); [listenerRegistration remove]; - if (snapshot.metadata.fromCache && source == FIRFirestoreSourceServer) { - completion(nil, - [NSError errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeUnavailable - userInfo:@{ - NSLocalizedDescriptionKey : - @"Failed to get documents from server. (However, these " - @"documents may exist in the local cache. Run again " - @"without setting source to FIRFirestoreSourceServer to " - @"retrieve the cached documents.)" - }]); + if (snapshot.metadata.fromCache && source == Source::Server) { + completion(nil, [NSError errorWithDomain:FIRFirestoreErrorDomain + code:FIRFirestoreErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to get documents from server. (However, these " + @"documents may exist in the local cache. Run again " + @"without setting source to FirestoreSourceServer to " + @"retrieve the cached documents.)" + }]); } else { completion(snapshot, nil); } diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index de03ebe8b7c..495e021910a 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -345,7 +345,7 @@ - (void)getDocumentFromLocalCache:(const DocumentReference &)doc maybe_snapshot = Status{FirestoreErrorCode::Unavailable, "Failed to get document from cache. (However, this document " "may exist on the server. Run again without setting source to " - "FIRFirestoreSourceCache to attempt to retrieve the document "}; + "FirestoreSourceCache to attempt to retrieve the document "}; } if (shared_completion) { diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.h b/Firestore/core/src/firebase/firestore/api/document_reference.h index f9da6004cf6..01c0121c4c4 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.h +++ b/Firestore/core/src/firebase/firestore/api/document_reference.h @@ -27,8 +27,6 @@ #include #include -#import "FIRFirestoreSource.h" - #include "Firestore/core/src/firebase/firestore/api/document_snapshot.h" #include "Firestore/core/src/firebase/firestore/api/listener_registration.h" #include "Firestore/core/src/firebase/firestore/core/listen_options.h" @@ -45,6 +43,7 @@ namespace firestore { namespace api { class Firestore; +enum class Source; class DocumentReference { public: @@ -82,8 +81,7 @@ class DocumentReference { void DeleteDocument(Completion completion); - void GetDocument(FIRFirestoreSource source, - DocumentSnapshot::Listener&& completion); + void GetDocument(Source source, DocumentSnapshot::Listener&& completion); ListenerRegistration AddSnapshotListener( core::ListenOptions options, DocumentSnapshot::Listener&& listener); diff --git a/Firestore/core/src/firebase/firestore/api/document_reference.mm b/Firestore/core/src/firebase/firestore/api/document_reference.mm index fdd8993b67a..54598019e2c 100644 --- a/Firestore/core/src/firebase/firestore/api/document_reference.mm +++ b/Firestore/core/src/firebase/firestore/api/document_reference.mm @@ -27,6 +27,7 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTMutation.h" +#include "Firestore/core/src/firebase/firestore/api/source.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" @@ -113,9 +114,9 @@ [firestore_->client() writeMutations:{mutation} completion:completion]; } -void DocumentReference::GetDocument(FIRFirestoreSource source, +void DocumentReference::GetDocument(Source source, DocumentSnapshot::Listener&& completion) { - if (source == FIRFirestoreSourceCache) { + if (source == Source::Cache) { [firestore_->client() getDocumentFromLocalCache:*this completion:std::move(completion)]; return; @@ -128,8 +129,7 @@ ListenOptions options( class ListenOnce : public EventListener { public: - ListenOnce(FIRFirestoreSource source, - DocumentSnapshot::Listener&& completion) + ListenOnce(Source source, DocumentSnapshot::Listener&& completion) : source_(source), completion_(std::move(completion)) { } @@ -160,13 +160,13 @@ void OnEvent(StatusOr maybe_snapshot) override { Status{FirestoreErrorCode::Unavailable, "Failed to get document because the client is offline."}); } else if (snapshot.exists() && snapshot.metadata().from_cache() && - source_ == FIRFirestoreSourceServer) { + source_ == Source::Server) { completion_->OnEvent( Status{FirestoreErrorCode::Unavailable, "Failed to get document from server. (However, " "this document does exist in the local cache. Run " "again without setting source to " - "FIRFirestoreSourceServer to retrieve the cached " + "FirestoreSourceServer to retrieve the cached " "document.)"}); } else { completion_->OnEvent(std::move(snapshot)); @@ -178,7 +178,7 @@ void Resolve(ListenerRegistration&& registration) { } private: - FIRFirestoreSource source_; + Source source_; DocumentSnapshot::Listener completion_; std::promise registration_promise_; diff --git a/Firestore/core/src/firebase/firestore/api/source.h b/Firestore/core/src/firebase/firestore/api/source.h new file mode 100644 index 00000000000..be1ec0de9fa --- /dev/null +++ b/Firestore/core/src/firebase/firestore/api/source.h @@ -0,0 +1,39 @@ +/* + * Copyright 2019 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_API_SOURCE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_SOURCE_H_ + +namespace firebase { +namespace firestore { +namespace api { + +/** + * An enum that configures the behavior of `DocumentReference.GetDocument()` and + * `Query.GetDocuments()`. By providing a source enum the `GetDocument[s]` + * methods can be configured to fetch results only from the server, only from + * the local cache, or attempt to fetch results from the server and fall back to + * the cache (which is the default). + * + * See `FIRFirestoreSource` for more details. + */ +enum class Source { Default, Server, Cache }; + +} // namespace api +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_API_SOURCE_H_ From 423e3741fdbfa0c1279b2ece1c465db0b221f9b8 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Tue, 9 Apr 2019 10:47:49 -0400 Subject: [PATCH 174/214] FirebaseCore changelog - cocoapods 1.7.0 (#2776) --- Firebase/Core/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index 337d91a7a55..e3aadc99cbe 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- [changed] Added support for new CocoaPods 1.7.x `:generate_multiple_pod_projects` feature (#2751) + # 2019-04-02 -- v5.4.1 -- M46 - [changed] Avoid using NSRegularExpression in FIRApp. - [changed] Improve error meessage for invalid app names. (#2614) From f3584909ce625b37483b9cc1c8ff8924cdeffd06 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 9 Apr 2019 09:39:09 -0700 Subject: [PATCH 175/214] Use NSCachesDirectory for database storage on tvOS (#2777) Use of NSDocumentsDirectory (or NSApplicationSupportDirectory) is forbidden there. Fixes #2735. --- Firestore/Source/Local/FSTLevelDB.mm | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index 478e68ab4e5..46cb63fadfe 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -395,19 +395,22 @@ - (size_t)byteSize { } + (Path)documentsDirectory { -#if TARGET_OS_IPHONE +#if TARGET_OS_IOS NSArray *directories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); return Path::FromNSString(directories[0]).AppendUtf8(kReservedPathComponent); +#elif TARGET_OS_TV + NSArray *directories = + NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + return Path::FromNSString(directories[0]).AppendUtf8(kReservedPathComponent); + #elif TARGET_OS_OSX std::string dotPrefixed = absl::StrCat(".", kReservedPathComponent); return Path::FromNSString(NSHomeDirectory()).AppendUtf8(dotPrefixed); #else -#error "local storage on tvOS" - // TODO(mcg): Writing to NSDocumentsDirectory on tvOS will fail; we need to write to Caches - // https://developer.apple.com/library/content/documentation/General/Conceptual/AppleTV_PG/ +#error "Don't know where to store documents on this platform." #endif } From 0f093fb75313dd3b95f784515fa27b0b12cf64f6 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 9 Apr 2019 10:20:34 -0700 Subject: [PATCH 176/214] Undeprecate +[FIRFirestore enableLogging:] (#2773) --- Firestore/Source/Public/FIRFirestore.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Firestore/Source/Public/FIRFirestore.h b/Firestore/Source/Public/FIRFirestore.h index cd9302c4cd3..f8a465e2ff1 100644 --- a/Firestore/Source/Public/FIRFirestore.h +++ b/Firestore/Source/Public/FIRFirestore.h @@ -137,9 +137,7 @@ NS_SWIFT_NAME(Firestore) #pragma mark - Logging /** Enables or disables logging from the Firestore client. */ -+ (void)enableLogging:(BOOL)logging - DEPRECATED_MSG_ATTRIBUTE("Use FirebaseConfiguration.shared.setLoggerLevel(.debug) to enable " - "logging."); ++ (void)enableLogging:(BOOL)logging; #pragma mark - Network From 65c7c9f6bd79b2ed60f3650325a44886ffd0f1d7 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 9 Apr 2019 12:54:45 -0700 Subject: [PATCH 177/214] M47 FirebaseCore release notes (#2779) --- Firebase/Core/CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index e3aadc99cbe..bdfee8569f2 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,5 +1,7 @@ -# Unreleased -- [changed] Added support for new CocoaPods 1.7.x `:generate_multiple_pod_projects` feature (#2751) +# v6.0.0 -- M47 +- [changed] Added support for CocoaPods 1.7.x `:generate_multiple_pod_projects` feature. (#2751) +- [removed] Remove FIRAnalyticsConfiguration from Public header. Use from FirebaseAnalytics. (#2728) +- [changed] Remove runtime warning for missing analytics in favor of one at build time. (#2734) # 2019-04-02 -- v5.4.1 -- M46 - [changed] Avoid using NSRegularExpression in FIRApp. From 384ccd81a9be62b645ee485cd53f3c3ca0359f97 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Tue, 9 Apr 2019 13:27:00 -0700 Subject: [PATCH 178/214] Move input validation logic from FIRFieldPath to model::FieldPath. (#2727) This will allow it to be shared with the (eventual) C++ FieldPath Public API. --- Firestore/Source/API/FIRFieldPath.mm | 20 ++------------- .../firebase/firestore/model/CMakeLists.txt | 1 + .../firebase/firestore/model/field_path.cc | 25 +++++++++++++++++++ .../src/firebase/firestore/model/field_path.h | 24 ++++++++++++++++++ 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/Firestore/Source/API/FIRFieldPath.mm b/Firestore/Source/API/FIRFieldPath.mm index 9ffbc3ea60f..910811ad6ca 100644 --- a/Firestore/Source/API/FIRFieldPath.mm +++ b/Firestore/Source/API/FIRFieldPath.mm @@ -51,9 +51,6 @@ - (instancetype)initWithFields:(NSArray *)fieldNames { std::vector field_names; field_names.reserve(fieldNames.count); for (int i = 0; i < fieldNames.count; ++i) { - if (fieldNames[i].length == 0) { - ThrowInvalidArgument("Invalid field name at index %s. Field names must not be empty.", i); - } field_names.emplace_back(util::MakeString(fieldNames[i])); } @@ -72,21 +69,8 @@ - (instancetype)initPrivate:(FieldPath)fieldPath { } + (instancetype)pathWithDotSeparatedString:(NSString *)path { - if ([[FIRFieldPath reservedCharactersRegex] - numberOfMatchesInString:path - options:0 - range:NSMakeRange(0, path.length)] > 0) { - ThrowInvalidArgument( - "Invalid field path (%s). Paths must not contain '~', '*', '/', '[', or ']'", path); - } - @try { - return [[FIRFieldPath alloc] initWithFields:[path componentsSeparatedByString:@"."]]; - } @catch (NSException *exception) { - ThrowInvalidArgument( - "Invalid field path (%s). Paths must not be empty, begin with '.', end with '.', or " - "contain '..'", - path); - } + return + [[FIRFieldPath alloc] initPrivate:FieldPath::FromDotSeparatedString(util::MakeString(path))]; } /** Matches any characters in a field path string that are reserved. */ diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt index 170bf8e83a3..c83dd12b726 100644 --- a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt @@ -50,6 +50,7 @@ cc_library( DEPENDS absl_optional absl_strings + firebase_firestore_api_input_validation firebase_firestore_immutable firebase_firestore_util firebase_firestore_types diff --git a/Firestore/core/src/firebase/firestore/model/field_path.cc b/Firestore/core/src/firebase/firestore/model/field_path.cc index b1dcad75a65..1bec0e0c457 100644 --- a/Firestore/core/src/firebase/firestore/model/field_path.cc +++ b/Firestore/core/src/firebase/firestore/model/field_path.cc @@ -19,6 +19,7 @@ #include #include +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" @@ -28,6 +29,8 @@ namespace firebase { namespace firestore { namespace model { +using api::ThrowInvalidArgument; + namespace { /** @@ -78,6 +81,28 @@ struct JoinEscaped { }; } // namespace +FieldPath FieldPath::FromDotSeparatedString(absl::string_view path) { + if (path.find_first_of("~*/[]") != absl::string_view::npos) { + ThrowInvalidArgument( + "Invalid field path (%s). Paths must not contain '~', '*', '/', '[', " + "or ']'", + path); + } + + SegmentsT segments = + absl::StrSplit(path, '.', [path](absl::string_view segment) { + if (segment.empty()) { + ThrowInvalidArgument( + "Invalid field path (%s). Paths must not be empty, begin with " + "'.', end with '.', or contain '..'", + path); + } + return true; + }); + + return FieldPath{std::move(segments)}; +} + FieldPath FieldPath::FromServerFormat(const absl::string_view path) { SegmentsT segments; std::string segment; diff --git a/Firestore/core/src/firebase/firestore/model/field_path.h b/Firestore/core/src/firebase/firestore/model/field_path.h index ce3527d920c..c0c55d131cd 100644 --- a/Firestore/core/src/firebase/firestore/model/field_path.h +++ b/Firestore/core/src/firebase/firestore/model/field_path.h @@ -21,6 +21,7 @@ #include #include +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/model/base_path.h" #include "absl/strings/string_view.h" @@ -47,10 +48,33 @@ class FieldPath : public impl::BasePath { FieldPath(const IterT begin, const IterT end) : BasePath{begin, end} { } FieldPath(std::initializer_list list) : BasePath{list} { + // PORTING NOTE: We do API Validation in the model class to avoid needing + // to define a tiny api::FieldPath wrapper. + if (empty()) { + api::ThrowInvalidArgument( + "Invalid field path. Provided names must not be empty."); + } + + for (size_t i = 0; i < size(); i++) { + if ((*this)[i].empty()) { + api::ThrowInvalidArgument( + "Invalid field name at index %s. Field names must not be empty.", + i); + } + } } explicit FieldPath(SegmentsT&& segments) : BasePath{std::move(segments)} { } + /** + * Creates and returns a new path from a dot-separated field-path string, + * where path segments are separated by a dot ".". + * + * PORTING NOTE: We define this on the model class to avoid having a tiny + * api::FieldPath wrapper class. + */ + static FieldPath FromDotSeparatedString(absl::string_view path); + /** * Creates and returns a new path from the server formatted field-path string, * where path segments are separated by a dot "." and optionally encoded using From eca03aafd3c0e3d4798dab809c5004b8b5895433 Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 9 Apr 2019 14:43:35 -0700 Subject: [PATCH 179/214] Update CHANGELOG for Firestore v1.3.0 (#2785) --- Firestore/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 953a791eb32..b457ae11627 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,4 +1,6 @@ # Unreleased + +# 1.3.0 - [feature] Added community support for tvOS. # 1.2.1 From 8ab26dd6514f751b43f5aed532b0dbc533f085f6 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 9 Apr 2019 15:54:09 -0700 Subject: [PATCH 180/214] remove test header from source code (#2783) --- .../Tests/FIRInstanceIDResultTest.m | 5 ++- Example/InstanceID/Tests/FIRInstanceIDTest.m | 13 +++++- Firebase/InstanceID/FIRInstanceID+Testing.h | 42 ------------------- 3 files changed, 16 insertions(+), 44 deletions(-) delete mode 100644 Firebase/InstanceID/FIRInstanceID+Testing.h diff --git a/Example/InstanceID/Tests/FIRInstanceIDResultTest.m b/Example/InstanceID/Tests/FIRInstanceIDResultTest.m index 64e7ff84e07..65cbd7ceec7 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDResultTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDResultTest.m @@ -16,8 +16,9 @@ #import +#import #import -#import "Firebase/InstanceID/FIRInstanceID+Testing.h" + #import "Firebase/InstanceID/NSError+FIRInstanceID.h" static NSString *const kFakeIID = @"fE1e1PZJFSQ"; @@ -29,6 +30,8 @@ @interface FIRInstanceID (ExposedForTest) - (NSString *)cachedTokenIfAvailable; - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler; +- (instancetype)initPrivately; +- (void)start; @end @interface FIRInstanceIDResultTest : XCTestCase { diff --git a/Example/InstanceID/Tests/FIRInstanceIDTest.m b/Example/InstanceID/Tests/FIRInstanceIDTest.m index 498c0926e13..ebb072e0d5a 100644 --- a/Example/InstanceID/Tests/FIRInstanceIDTest.m +++ b/Example/InstanceID/Tests/FIRInstanceIDTest.m @@ -21,11 +21,11 @@ #import #import -#import "Firebase/InstanceID/FIRInstanceID+Testing.h" #import "Firebase/InstanceID/FIRInstanceIDAuthService.h" #import "Firebase/InstanceID/FIRInstanceIDCheckinPreferences+Internal.h" #import "Firebase/InstanceID/FIRInstanceIDConstants.h" #import "Firebase/InstanceID/FIRInstanceIDKeyPair.h" +#import "Firebase/InstanceID/FIRInstanceIDKeyPairStore.h" #import "Firebase/InstanceID/FIRInstanceIDTokenInfo.h" #import "Firebase/InstanceID/FIRInstanceIDTokenManager.h" #import "Firebase/InstanceID/FIRInstanceIDUtilities.h" @@ -47,6 +47,11 @@ static NSString *const kGoogleAppID = @"1:123:ios:123abc"; @interface FIRInstanceID (ExposedForTest) + +@property(nonatomic, readwrite, strong) FIRInstanceIDTokenManager *tokenManager; +@property(nonatomic, readwrite, strong) FIRInstanceIDKeyPairStore *keyPairStore; +@property(nonatomic, readwrite, copy) NSString *fcmSenderID; + - (NSInteger)retryIntervalToFetchDefaultToken; - (BOOL)isFCMAutoInitEnabled; - (void)didCompleteConfigure; @@ -54,6 +59,12 @@ - (NSString *)cachedTokenIfAvailable; - (void)deleteIdentityWithHandler:(FIRInstanceIDDeleteHandler)handler; + (FIRInstanceID *)instanceIDForTests; - (void)defaultTokenWithHandler:(FIRInstanceIDTokenHandler)handler; +- (instancetype)initPrivately; +- (void)start; ++ (int64_t)maxRetryCountForDefaultToken; ++ (int64_t)minIntervalForDefaultTokenRetry; ++ (int64_t)maxRetryIntervalForDefaultTokenInSeconds; + @end @interface FIRInstanceIDTest : XCTestCase diff --git a/Firebase/InstanceID/FIRInstanceID+Testing.h b/Firebase/InstanceID/FIRInstanceID+Testing.h deleted file mode 100644 index ba24926fc4f..00000000000 --- a/Firebase/InstanceID/FIRInstanceID+Testing.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2019 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 "FIRInstanceID+Private.h" -#import "FIRInstanceID.h" -#import "FIRInstanceIDKeyPairStore.h" -#import "FIRInstanceIDTokenManager.h" - -@interface FIRInstanceID (Testing) - -@property(nonatomic, readwrite, strong) FIRInstanceIDTokenManager *tokenManager; -@property(nonatomic, readwrite, strong) FIRInstanceIDKeyPairStore *keyPairStore; -@property(nonatomic, readwrite, copy) NSString *fcmSenderID; - -/** - * Private initializer. - */ -- (instancetype)initPrivately; - -/** - * Actually makes InstanceID instantiate both the IID and Token-related subsystems. - */ -- (void)start; - -+ (int64_t)maxRetryCountForDefaultToken; -+ (int64_t)minIntervalForDefaultTokenRetry; -+ (int64_t)maxRetryIntervalForDefaultTokenInSeconds; - -@end From 7e6cc0ddd517d9efa28843df995424864df0c3d7 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Tue, 9 Apr 2019 15:59:06 -0700 Subject: [PATCH 181/214] Revert "Move input validation logic from FIRFieldPath to model::FieldPath. (#2727)" (#2789) This reverts commit 384ccd81a9be62b645ee485cd53f3c3ca0359f97 due to broken integration tests. --- Firestore/Source/API/FIRFieldPath.mm | 20 +++++++++++++-- .../firebase/firestore/model/CMakeLists.txt | 1 - .../firebase/firestore/model/field_path.cc | 25 ------------------- .../src/firebase/firestore/model/field_path.h | 24 ------------------ 4 files changed, 18 insertions(+), 52 deletions(-) diff --git a/Firestore/Source/API/FIRFieldPath.mm b/Firestore/Source/API/FIRFieldPath.mm index 910811ad6ca..9ffbc3ea60f 100644 --- a/Firestore/Source/API/FIRFieldPath.mm +++ b/Firestore/Source/API/FIRFieldPath.mm @@ -51,6 +51,9 @@ - (instancetype)initWithFields:(NSArray *)fieldNames { std::vector field_names; field_names.reserve(fieldNames.count); for (int i = 0; i < fieldNames.count; ++i) { + if (fieldNames[i].length == 0) { + ThrowInvalidArgument("Invalid field name at index %s. Field names must not be empty.", i); + } field_names.emplace_back(util::MakeString(fieldNames[i])); } @@ -69,8 +72,21 @@ - (instancetype)initPrivate:(FieldPath)fieldPath { } + (instancetype)pathWithDotSeparatedString:(NSString *)path { - return - [[FIRFieldPath alloc] initPrivate:FieldPath::FromDotSeparatedString(util::MakeString(path))]; + if ([[FIRFieldPath reservedCharactersRegex] + numberOfMatchesInString:path + options:0 + range:NSMakeRange(0, path.length)] > 0) { + ThrowInvalidArgument( + "Invalid field path (%s). Paths must not contain '~', '*', '/', '[', or ']'", path); + } + @try { + return [[FIRFieldPath alloc] initWithFields:[path componentsSeparatedByString:@"."]]; + } @catch (NSException *exception) { + ThrowInvalidArgument( + "Invalid field path (%s). Paths must not be empty, begin with '.', end with '.', or " + "contain '..'", + path); + } } /** Matches any characters in a field path string that are reserved. */ diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt index c83dd12b726..170bf8e83a3 100644 --- a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt @@ -50,7 +50,6 @@ cc_library( DEPENDS absl_optional absl_strings - firebase_firestore_api_input_validation firebase_firestore_immutable firebase_firestore_util firebase_firestore_types diff --git a/Firestore/core/src/firebase/firestore/model/field_path.cc b/Firestore/core/src/firebase/firestore/model/field_path.cc index 1bec0e0c457..b1dcad75a65 100644 --- a/Firestore/core/src/firebase/firestore/model/field_path.cc +++ b/Firestore/core/src/firebase/firestore/model/field_path.cc @@ -19,7 +19,6 @@ #include #include -#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" @@ -29,8 +28,6 @@ namespace firebase { namespace firestore { namespace model { -using api::ThrowInvalidArgument; - namespace { /** @@ -81,28 +78,6 @@ struct JoinEscaped { }; } // namespace -FieldPath FieldPath::FromDotSeparatedString(absl::string_view path) { - if (path.find_first_of("~*/[]") != absl::string_view::npos) { - ThrowInvalidArgument( - "Invalid field path (%s). Paths must not contain '~', '*', '/', '[', " - "or ']'", - path); - } - - SegmentsT segments = - absl::StrSplit(path, '.', [path](absl::string_view segment) { - if (segment.empty()) { - ThrowInvalidArgument( - "Invalid field path (%s). Paths must not be empty, begin with " - "'.', end with '.', or contain '..'", - path); - } - return true; - }); - - return FieldPath{std::move(segments)}; -} - FieldPath FieldPath::FromServerFormat(const absl::string_view path) { SegmentsT segments; std::string segment; diff --git a/Firestore/core/src/firebase/firestore/model/field_path.h b/Firestore/core/src/firebase/firestore/model/field_path.h index c0c55d131cd..ce3527d920c 100644 --- a/Firestore/core/src/firebase/firestore/model/field_path.h +++ b/Firestore/core/src/firebase/firestore/model/field_path.h @@ -21,7 +21,6 @@ #include #include -#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/model/base_path.h" #include "absl/strings/string_view.h" @@ -48,33 +47,10 @@ class FieldPath : public impl::BasePath { FieldPath(const IterT begin, const IterT end) : BasePath{begin, end} { } FieldPath(std::initializer_list list) : BasePath{list} { - // PORTING NOTE: We do API Validation in the model class to avoid needing - // to define a tiny api::FieldPath wrapper. - if (empty()) { - api::ThrowInvalidArgument( - "Invalid field path. Provided names must not be empty."); - } - - for (size_t i = 0; i < size(); i++) { - if ((*this)[i].empty()) { - api::ThrowInvalidArgument( - "Invalid field name at index %s. Field names must not be empty.", - i); - } - } } explicit FieldPath(SegmentsT&& segments) : BasePath{std::move(segments)} { } - /** - * Creates and returns a new path from a dot-separated field-path string, - * where path segments are separated by a dot ".". - * - * PORTING NOTE: We define this on the model class to avoid having a tiny - * api::FieldPath wrapper class. - */ - static FieldPath FromDotSeparatedString(absl::string_view path); - /** * Creates and returns a new path from the server formatted field-path string, * where path segments are separated by a dot "." and optionally encoded using From 2935f29fac372c50205f39d541f5f156ea6110bb Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 9 Apr 2019 16:32:56 -0700 Subject: [PATCH 182/214] update CHANGELOG for Messaging and InstanceID (#2787) --- Firebase/InstanceID/CHANGELOG.md | 3 ++- Firebase/Messaging/CHANGELOG.md | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Firebase/InstanceID/CHANGELOG.md b/Firebase/InstanceID/CHANGELOG.md index 6b685626588..ed100bab4ba 100644 --- a/Firebase/InstanceID/CHANGELOG.md +++ b/Firebase/InstanceID/CHANGELOG.md @@ -1,4 +1,5 @@ -# Unreleased +# 2019-05-07 -- 4.0.0 +- Remove deprecated `token` method. Use `instanceIDWithHandler:` instead. (#2741) - Send `firebaseUserAgent` with a register request (#2679) # 2019-04-02 -- v3.8.1 diff --git a/Firebase/Messaging/CHANGELOG.md b/Firebase/Messaging/CHANGELOG.md index c3d93375c00..52fa7f057f9 100644 --- a/Firebase/Messaging/CHANGELOG.md +++ b/Firebase/Messaging/CHANGELOG.md @@ -1,5 +1,7 @@ -# Unreleased -- `GULAppDelegateSwizzler` is used for the app delegate swizzling (#2683) +# 2019-05-07 -- v4.0.0 +- Remove deprecated `useMessagingDelegateForDirectChannel` property.(#2711) All direct channels (non-APNS) messages will be handled by `messaging:didReceiveMessage:`. Previously in iOS 9 and below, the direct channel messages are handled in `application:didReceiveRemoteNotification:fetchCompletionHandler:` and this behavior can be changed by setting `useMessagingDelegateForDirectChannel` to true. Now that all messages by default are handled in `messaging:didReceiveMessage:`. This boolean value is no longer needed. If you already have set useMessagingDelegateForDirectChannel to YES, or handle all your direct channel messages in `messaging:didReceiveMessage:`. This change should not affect you. +- Remove deprecated API to connect direct channel. (#2717) Should use `shouldEstablishDirectChannel` property instead. +- `GULAppDelegateSwizzler` is used for the app delegate swizzling. (#2683) # 2019-04-02 -- v3.5.0 - Add image support for notification. (#2644) From 71accc48385bafeaffc5cb44495339f1f58b7d37 Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Tue, 9 Apr 2019 17:26:15 -0700 Subject: [PATCH 183/214] Update CHANGELOG.md (#2781) --- Firebase/Auth/CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md index 1a549506ae4..7abd4d7f0e4 100644 --- a/Firebase/Auth/CHANGELOG.md +++ b/Firebase/Auth/CHANGELOG.md @@ -1,5 +1,9 @@ -# Unreleased -- `GULAppDelegateSwizzler` is used for the app delegate swizzling (#2591) +# v6.0.0 +- Add support of single sign on. (#2684) +- Deprecate `reauthenticateAndRetrieveDataWithCredential:completion:`, `signInAndRetrieveDataWithCredential:completion:`, `linkAndRetrieveDataWithCredential:completion:`, `fetchProvidersForEmail:completion:`. (#2723, #2756) +- Implement oauth secret token in headful-lite. (#2663) +- Remove pendingToken from public API. (#2676) +- `GULAppDelegateSwizzler` is used for the app delegate swizzling. (#2591) # v5.4.2 - Support new error code ERROR_INVALID_PROVIDER_ID. (#2629) From 955ef6bb15e9f4a9d04aa10757fd8987f238ffe5 Mon Sep 17 00:00:00 2001 From: davidair Date: Tue, 9 Apr 2019 21:02:41 -0400 Subject: [PATCH 184/214] Adding missing LICENSE files (#2791) --- .../Database/third_party/SocketRocket/LICENSE | 53 +++++++++++++++++++ .../Database/third_party/Wrap-leveldb/LICENSE | 46 ++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 Firebase/Database/third_party/SocketRocket/LICENSE create mode 100644 Firebase/Database/third_party/Wrap-leveldb/LICENSE diff --git a/Firebase/Database/third_party/SocketRocket/LICENSE b/Firebase/Database/third_party/SocketRocket/LICENSE new file mode 100644 index 00000000000..1582810d62a --- /dev/null +++ b/Firebase/Database/third_party/SocketRocket/LICENSE @@ -0,0 +1,53 @@ +Copyright 2012 Square Inc. + +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. + +$OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ + +Copyright (c) 1996 by Internet Software Consortium. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +Portions Copyright (c) 1995 by International Business Machines, Inc. + +International Business Machines, Inc. (hereinafter called IBM) grants +permission under its copyrights to use, copy, modify, and distribute this +Software with or without fee, provided that the above copyright notice and +all paragraphs of this notice appear in all copies, and that the name of IBM +not be used in connection with the marketing of any product incorporating +the Software or modifications thereof, without specific, written prior +permission. + +To the extent it has a right to do so, IBM grants an immunity from suit +under its patents, if any, for the use, sale or manufacture of products to +the extent that such products are used for performing Domain Name System +dynamic updates in TCP/IP networks by means of the Software. No immunity is +granted for any product per se or for any other function of any product. + +THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, +DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING +OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN +IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/Firebase/Database/third_party/Wrap-leveldb/LICENSE b/Firebase/Database/third_party/Wrap-leveldb/LICENSE new file mode 100644 index 00000000000..eda4ff89e23 --- /dev/null +++ b/Firebase/Database/third_party/Wrap-leveldb/LICENSE @@ -0,0 +1,46 @@ +Created by Adam Preble on 1/23/12. +Copyright (c) 2012 Adam Preble. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +Portions of APLevelDB are based on LevelDB-ObjC: + https:github.com/hoisie/LevelDB-ObjC +Specifically the SliceFromString/StringFromSlice macros, and the structure of +the enumeration methods. License for those potions follows: + +Copyright (c) 2011 Pave Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From c0e4211849aa30347193b69a52bf24fe78766b42 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Tue, 9 Apr 2019 21:42:13 -0400 Subject: [PATCH 185/214] Fix FSTDelegateValue: property -> ivar (#2778) * Fix FSTDelegateValue: property -> ivar c++ properties don't mix well with obj-c properties. --- Firestore/Source/Model/FSTFieldValue.h | 1 + Firestore/Source/Model/FSTFieldValue.mm | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Firestore/Source/Model/FSTFieldValue.h b/Firestore/Source/Model/FSTFieldValue.h index 8eebde5b747..4c9f0a48945 100644 --- a/Firestore/Source/Model/FSTFieldValue.h +++ b/Firestore/Source/Model/FSTFieldValue.h @@ -277,6 +277,7 @@ enum class ServerTimestampBehavior { None, Estimate, Previous }; */ @interface FSTDelegateValue : FSTFieldValue + (instancetype)delegateWithValue:(FieldValue &&)value; +- (const FieldValue &)internalValue; @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Model/FSTFieldValue.mm b/Firestore/Source/Model/FSTFieldValue.mm index 8d931fef8d7..6287c37f13b 100644 --- a/Firestore/Source/Model/FSTFieldValue.mm +++ b/Firestore/Source/Model/FSTFieldValue.mm @@ -907,15 +907,18 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { @end -@interface FSTDelegateValue () -@property(nonatomic, assign, readonly) FieldValue internalValue; -@end +@implementation FSTDelegateValue { + FieldValue _internalValue; +} -@implementation FSTDelegateValue + (instancetype)delegateWithValue:(FieldValue &&)value { return [[FSTDelegateValue alloc] initWithValue:std::move(value)]; } +- (const FieldValue &)internalValue { + return _internalValue; +} + - (id)initWithValue:(FieldValue &&)value { self = [super init]; if (self) { From 754d5c07d49c2944344f08913684239cf530d4fc Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Tue, 9 Apr 2019 20:50:58 -0700 Subject: [PATCH 186/214] Expose Collection Group queries. (#2786) --- Firestore/CHANGELOG.md | 2 ++ .../Example/Tests/Integration/API/FIRQueryTests.mm | 2 -- .../Tests/Integration/API/FIRValidationTests.mm | 2 -- Firestore/Source/API/FIRFirestore+Internal.h | 13 ------------- Firestore/Source/Public/FIRFirestore.h | 12 ++++++++++++ Firestore/Swift/Tests/API/BasicCompileTests.swift | 5 ++--- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index b457ae11627..2ede2f61cfa 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,6 +1,8 @@ # Unreleased # 1.3.0 +- [feature] You can now query across all collections in your database with a + given collection ID using the `Firestore.collectionGroup()` method. - [feature] Added community support for tvOS. # 1.2.1 diff --git a/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm index 18555a61334..af7be097953 100644 --- a/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm @@ -20,8 +20,6 @@ #import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" -// TODO(b/116617988): Remove Internal include once CG queries are public. -#import "Firestore/Source/API/FIRFirestore+Internal.h" @interface FIRQueryTests : FSTIntegrationTestCase @end diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index a4233228204..002da854436 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -24,8 +24,6 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" -// TODO(b/116617988): Remove Internal include once CG queries are public. -#import "Firestore/Source/API/FIRFirestore+Internal.h" // We have tests for passing nil when nil is not supposed to be allowed. So suppress the warnings. #pragma clang diagnostic ignored "-Wnonnull" diff --git a/Firestore/Source/API/FIRFirestore+Internal.h b/Firestore/Source/API/FIRFirestore+Internal.h index 06d5b7d008a..d6d09ef0196 100644 --- a/Firestore/Source/API/FIRFirestore+Internal.h +++ b/Firestore/Source/API/FIRFirestore+Internal.h @@ -48,19 +48,6 @@ NS_ASSUME_NONNULL_BEGIN /** Internal FIRFirestore API we don't want exposed in our public header files. */ @interface FIRFirestore (Internal) -// TODO(b/116617988): Move this to FIRFirestore.h and update CHANGELOG.md once backend support is -// ready. -#pragma mark - Collection Group Queries -/** - * Creates and returns a new `Query` that includes all documents in the database that are contained - * in a collection or subcollection with the given collectionID. - * - * @param collectionID Identifies the collections to query over. Every collection or subcollection - * with this ID as the last segment of its path will be included. Cannot contain a slash. - * @return The created `Query`. - */ -- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID NS_SWIFT_NAME(collectionGroup(_:)); - /** Checks to see if logging is is globally enabled for the Firestore client. */ + (BOOL)isLoggingEnabled; diff --git a/Firestore/Source/Public/FIRFirestore.h b/Firestore/Source/Public/FIRFirestore.h index f8a465e2ff1..5c6e6b63b7c 100644 --- a/Firestore/Source/Public/FIRFirestore.h +++ b/Firestore/Source/Public/FIRFirestore.h @@ -92,6 +92,18 @@ NS_SWIFT_NAME(Firestore) */ - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath NS_SWIFT_NAME(document(_:)); +#pragma mark - Collection Group Queries + +/** + * Creates and returns a new `Query` that includes all documents in the database that are contained + * in a collection or subcollection with the given collectionID. + * + * @param collectionID Identifies the collections to query over. Every collection or subcollection + * with this ID as the last segment of its path will be included. Cannot contain a slash. + * @return The created `Query`. + */ +- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID NS_SWIFT_NAME(collectionGroup(_:)); + #pragma mark - Transactions and Write Batches /** diff --git a/Firestore/Swift/Tests/API/BasicCompileTests.swift b/Firestore/Swift/Tests/API/BasicCompileTests.swift index 71cff227dee..8308dea96b7 100644 --- a/Firestore/Swift/Tests/API/BasicCompileTests.swift +++ b/Firestore/Swift/Tests/API/BasicCompileTests.swift @@ -90,7 +90,7 @@ func makeRefs(database db: Firestore) -> (CollectionReference, DocumentReference } func makeQuery(collection collectionRef: CollectionReference) -> Query { - let query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") + var query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") .whereField("age", isGreaterThanOrEqualTo: 24) .whereField("tags", arrayContains: "active") .whereField(FieldPath(["tags"]), arrayContains: "active") @@ -99,8 +99,7 @@ func makeQuery(collection collectionRef: CollectionReference) -> Query { .order(by: "name", descending: true) .limit(to: 10) - // TODO(b/116617988): collectionGroup query. - // query = collectionRef.firestore.collectionGroup("collection") + query = collectionRef.firestore.collectionGroup("collection") return query } From 04b2de75e88eb2593b6dd7d46fcccafa82169322 Mon Sep 17 00:00:00 2001 From: dmandar Date: Tue, 9 Apr 2019 21:04:59 -0700 Subject: [PATCH 187/214] FDL release notes for M47 (#2792) * Update FDL release notes for M47 release. * Add note on analytics hard dependency removal. --- Firebase/DynamicLinks/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Firebase/DynamicLinks/CHANGELOG.md b/Firebase/DynamicLinks/CHANGELOG.md index 8d4066baecd..07258408918 100644 --- a/Firebase/DynamicLinks/CHANGELOG.md +++ b/Firebase/DynamicLinks/CHANGELOG.md @@ -1,3 +1,7 @@ +# v4.0 +- FirebaseAnalytics is no longer a hard dependency in the DynamicLinks pod. If you were installing Dynamic Links via pod ''Firebase/DynamicLinks'', you should add 'pod 'Firebase/Analytics'' to the Podfile to maintain full Dynamic Links functionality. If you previously have 'pod 'Firebase/Core'' in the Podfile, no change is necessary. (#2738) +- Remove deprecated API in FDLURLComponents. (#2768) + # v3.4.3 - Fixed an issue where matchesshortlinkformat was returning true for certain FDL long links. From 51159943d9c5c16d7e8d3d99e98595a0af9e7572 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 9 Apr 2019 21:21:37 -0700 Subject: [PATCH 188/214] Fix 1P build issue (#2793) --- .../DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h | 5 +++++ Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m | 1 - Firebase/DynamicLinks/FIRDynamicLinks+Private.h | 5 ----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h b/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h index 2e4745cfdf8..68207503ecc 100644 --- a/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h +++ b/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h @@ -16,6 +16,11 @@ #import "DynamicLinks/Public/FDLURLComponents.h" +/** + * Label exceptions from FDL. + */ +FOUNDATION_EXPORT NSString *_Nonnull const kFirebaseDurableDeepLinkErrorDomain; + NS_ASSUME_NONNULL_BEGIN /// Each of the parameter classes used in FIRDynamicLinkURLComponents needs to be able to diff --git a/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m b/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m index 47f6b5814b0..c8b82f84125 100644 --- a/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m +++ b/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m @@ -18,7 +18,6 @@ #import "DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h" #import "DynamicLinks/FDLURLComponents/FIRDynamicLinkComponentsKeyProvider.h" -#import "DynamicLinks/FIRDynamicLinks+Private.h" #import "DynamicLinks/Public/FDLURLComponents.h" #import "DynamicLinks/Logging/FDLLogging.h" diff --git a/Firebase/DynamicLinks/FIRDynamicLinks+Private.h b/Firebase/DynamicLinks/FIRDynamicLinks+Private.h index e771a5ec67f..f1e3c28cdda 100644 --- a/Firebase/DynamicLinks/FIRDynamicLinks+Private.h +++ b/Firebase/DynamicLinks/FIRDynamicLinks+Private.h @@ -30,11 +30,6 @@ FOUNDATION_EXPORT NSString *const kFIRDLVersion; */ FOUNDATION_EXPORT NSString *const kFIRDLReadDeepLinkAfterInstallKey; -/** - * Label exceptions from FDL. - */ -FOUNDATION_EXPORT NSString *const kFirebaseDurableDeepLinkErrorDomain; - @interface FIRDynamicLinks (Private) /** From 423959ae0b3fd1564c58e70e613b3e0e16a06867 Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Wed, 10 Apr 2019 10:55:48 -0400 Subject: [PATCH 189/214] Fix headers imports moved private (#2784) --- Firebase/Core/FIRAnalyticsConfiguration.m | 2 +- Firebase/Core/FIRConfiguration.m | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Firebase/Core/FIRAnalyticsConfiguration.m b/Firebase/Core/FIRAnalyticsConfiguration.m index 3ade594dff6..8c3a37ffe29 100644 --- a/Firebase/Core/FIRAnalyticsConfiguration.m +++ b/Firebase/Core/FIRAnalyticsConfiguration.m @@ -14,7 +14,7 @@ #import -#import "FIRAnalyticsConfiguration.h" +#import "Private/FIRAnalyticsConfiguration.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-implementations" diff --git a/Firebase/Core/FIRConfiguration.m b/Firebase/Core/FIRConfiguration.m index 607757440bd..869f73d7f65 100644 --- a/Firebase/Core/FIRConfiguration.m +++ b/Firebase/Core/FIRConfiguration.m @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import "FIRConfigurationInternal.h" #import "Private/FIRConfigurationInternal.h" -#import "FIRAnalyticsConfiguration.h" +#import "Private/FIRAnalyticsConfiguration.h" extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel); From b9958368f142b739052a95d8d035a5a30b126a8b Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Wed, 10 Apr 2019 12:03:54 -0400 Subject: [PATCH 190/214] Add initializer used by Swift 1p tests (#2794) --- Firebase/InstanceID/FIRInstanceID+Private.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Firebase/InstanceID/FIRInstanceID+Private.h b/Firebase/InstanceID/FIRInstanceID+Private.h index 3e3ec175f9c..0beb2607173 100644 --- a/Firebase/InstanceID/FIRInstanceID+Private.h +++ b/Firebase/InstanceID/FIRInstanceID+Private.h @@ -24,6 +24,11 @@ // TODO(chliangGoogle) Rename this to Internal. @interface FIRInstanceID (Private) +/** + * Private initializer. + */ +- (nonnull instancetype)initPrivately; + /** * Return the cached checkin preferences on the disk. This is used internally only by Messaging. * From cbeec8f9d7224621570b21ca8a3ecb4b1eb29f9a Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 10 Apr 2019 10:53:24 -0700 Subject: [PATCH 191/214] Fix internal build target (#2795) --- Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m | 3 +++ Firebase/DynamicLinks/FIRDynamicLinks.m | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m b/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m index c8b82f84125..7c6a12e6ca1 100644 --- a/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m +++ b/Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m @@ -23,6 +23,9 @@ #import "DynamicLinks/Logging/FDLLogging.h" #import "DynamicLinks/Utilities/FDLUtilities.h" +// Label exceptions from FDL. +NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink"; + /// The exact behavior of dict[key] = value is unclear when value is nil. This function safely adds /// the key-value pair to the dictionary, even when value is nil. /// This function will treat empty string in the same way as nil. diff --git a/Firebase/DynamicLinks/FIRDynamicLinks.m b/Firebase/DynamicLinks/FIRDynamicLinks.m index 53f5fed0e7b..aca48d205d4 100644 --- a/Firebase/DynamicLinks/FIRDynamicLinks.m +++ b/Firebase/DynamicLinks/FIRDynamicLinks.m @@ -29,6 +29,7 @@ #import "DynamicLinks/FIRDLScionLogging.h" #endif +#import "DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h" #import "DynamicLinks/FIRDLRetrievalProcessFactory.h" #import "DynamicLinks/FIRDLRetrievalProcessProtocols.h" #import "DynamicLinks/FIRDLRetrievalProcessResult.h" @@ -55,9 +56,6 @@ NSString *const kFIRDLReadDeepLinkAfterInstallKey = @"com.google.appinvite.readDeeplinkAfterInstall"; -// Label exceptions from FDL. -NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink"; - // Error code from FDL. static const NSInteger FIRErrorCodeDurableDeepLinkFailed = -119; From 53d7ee7c3e1d14c53e779d200b2617d763563b2c Mon Sep 17 00:00:00 2001 From: Chuan Ren Date: Wed, 10 Apr 2019 11:38:24 -0700 Subject: [PATCH 192/214] Remove spaces in file paths (#2798) --- .../{Auth Provider => AuthProvider}/Email/FIREmailAuthProvider.m | 0 .../Email/FIREmailPasswordAuthCredential.h | 0 .../Email/FIREmailPasswordAuthCredential.m | 0 .../Source/{Auth Provider => AuthProvider}/FIRAuthCredential.m | 0 .../{Auth Provider => AuthProvider}/FIRAuthCredential_Internal.h | 0 .../Auth/Source/{Auth Provider => AuthProvider}/FIRAuthProvider.m | 0 .../Facebook/FIRFacebookAuthCredential.h | 0 .../Facebook/FIRFacebookAuthCredential.m | 0 .../Facebook/FIRFacebookAuthProvider.m | 0 .../GameCenter/FIRGameCenterAuthCredential.h | 0 .../GameCenter/FIRGameCenterAuthCredential.m | 0 .../GameCenter/FIRGameCenterAuthProvider.m | 0 .../GitHub/FIRGitHubAuthCredential.h | 0 .../GitHub/FIRGitHubAuthCredential.m | 0 .../GitHub/FIRGitHubAuthProvider.m | 0 .../Google/FIRGoogleAuthCredential.h | 0 .../Google/FIRGoogleAuthCredential.m | 0 .../Google/FIRGoogleAuthProvider.m | 0 .../{Auth Provider => AuthProvider}/OAuth/FIROAuthCredential.m | 0 .../OAuth/FIROAuthCredential_Internal.h | 0 .../{Auth Provider => AuthProvider}/OAuth/FIROAuthProvider.m | 0 .../Phone/FIRPhoneAuthCredential.m | 0 .../Phone/FIRPhoneAuthCredential_Internal.h | 0 .../{Auth Provider => AuthProvider}/Phone/FIRPhoneAuthProvider.m | 0 .../Twitter/FIRTwitterAuthCredential.h | 0 .../Twitter/FIRTwitterAuthCredential.m | 0 .../Twitter/FIRTwitterAuthProvider.m | 0 .../Source/{System Services => SystemService}/FIRAuthAPNSToken.h | 0 .../Source/{System Services => SystemService}/FIRAuthAPNSToken.m | 0 .../{System Services => SystemService}/FIRAuthAPNSTokenManager.h | 0 .../{System Services => SystemService}/FIRAuthAPNSTokenManager.m | 0 .../{System Services => SystemService}/FIRAuthAppCredential.h | 0 .../{System Services => SystemService}/FIRAuthAppCredential.m | 0 .../FIRAuthAppCredentialManager.h | 0 .../FIRAuthAppCredentialManager.m | 0 .../FIRAuthNotificationManager.h | 0 .../FIRAuthNotificationManager.m | 0 .../{System Services => SystemService}/FIRAuthStoredUserManager.h | 0 .../{System Services => SystemService}/FIRAuthStoredUserManager.m | 0 .../{System Services => SystemService}/FIRSecureTokenService.h | 0 .../{System Services => SystemService}/FIRSecureTokenService.m | 0 41 files changed, 0 insertions(+), 0 deletions(-) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Email/FIREmailAuthProvider.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Email/FIREmailPasswordAuthCredential.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Email/FIREmailPasswordAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/FIRAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/FIRAuthCredential_Internal.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/FIRAuthProvider.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Facebook/FIRFacebookAuthCredential.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Facebook/FIRFacebookAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Facebook/FIRFacebookAuthProvider.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/GameCenter/FIRGameCenterAuthCredential.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/GameCenter/FIRGameCenterAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/GameCenter/FIRGameCenterAuthProvider.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/GitHub/FIRGitHubAuthCredential.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/GitHub/FIRGitHubAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/GitHub/FIRGitHubAuthProvider.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Google/FIRGoogleAuthCredential.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Google/FIRGoogleAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Google/FIRGoogleAuthProvider.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/OAuth/FIROAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/OAuth/FIROAuthCredential_Internal.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/OAuth/FIROAuthProvider.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Phone/FIRPhoneAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Phone/FIRPhoneAuthCredential_Internal.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Phone/FIRPhoneAuthProvider.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Twitter/FIRTwitterAuthCredential.h (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Twitter/FIRTwitterAuthCredential.m (100%) rename Firebase/Auth/Source/{Auth Provider => AuthProvider}/Twitter/FIRTwitterAuthProvider.m (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthAPNSToken.h (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthAPNSToken.m (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthAPNSTokenManager.h (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthAPNSTokenManager.m (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthAppCredential.h (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthAppCredential.m (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthAppCredentialManager.h (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthAppCredentialManager.m (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthNotificationManager.h (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthNotificationManager.m (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthStoredUserManager.h (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRAuthStoredUserManager.m (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRSecureTokenService.h (100%) rename Firebase/Auth/Source/{System Services => SystemService}/FIRSecureTokenService.m (100%) diff --git a/Firebase/Auth/Source/Auth Provider/Email/FIREmailAuthProvider.m b/Firebase/Auth/Source/AuthProvider/Email/FIREmailAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Email/FIREmailAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/Email/FIREmailAuthProvider.m diff --git a/Firebase/Auth/Source/Auth Provider/Email/FIREmailPasswordAuthCredential.h b/Firebase/Auth/Source/AuthProvider/Email/FIREmailPasswordAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Email/FIREmailPasswordAuthCredential.h rename to Firebase/Auth/Source/AuthProvider/Email/FIREmailPasswordAuthCredential.h diff --git a/Firebase/Auth/Source/Auth Provider/Email/FIREmailPasswordAuthCredential.m b/Firebase/Auth/Source/AuthProvider/Email/FIREmailPasswordAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Email/FIREmailPasswordAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/Email/FIREmailPasswordAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/FIRAuthCredential.m b/Firebase/Auth/Source/AuthProvider/FIRAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/FIRAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/FIRAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/FIRAuthCredential_Internal.h b/Firebase/Auth/Source/AuthProvider/FIRAuthCredential_Internal.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/FIRAuthCredential_Internal.h rename to Firebase/Auth/Source/AuthProvider/FIRAuthCredential_Internal.h diff --git a/Firebase/Auth/Source/Auth Provider/FIRAuthProvider.m b/Firebase/Auth/Source/AuthProvider/FIRAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/FIRAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/FIRAuthProvider.m diff --git a/Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthCredential.h b/Firebase/Auth/Source/AuthProvider/Facebook/FIRFacebookAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthCredential.h rename to Firebase/Auth/Source/AuthProvider/Facebook/FIRFacebookAuthCredential.h diff --git a/Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthCredential.m b/Firebase/Auth/Source/AuthProvider/Facebook/FIRFacebookAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/Facebook/FIRFacebookAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthProvider.m b/Firebase/Auth/Source/AuthProvider/Facebook/FIRFacebookAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Facebook/FIRFacebookAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/Facebook/FIRFacebookAuthProvider.m diff --git a/Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthCredential.h b/Firebase/Auth/Source/AuthProvider/GameCenter/FIRGameCenterAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthCredential.h rename to Firebase/Auth/Source/AuthProvider/GameCenter/FIRGameCenterAuthCredential.h diff --git a/Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthCredential.m b/Firebase/Auth/Source/AuthProvider/GameCenter/FIRGameCenterAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/GameCenter/FIRGameCenterAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthProvider.m b/Firebase/Auth/Source/AuthProvider/GameCenter/FIRGameCenterAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/GameCenter/FIRGameCenterAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/GameCenter/FIRGameCenterAuthProvider.m diff --git a/Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthCredential.h b/Firebase/Auth/Source/AuthProvider/GitHub/FIRGitHubAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthCredential.h rename to Firebase/Auth/Source/AuthProvider/GitHub/FIRGitHubAuthCredential.h diff --git a/Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthCredential.m b/Firebase/Auth/Source/AuthProvider/GitHub/FIRGitHubAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/GitHub/FIRGitHubAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthProvider.m b/Firebase/Auth/Source/AuthProvider/GitHub/FIRGitHubAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/GitHub/FIRGitHubAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/GitHub/FIRGitHubAuthProvider.m diff --git a/Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthCredential.h b/Firebase/Auth/Source/AuthProvider/Google/FIRGoogleAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthCredential.h rename to Firebase/Auth/Source/AuthProvider/Google/FIRGoogleAuthCredential.h diff --git a/Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthCredential.m b/Firebase/Auth/Source/AuthProvider/Google/FIRGoogleAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/Google/FIRGoogleAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthProvider.m b/Firebase/Auth/Source/AuthProvider/Google/FIRGoogleAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Google/FIRGoogleAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/Google/FIRGoogleAuthProvider.m diff --git a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential.m b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential_Internal.h b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential_Internal.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthCredential_Internal.h rename to Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential_Internal.h diff --git a/Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/OAuth/FIROAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthProvider.m diff --git a/Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthCredential.m b/Firebase/Auth/Source/AuthProvider/Phone/FIRPhoneAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/Phone/FIRPhoneAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthCredential_Internal.h b/Firebase/Auth/Source/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthCredential_Internal.h rename to Firebase/Auth/Source/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h diff --git a/Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthProvider.m b/Firebase/Auth/Source/AuthProvider/Phone/FIRPhoneAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Phone/FIRPhoneAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/Phone/FIRPhoneAuthProvider.m diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h b/Firebase/Auth/Source/AuthProvider/Twitter/FIRTwitterAuthCredential.h similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.h rename to Firebase/Auth/Source/AuthProvider/Twitter/FIRTwitterAuthCredential.h diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m b/Firebase/Auth/Source/AuthProvider/Twitter/FIRTwitterAuthCredential.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthCredential.m rename to Firebase/Auth/Source/AuthProvider/Twitter/FIRTwitterAuthCredential.m diff --git a/Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m b/Firebase/Auth/Source/AuthProvider/Twitter/FIRTwitterAuthProvider.m similarity index 100% rename from Firebase/Auth/Source/Auth Provider/Twitter/FIRTwitterAuthProvider.m rename to Firebase/Auth/Source/AuthProvider/Twitter/FIRTwitterAuthProvider.m diff --git a/Firebase/Auth/Source/System Services/FIRAuthAPNSToken.h b/Firebase/Auth/Source/SystemService/FIRAuthAPNSToken.h similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthAPNSToken.h rename to Firebase/Auth/Source/SystemService/FIRAuthAPNSToken.h diff --git a/Firebase/Auth/Source/System Services/FIRAuthAPNSToken.m b/Firebase/Auth/Source/SystemService/FIRAuthAPNSToken.m similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthAPNSToken.m rename to Firebase/Auth/Source/SystemService/FIRAuthAPNSToken.m diff --git a/Firebase/Auth/Source/System Services/FIRAuthAPNSTokenManager.h b/Firebase/Auth/Source/SystemService/FIRAuthAPNSTokenManager.h similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthAPNSTokenManager.h rename to Firebase/Auth/Source/SystemService/FIRAuthAPNSTokenManager.h diff --git a/Firebase/Auth/Source/System Services/FIRAuthAPNSTokenManager.m b/Firebase/Auth/Source/SystemService/FIRAuthAPNSTokenManager.m similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthAPNSTokenManager.m rename to Firebase/Auth/Source/SystemService/FIRAuthAPNSTokenManager.m diff --git a/Firebase/Auth/Source/System Services/FIRAuthAppCredential.h b/Firebase/Auth/Source/SystemService/FIRAuthAppCredential.h similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthAppCredential.h rename to Firebase/Auth/Source/SystemService/FIRAuthAppCredential.h diff --git a/Firebase/Auth/Source/System Services/FIRAuthAppCredential.m b/Firebase/Auth/Source/SystemService/FIRAuthAppCredential.m similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthAppCredential.m rename to Firebase/Auth/Source/SystemService/FIRAuthAppCredential.m diff --git a/Firebase/Auth/Source/System Services/FIRAuthAppCredentialManager.h b/Firebase/Auth/Source/SystemService/FIRAuthAppCredentialManager.h similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthAppCredentialManager.h rename to Firebase/Auth/Source/SystemService/FIRAuthAppCredentialManager.h diff --git a/Firebase/Auth/Source/System Services/FIRAuthAppCredentialManager.m b/Firebase/Auth/Source/SystemService/FIRAuthAppCredentialManager.m similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthAppCredentialManager.m rename to Firebase/Auth/Source/SystemService/FIRAuthAppCredentialManager.m diff --git a/Firebase/Auth/Source/System Services/FIRAuthNotificationManager.h b/Firebase/Auth/Source/SystemService/FIRAuthNotificationManager.h similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthNotificationManager.h rename to Firebase/Auth/Source/SystemService/FIRAuthNotificationManager.h diff --git a/Firebase/Auth/Source/System Services/FIRAuthNotificationManager.m b/Firebase/Auth/Source/SystemService/FIRAuthNotificationManager.m similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthNotificationManager.m rename to Firebase/Auth/Source/SystemService/FIRAuthNotificationManager.m diff --git a/Firebase/Auth/Source/System Services/FIRAuthStoredUserManager.h b/Firebase/Auth/Source/SystemService/FIRAuthStoredUserManager.h similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthStoredUserManager.h rename to Firebase/Auth/Source/SystemService/FIRAuthStoredUserManager.h diff --git a/Firebase/Auth/Source/System Services/FIRAuthStoredUserManager.m b/Firebase/Auth/Source/SystemService/FIRAuthStoredUserManager.m similarity index 100% rename from Firebase/Auth/Source/System Services/FIRAuthStoredUserManager.m rename to Firebase/Auth/Source/SystemService/FIRAuthStoredUserManager.m diff --git a/Firebase/Auth/Source/System Services/FIRSecureTokenService.h b/Firebase/Auth/Source/SystemService/FIRSecureTokenService.h similarity index 100% rename from Firebase/Auth/Source/System Services/FIRSecureTokenService.h rename to Firebase/Auth/Source/SystemService/FIRSecureTokenService.h diff --git a/Firebase/Auth/Source/System Services/FIRSecureTokenService.m b/Firebase/Auth/Source/SystemService/FIRSecureTokenService.m similarity index 100% rename from Firebase/Auth/Source/System Services/FIRSecureTokenService.m rename to Firebase/Auth/Source/SystemService/FIRSecureTokenService.m From 9c72e1a8e414101966af2211c16065a19d2787ce Mon Sep 17 00:00:00 2001 From: Maksym Malyhin Date: Wed, 10 Apr 2019 15:07:12 -0400 Subject: [PATCH 193/214] InstanceID - podspec lint fix (#2796) --- Firebase/InstanceID/FIRInstanceID+Private.h | 5 ----- Firebase/InstanceID/Private/FIRInstanceID_Private.h | 5 +++++ scripts/if_changed.sh | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Firebase/InstanceID/FIRInstanceID+Private.h b/Firebase/InstanceID/FIRInstanceID+Private.h index 0beb2607173..3e3ec175f9c 100644 --- a/Firebase/InstanceID/FIRInstanceID+Private.h +++ b/Firebase/InstanceID/FIRInstanceID+Private.h @@ -24,11 +24,6 @@ // TODO(chliangGoogle) Rename this to Internal. @interface FIRInstanceID (Private) -/** - * Private initializer. - */ -- (nonnull instancetype)initPrivately; - /** * Return the cached checkin preferences on the disk. This is used internally only by Messaging. * diff --git a/Firebase/InstanceID/Private/FIRInstanceID_Private.h b/Firebase/InstanceID/Private/FIRInstanceID_Private.h index 683dd4ed8c7..6a9e2d34bc6 100644 --- a/Firebase/InstanceID/Private/FIRInstanceID_Private.h +++ b/Firebase/InstanceID/Private/FIRInstanceID_Private.h @@ -21,6 +21,11 @@ */ @interface FIRInstanceID () +/** + * Private initializer. + */ +- (nonnull instancetype)initPrivately; + /** * Returns a Firebase Messaging scoped token for the firebase app. * diff --git a/scripts/if_changed.sh b/scripts/if_changed.sh index 6c4f6f5031e..64c6442d444 100755 --- a/scripts/if_changed.sh +++ b/scripts/if_changed.sh @@ -53,7 +53,8 @@ else 'FirebaseMessaging.podspec|FirebaseStorage.podspec|'\ 'FirebaseStorage.podspec|Firebase/InAppMessagingDisplay|InAppMessagingDisplay|'\ 'InAppMessaging|Firebase/InAppMessaging|'\ -'FirebaseInAppMessaging.podspec|FirebaseInAppMessagingDisplay.podspec)' +'FirebaseInAppMessaging.podspec|FirebaseInAppMessagingDisplay.podspec|'\ +'Firebase/InstanceID|FirebaseInstanceID.podspec)' ;; Firebase-*) @@ -62,7 +63,7 @@ else 'FirebaseAnalyticsIntop.podspec|FirebaseAuth.podspec|FirebaseAuthInterop.podspec|'\ 'FirebaseCore.podspec|FirebaseDatabase.podspec|FirebaseDynamicLinks.podspec|'\ 'FirebaseMessaging.podspec|FirebaseStorage.podspec|'\ -'FirebaseStorage.podspec)' +'FirebaseStorage.podspec|Firebase/InstanceID|FirebaseInstanceID.podspec)' ;; Functions-*) From 071d6e9c0158ffa6f70b9e26fc6c8ee680443862 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 10 Apr 2019 12:28:09 -0700 Subject: [PATCH 194/214] FIRErrorCodeDurableDeepLinkFailed is for 3P only (#2797) * FIRErrorCodeDurableDeepLinkFailed is for 3P only * import is also 3P only --- Firebase/DynamicLinks/FIRDynamicLinks.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Firebase/DynamicLinks/FIRDynamicLinks.m b/Firebase/DynamicLinks/FIRDynamicLinks.m index aca48d205d4..d670a925b78 100644 --- a/Firebase/DynamicLinks/FIRDynamicLinks.m +++ b/Firebase/DynamicLinks/FIRDynamicLinks.m @@ -29,7 +29,9 @@ #import "DynamicLinks/FIRDLScionLogging.h" #endif +#ifdef FIRDynamicLinks3P #import "DynamicLinks/FDLURLComponents/FDLURLComponents+Private.h" +#endif #import "DynamicLinks/FIRDLRetrievalProcessFactory.h" #import "DynamicLinks/FIRDLRetrievalProcessProtocols.h" #import "DynamicLinks/FIRDLRetrievalProcessResult.h" @@ -56,9 +58,6 @@ NSString *const kFIRDLReadDeepLinkAfterInstallKey = @"com.google.appinvite.readDeeplinkAfterInstall"; -// Error code from FDL. -static const NSInteger FIRErrorCodeDurableDeepLinkFailed = -119; - // We should only open url once. We use the following key to store the state in the user defaults. static NSString *const kFIRDLOpenURLKey = @"com.google.appinvite.openURL"; @@ -86,6 +85,9 @@ @interface FIRDynamicLinks () @end #ifdef FIRDynamicLinks3P +// Error code from FDL. +static const NSInteger FIRErrorCodeDurableDeepLinkFailed = -119; + @interface FIRDynamicLinks () { /// Stored Analytics reference, if it exists. id _Nullable _analytics; From 47bd60a7d05d49e02c3daf15c4b5c8b7d02c1095 Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 10 Apr 2019 17:32:49 -0700 Subject: [PATCH 195/214] Move collectionGroup method out of the Internal category (#2802) This prevents a warning for end users. --- Firestore/Source/API/FIRFirestore.mm | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index 094db978a3e..6ecd2a60c7e 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -204,6 +204,18 @@ - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath { return [[FIRDocumentReference alloc] initWithReference:std::move(documentReference)]; } +- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID { + if (!collectionID) { + ThrowInvalidArgument("Collection ID cannot be nil."); + } + if ([collectionID containsString:@"/"]) { + ThrowInvalidArgument("Invalid collection ID (%s). Collection IDs must not contain / in them.", + collectionID); + } + + return _firestore->GetCollectionGroup(collectionID); +} + - (FIRWriteBatch *)batch { return _firestore->GetBatch(); } @@ -273,18 +285,6 @@ + (FIRFirestore *)recoverFromFirestore:(Firestore *)firestore { return (__bridge FIRFirestore *)firestore->extension(); } -- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID { - if (!collectionID) { - ThrowInvalidArgument("Collection ID cannot be nil."); - } - if ([collectionID containsString:@"/"]) { - ThrowInvalidArgument("Invalid collection ID (%s). Collection IDs must not contain / in them.", - collectionID); - } - - return _firestore->GetCollectionGroup(collectionID); -} - - (void)shutdownWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { _firestore->Shutdown(completion); } From 64b270b6d021d1361c0c217c61f053a3905b09f7 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 10 Apr 2019 18:08:51 -0700 Subject: [PATCH 196/214] Temporarily restore setAnalyticsCollectionEnabled (#2803) --- Firebase/Core/FIRAnalyticsConfiguration.m | 4 ++++ Firebase/Core/Private/FIRAnalyticsConfiguration.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Firebase/Core/FIRAnalyticsConfiguration.m b/Firebase/Core/FIRAnalyticsConfiguration.m index 8c3a37ffe29..a57936b9bc7 100644 --- a/Firebase/Core/FIRAnalyticsConfiguration.m +++ b/Firebase/Core/FIRAnalyticsConfiguration.m @@ -39,6 +39,10 @@ - (void)postNotificationName:(NSString *)name value:(id)value { userInfo:@{name : value}]; } +- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled { + [self setAnalyticsCollectionEnabled:analyticsCollectionEnabled persistSetting:YES]; +} + - (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled persistSetting:(BOOL)shouldPersist { // Persist the measurementEnabledState. Use FIRAnalyticsEnabledState values instead of YES/NO. diff --git a/Firebase/Core/Private/FIRAnalyticsConfiguration.h b/Firebase/Core/Private/FIRAnalyticsConfiguration.h index 13b2af34fb3..6429ac70eac 100644 --- a/Firebase/Core/Private/FIRAnalyticsConfiguration.h +++ b/Firebase/Core/Private/FIRAnalyticsConfiguration.h @@ -43,6 +43,10 @@ static NSString *const kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotifi /// Returns the shared instance of FIRAnalyticsConfiguration. + (FIRAnalyticsConfiguration *)sharedInstance; +// Sets whether analytics collection is enabled for this app on this device. This setting is +// persisted across app sessions. By default it is enabled. +- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled; + /// Sets whether analytics collection is enabled for this app on this device, and a flag to persist /// the value or not. The setting should not be persisted if being set by the global data collection /// flag. From 9d0f07b2aae4843cfbd21f1ee3b23beed6175914 Mon Sep 17 00:00:00 2001 From: Marek Gilbert Date: Thu, 11 Apr 2019 10:14:04 -0700 Subject: [PATCH 197/214] Have Travis test firestore-master --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5c93cd05aff..4f207c5a174 100644 --- a/.travis.yml +++ b/.travis.yml @@ -293,3 +293,4 @@ jobs: branches: only: - master + - firestore-master From 09010cfba0a624db5e996d876de91b6105889396 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Thu, 11 Apr 2019 10:40:56 -0700 Subject: [PATCH 198/214] Fix a couple small issues I tripped over today. (#2806) * Fix a couple small issues I tripped over today. 1. Fix primeBackend so it uses an isolated FIRFirestore* instance. I ran into an issue where tests that try to change .settings would fail if they were the very first test run, since primeBackend had already configured the client and so settings could no longer be changed. 2. Remove FIRFirestore.client and make api::Firestore.client() assert client is configured. --- .../Example/Tests/Util/FSTIntegrationTestCase.mm | 13 +++++++------ Firestore/Source/API/FIRFirestore+Internal.h | 1 - Firestore/Source/API/FIRFirestore.mm | 7 ------- Firestore/Source/API/FIRQuery.mm | 2 +- Firestore/Source/API/FIRWriteBatch.mm | 2 +- .../core/src/firebase/firestore/api/firestore.h | 2 ++ 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index fe6cf3a166b..d05cb9cae9a 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -85,6 +85,7 @@ - (void)setUp { [super setUp]; [self clearPersistence]; + [self primeBackend]; _firestores = [NSMutableArray array]; self.db = [self firestore]; @@ -202,14 +203,13 @@ - (FIRFirestore *)firestoreWithProjectID:(NSString *)projectID { [_firestores addObject:firestore]; - [self primeBackend:firestore]; - return firestore; } -- (void)primeBackend:(FIRFirestore *)db { +- (void)primeBackend { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ + FIRFirestore *db = [self firestore]; XCTestExpectation *watchInitialized = [self expectationWithDescription:@"Prime backend: Watch initialized"]; __block XCTestExpectation *watchUpdateReceived; @@ -247,6 +247,8 @@ - (void)primeBackend:(FIRFirestore *)db { }]; [listenerRegistration remove]; + + [self shutdownFirestore:db]; }); } @@ -393,14 +395,13 @@ - (void)mergeDocumentRef:(FIRDocumentReference *)ref } - (void)disableNetwork { - [self.db.client + [self.db disableNetworkWithCompletion:[self completionForExpectationWithName:@"Disable Network."]]; [self awaitExpectations]; } - (void)enableNetwork { - [self.db.client - enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable Network."]]; + [self.db enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable Network."]]; [self awaitExpectations]; } diff --git a/Firestore/Source/API/FIRFirestore+Internal.h b/Firestore/Source/API/FIRFirestore+Internal.h index d6d09ef0196..2b2174fba2c 100644 --- a/Firestore/Source/API/FIRFirestore+Internal.h +++ b/Firestore/Source/API/FIRFirestore+Internal.h @@ -68,7 +68,6 @@ NS_ASSUME_NONNULL_BEGIN // FIRFirestore ownes the DatabaseId instance. @property(nonatomic, assign, readonly) const firebase::firestore::model::DatabaseId *databaseID; -@property(nonatomic, strong, readonly) FSTFirestoreClient *client; @property(nonatomic, strong, readonly) FSTUserDataConverter *dataConverter; @end diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index 6ecd2a60c7e..1853434e76b 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -174,13 +174,6 @@ - (void)setSettings:(FIRFirestoreSettings *)settings { _firestore->set_settings(settings); } -/** - * Ensures that the FirestoreClient is configured and returns it. - */ -- (FSTFirestoreClient *)client { - return _firestore->client(); -} - - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath { if (!collectionPath) { ThrowInvalidArgument("Collection path cannot be nil."); diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 1efd60a9a31..78a15d9432c 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -121,7 +121,7 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)publicSource NSError *_Nullable error))completion { Source source = MakeSource(publicSource); if (source == Source::Cache) { - [self.firestore.client getDocumentsFromLocalCache:self completion:completion]; + [self.firestore.wrapped->client() getDocumentsFromLocalCache:self completion:completion]; return; } diff --git a/Firestore/Source/API/FIRWriteBatch.mm b/Firestore/Source/API/FIRWriteBatch.mm index aec381d1bd6..d95576f1527 100644 --- a/Firestore/Source/API/FIRWriteBatch.mm +++ b/Firestore/Source/API/FIRWriteBatch.mm @@ -133,7 +133,7 @@ - (void)commit { - (void)commitWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { [self verifyNotCommitted]; self.committed = TRUE; - [self.firestore.client writeMutations:std::move(_mutations) completion:completion]; + [self.firestore.wrapped->client() writeMutations:std::move(_mutations) completion:completion]; } - (void)verifyNotCommitted { diff --git a/Firestore/core/src/firebase/firestore/api/firestore.h b/Firestore/core/src/firebase/firestore/api/firestore.h index 4be3b0bb4ae..ccfc6b52141 100644 --- a/Firestore/core/src/firebase/firestore/api/firestore.h +++ b/Firestore/core/src/firebase/firestore/api/firestore.h @@ -32,6 +32,7 @@ #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/async_queue.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" NS_ASSUME_NONNULL_BEGIN @@ -75,6 +76,7 @@ class Firestore { } FSTFirestoreClient* client() { + HARD_ASSERT(client_, "Client is not yet configured."); return client_; } From 2815f1498566e1143272618b936446bb17b03398 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 11 Apr 2019 10:47:43 -0700 Subject: [PATCH 199/214] Restore kFIRLoggerCrash for internal clients (#2809) --- Firebase/Core/FIRLogger.m | 1 + Firebase/Core/Private/FIRLogger.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Firebase/Core/FIRLogger.m b/Firebase/Core/FIRLogger.m index 6994a107694..532a96c294a 100644 --- a/Firebase/Core/FIRLogger.m +++ b/Firebase/Core/FIRLogger.m @@ -27,6 +27,7 @@ FIRLoggerService kFIRLoggerAdMob = @"[Firebase/AdMob]"; FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]"; FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]"; +FIRLoggerService kFIRLoggerCrash = @"[Firebase/Crash]"; FIRLoggerService kFIRLoggerMLKit = @"[Firebase/MLKit]"; FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]"; FIRLoggerService kFIRLoggerRemoteConfig = @"[Firebase/RemoteConfig]"; diff --git a/Firebase/Core/Private/FIRLogger.h b/Firebase/Core/Private/FIRLogger.h index a86fb9741b5..548e389a4ef 100644 --- a/Firebase/Core/Private/FIRLogger.h +++ b/Firebase/Core/Private/FIRLogger.h @@ -29,6 +29,7 @@ extern FIRLoggerService kFIRLoggerABTesting; extern FIRLoggerService kFIRLoggerAdMob; extern FIRLoggerService kFIRLoggerAnalytics; extern FIRLoggerService kFIRLoggerAuth; +extern FIRLoggerService kFIRLoggerCrash; extern FIRLoggerService kFIRLoggerCore; extern FIRLoggerService kFIRLoggerMLKit; extern FIRLoggerService kFIRLoggerPerf; From 279a095ae65176f130c1e960be90eaf8fcaf2889 Mon Sep 17 00:00:00 2001 From: Gil Date: Thu, 11 Apr 2019 11:20:46 -0700 Subject: [PATCH 200/214] log command execution (#2811) --- scripts/check.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/check.sh b/scripts/check.sh index ad5c18ab0eb..29731d93684 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -131,6 +131,8 @@ while [[ $# -gt 0 ]]; do shift done +set -x + if [[ "${TEST_ONLY}" == true && "${COMMIT_METHOD}" != "none" ]]; then echo "--test-only cannot be combined with --amend, --fixup, or --commit" exit 1 From 9ccd97a9c42b4ac4018dff7e20c298b0ae5f071c Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Thu, 11 Apr 2019 12:07:17 -0700 Subject: [PATCH 201/214] Use enum for DelayedConstructor<> so XCode can decode it in the debugger window... (#2805) --- .../firestore/util/delayed_constructor.h | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Firestore/core/src/firebase/firestore/util/delayed_constructor.h b/Firestore/core/src/firebase/firestore/util/delayed_constructor.h index 9c00e515158..c45b1868476 100644 --- a/Firestore/core/src/firebase/firestore/util/delayed_constructor.h +++ b/Firestore/core/src/firebase/firestore/util/delayed_constructor.h @@ -62,11 +62,7 @@ class DelayedConstructor { public: typedef T element_type; - /** - * Default constructor does nothing. - */ - DelayedConstructor() { - } + DelayedConstructor() = default; /** * Forwards arguments to the T's constructor: calls T(args...). @@ -79,27 +75,23 @@ class DelayedConstructor { void(DelayedConstructor)>::value, int>::type = 0> void Init(Ts&&... args) { - new (&space_) T(std::forward(args)...); + new (&space_.value) T(std::forward(args)...); } /** * Forwards copy and move construction for T. */ void Init(const T& x) { - new (&space_) T(x); + new (&space_.value) T(x); } void Init(T&& x) { - new (&space_) T(std::move(x)); + new (&space_.value) T(std::move(x)); } // No copying. DelayedConstructor(const DelayedConstructor&) = delete; DelayedConstructor& operator=(const DelayedConstructor&) = delete; - ~DelayedConstructor() { - get()->~T(); - } - // Pretend to be a smart pointer to T. T& operator*() { return *get(); @@ -108,7 +100,7 @@ class DelayedConstructor { return get(); } T* get() { - return reinterpret_cast(&space_); + return &space_.value; } const T& operator*() const { return *get(); @@ -117,11 +109,20 @@ class DelayedConstructor { return get(); } const T* get() const { - return reinterpret_cast(&space_); + return &space_.value; } private: - typename std::aligned_storage::type space_; + union Space { + /** Default constructor does nothing. */ + Space() { + } + ~Space() { + value.~T(); + } + char empty; + T value; + } space_; }; } // namespace util From 2a97c2ea5f91da105555fb22a5a3a520da8707ef Mon Sep 17 00:00:00 2001 From: Gil Date: Thu, 11 Apr 2019 13:35:09 -0700 Subject: [PATCH 202/214] Port FSTRelationFilterOperator to Filter::Operator (#2812) Note: * This includes a partial port of array-contains for Filters because this silenced a warning about an unhandled case * There's some sensitivity to the order of the cases that I didn't bother to figure out; instead I just made it match what came before. --- Firestore/Example/Tests/Core/FSTViewTests.mm | 16 +++--- Firestore/Example/Tests/Util/FSTHelpers.mm | 16 +++--- Firestore/Source/API/FIRQuery.mm | 42 +++++++--------- Firestore/Source/Core/FSTQuery.h | 24 +++------ Firestore/Source/Core/FSTQuery.mm | 50 ++++++++++--------- Firestore/Source/Remote/FSTSerializerBeta.mm | 34 +++++++------ .../core/src/firebase/firestore/core/filter.h | 8 ++- .../firestore/core/relation_filter.cc | 17 +++++-- 8 files changed, 107 insertions(+), 100 deletions(-) diff --git a/Firestore/Example/Tests/Core/FSTViewTests.mm b/Firestore/Example/Tests/Core/FSTViewTests.mm index 4245d072e6f..1c28d926e93 100644 --- a/Firestore/Example/Tests/Core/FSTViewTests.mm +++ b/Firestore/Example/Tests/Core/FSTViewTests.mm @@ -29,6 +29,7 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/core/filter.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/document_set.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" @@ -38,6 +39,7 @@ namespace testutil = firebase::firestore::testutil; using firebase::firestore::core::DocumentViewChange; +using firebase::firestore::core::Filter; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::ResourcePath; using firebase::firestore::model::DocumentKeySet; @@ -169,10 +171,9 @@ - (void)testDoesNotReturnNilForFirstChanges { - (void)testFiltersDocumentsBasedOnQueryWithFilter { FSTQuery *query = [self queryForMessages]; - FSTRelationFilter *filter = - [FSTRelationFilter filterWithField:testutil::Field("sort") - filterOperator:FSTRelationFilterOperatorLessThanOrEqual - value:[FSTDoubleValue doubleValue:2]]; + FSTRelationFilter *filter = [FSTRelationFilter filterWithField:testutil::Field("sort") + filterOperator:Filter::Operator::LessThanOrEqual + value:[FSTDoubleValue doubleValue:2]]; query = [query queryByAddingFilter:filter]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; @@ -208,10 +209,9 @@ - (void)testFiltersDocumentsBasedOnQueryWithFilter { - (void)testUpdatesDocumentsBasedOnQueryWithFilter { FSTQuery *query = [self queryForMessages]; - FSTRelationFilter *filter = - [FSTRelationFilter filterWithField:testutil::Field("sort") - filterOperator:FSTRelationFilterOperatorLessThanOrEqual - value:[FSTDoubleValue doubleValue:2]]; + FSTRelationFilter *filter = [FSTRelationFilter filterWithField:testutil::Field("sort") + filterOperator:Filter::Operator::LessThanOrEqual + value:[FSTDoubleValue doubleValue:2]]; query = [query queryByAddingFilter:filter]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm index 1ce94651b84..fcffe6b8ca2 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.mm +++ b/Firestore/Example/Tests/Util/FSTHelpers.mm @@ -35,6 +35,7 @@ #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Model/FSTMutation.h" +#include "Firestore/core/src/firebase/firestore/core/filter.h" #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" @@ -54,6 +55,7 @@ namespace testutil = firebase::firestore::testutil; namespace util = firebase::firestore::util; +using firebase::firestore::core::Filter; using firebase::firestore::core::ParsedUpdateData; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::DatabaseId; @@ -195,19 +197,19 @@ DocumentKey FSTTestDocKey(NSString *path) { FSTFilter *FSTTestFilter(const absl::string_view field, NSString *opString, id value) { const FieldPath path = testutil::Field(field); - FSTRelationFilterOperator op; + Filter::Operator op; if ([opString isEqualToString:@"<"]) { - op = FSTRelationFilterOperatorLessThan; + op = Filter::Operator::LessThan; } else if ([opString isEqualToString:@"<="]) { - op = FSTRelationFilterOperatorLessThanOrEqual; + op = Filter::Operator::LessThanOrEqual; } else if ([opString isEqualToString:@"=="]) { - op = FSTRelationFilterOperatorEqual; + op = Filter::Operator::Equal; } else if ([opString isEqualToString:@">="]) { - op = FSTRelationFilterOperatorGreaterThanOrEqual; + op = Filter::Operator::GreaterThanOrEqual; } else if ([opString isEqualToString:@">"]) { - op = FSTRelationFilterOperatorGreaterThan; + op = Filter::Operator::GreaterThan; } else if ([opString isEqualToString:@"array_contains"]) { - op = FSTRelationFilterOperatorArrayContains; + op = Filter::Operator::ArrayContains; } else { HARD_FAIL("Unsupported operator type: %s", opString); } diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 78a15d9432c..9b09bc1fc6e 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -40,6 +40,7 @@ #import "Firestore/Source/Model/FSTFieldValue.h" #include "Firestore/core/src/firebase/firestore/api/input_validation.h" +#include "Firestore/core/src/firebase/firestore/core/filter.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/field_value.h" @@ -55,6 +56,7 @@ using firebase::firestore::api::ThrowInvalidArgument; using firebase::firestore::core::AsyncEventListener; using firebase::firestore::core::EventListener; +using firebase::firestore::core::Filter; using firebase::firestore::core::ViewSnapshot; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldPath; @@ -211,69 +213,61 @@ ListenOptions listenOptions( } - (FIRQuery *)queryWhereField:(NSString *)field isEqualTo:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorEqual field:field value:value]; + return [self queryWithFilterOperator:Filter::Operator::Equal field:field value:value]; } - (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isEqualTo:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorEqual - path:path.internalValue - value:value]; + return [self queryWithFilterOperator:Filter::Operator::Equal path:path.internalValue value:value]; } - (FIRQuery *)queryWhereField:(NSString *)field isLessThan:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorLessThan field:field value:value]; + return [self queryWithFilterOperator:Filter::Operator::LessThan field:field value:value]; } - (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isLessThan:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorLessThan + return [self queryWithFilterOperator:Filter::Operator::LessThan path:path.internalValue value:value]; } - (FIRQuery *)queryWhereField:(NSString *)field isLessThanOrEqualTo:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorLessThanOrEqual - field:field - value:value]; + return [self queryWithFilterOperator:Filter::Operator::LessThanOrEqual field:field value:value]; } - (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isLessThanOrEqualTo:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorLessThanOrEqual + return [self queryWithFilterOperator:Filter::Operator::LessThanOrEqual path:path.internalValue value:value]; } - (FIRQuery *)queryWhereField:(NSString *)field isGreaterThan:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorGreaterThan - field:field - value:value]; + return [self queryWithFilterOperator:Filter::Operator::GreaterThan field:field value:value]; } - (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isGreaterThan:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorGreaterThan + return [self queryWithFilterOperator:Filter::Operator::GreaterThan path:path.internalValue value:value]; } - (FIRQuery *)queryWhereField:(NSString *)field arrayContains:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorArrayContains - field:field - value:value]; + return [self queryWithFilterOperator:Filter::Operator::ArrayContains field:field value:value]; } - (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path arrayContains:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorArrayContains + return [self queryWithFilterOperator:Filter::Operator::ArrayContains path:path.internalValue value:value]; } - (FIRQuery *)queryWhereField:(NSString *)field isGreaterThanOrEqualTo:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorGreaterThanOrEqual + return [self queryWithFilterOperator:Filter::Operator::GreaterThanOrEqual field:field value:value]; } - (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isGreaterThanOrEqualTo:(id)value { - return [self queryWithFilterOperator:FSTRelationFilterOperatorGreaterThanOrEqual + return [self queryWithFilterOperator:Filter::Operator::GreaterThanOrEqual path:path.internalValue value:value]; } @@ -452,7 +446,7 @@ - (FIRQuery *)queryEndingAtValues:(NSArray *)fieldValues { #pragma mark - Private Methods /** Private helper for all of the queryWhereField: methods. */ -- (FIRQuery *)queryWithFilterOperator:(FSTRelationFilterOperator)filterOperator +- (FIRQuery *)queryWithFilterOperator:(Filter::Operator)filterOperator field:(NSString *)field value:(id)value { return [self queryWithFilterOperator:filterOperator @@ -460,12 +454,12 @@ - (FIRQuery *)queryWithFilterOperator:(FSTRelationFilterOperator)filterOperator value:value]; } -- (FIRQuery *)queryWithFilterOperator:(FSTRelationFilterOperator)filterOperator +- (FIRQuery *)queryWithFilterOperator:(Filter::Operator)filterOperator path:(const FieldPath &)fieldPath value:(id)value { FSTFieldValue *fieldValue; if (fieldPath.IsKeyFieldPath()) { - if (filterOperator == FSTRelationFilterOperatorArrayContains) { + if (filterOperator == Filter::Operator::ArrayContains) { ThrowInvalidArgument("Invalid query. You can't perform arrayContains queries on document ID " "since document IDs are not arrays."); } @@ -531,7 +525,7 @@ - (void)validateNewRelationFilter:(FSTRelationFilter *)filter { if (firstOrderByField) { [self validateOrderByField:*firstOrderByField matchesInequalityField:filter.field]; } - } else if (filter.filterOperator == FSTRelationFilterOperatorArrayContains) { + } else if (filter.filterOperator == Filter::Operator::ArrayContains) { if ([self.query hasArrayContainsFilter]) { ThrowInvalidArgument("Invalid Query. Queries only support a single arrayContains filter."); } diff --git a/Firestore/Source/Core/FSTQuery.h b/Firestore/Source/Core/FSTQuery.h index 851da923d53..91268772e4c 100644 --- a/Firestore/Source/Core/FSTQuery.h +++ b/Firestore/Source/Core/FSTQuery.h @@ -16,26 +16,16 @@ #import +#include "Firestore/core/src/firebase/firestore/core/filter.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" @class FSTDocument; @class FSTFieldValue; -NS_ASSUME_NONNULL_BEGIN +using firebase::firestore::core::Filter; -/** - * FSTRelationFilterOperator is a value relation operator that can be used to filter documents. - * It is similar to NSPredicateOperatorType, but only has operators supported by Firestore. - */ -typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { - FSTRelationFilterOperatorLessThan = 0, - FSTRelationFilterOperatorLessThanOrEqual, - FSTRelationFilterOperatorEqual, - FSTRelationFilterOperatorGreaterThanOrEqual, - FSTRelationFilterOperatorGreaterThan, - FSTRelationFilterOperatorArrayContains, -}; +NS_ASSUME_NONNULL_BEGIN /** Interface used for all query filters. */ @interface FSTFilter : NSObject @@ -43,13 +33,13 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { /** * Creates a filter for the provided path, operator, and value. * - * Note that if the relational operator is FSTRelationFilterOperatorEqual and + * Note that if the relational operator is Filter::Operator::Equal and * the value is [FSTNullValue nullValue] or [FSTDoubleValue nanValue], this * will return the appropriate FSTNullFilter or FSTNanFilter class instead of a * FSTRelationFilter. */ + (instancetype)filterWithField:(const firebase::firestore::model::FieldPath &)field - filterOperator:(FSTRelationFilterOperator)op + filterOperator:(Filter::Operator)op value:(FSTFieldValue *)value; /** Returns the field the Filter operates over. Abstract method. */ @@ -78,7 +68,7 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { * @return A new instance of FSTRelationFilter. */ - (instancetype)initWithField:(firebase::firestore::model::FieldPath)field - filterOperator:(FSTRelationFilterOperator)filterOperator + filterOperator:(Filter::Operator)filterOperator value:(FSTFieldValue *)value; - (instancetype)init NS_UNAVAILABLE; @@ -90,7 +80,7 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { - (const firebase::firestore::model::FieldPath &)field; /** The type of equality/inequality operator to use in the relation. */ -@property(nonatomic, assign, readonly) FSTRelationFilterOperator filterOperator; +@property(nonatomic, assign, readonly) Filter::Operator filterOperator; /** The right hand side of the relation. A constant value to compare to. */ @property(nonatomic, strong, readonly) FSTFieldValue *value; diff --git a/Firestore/Source/Core/FSTQuery.mm b/Firestore/Source/Core/FSTQuery.mm index 389b0224c00..9304a2df0ff 100644 --- a/Firestore/Source/Core/FSTQuery.mm +++ b/Firestore/Source/Core/FSTQuery.mm @@ -26,6 +26,7 @@ #import "Firestore/Source/Util/FSTClasses.h" #include "Firestore/core/src/firebase/firestore/api/input_validation.h" +#include "Firestore/core/src/firebase/firestore/core/filter.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/field_value.h" @@ -36,6 +37,7 @@ namespace util = firebase::firestore::util; using firebase::firestore::api::ThrowInvalidArgument; +using firebase::firestore::core::Filter; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldPath; using firebase::firestore::model::FieldValue; @@ -43,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN -#pragma mark - FSTRelationFilterOperator functions +#pragma mark - Filter::Operator functions /** * Returns the reverse order (i.e. Ascending => Descending) etc. @@ -52,37 +54,37 @@ static constexpr NSComparisonResult ReverseOrder(NSComparisonResult result) { return static_cast(-static_cast(result)); } -NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOperator) { +NSString *FSTStringFromQueryRelationOperator(Filter::Operator filterOperator) { switch (filterOperator) { - case FSTRelationFilterOperatorLessThan: + case Filter::Operator::LessThan: return @"<"; - case FSTRelationFilterOperatorLessThanOrEqual: + case Filter::Operator::LessThanOrEqual: return @"<="; - case FSTRelationFilterOperatorEqual: + case Filter::Operator::Equal: return @"=="; - case FSTRelationFilterOperatorGreaterThanOrEqual: + case Filter::Operator::GreaterThanOrEqual: return @">="; - case FSTRelationFilterOperatorGreaterThan: + case Filter::Operator::GreaterThan: return @">"; - case FSTRelationFilterOperatorArrayContains: + case Filter::Operator::ArrayContains: return @"array_contains"; default: - HARD_FAIL("Unknown FSTRelationFilterOperator %s", filterOperator); + HARD_FAIL("Unknown Filter::Operator %s", filterOperator); } } @implementation FSTFilter + (instancetype)filterWithField:(const FieldPath &)field - filterOperator:(FSTRelationFilterOperator)op + filterOperator:(Filter::Operator)op value:(FSTFieldValue *)value { if ([value isEqual:[FSTNullValue nullValue]]) { - if (op != FSTRelationFilterOperatorEqual) { + if (op != Filter::Operator::Equal) { ThrowInvalidArgument("Invalid Query. Nil and NSNull only support equality comparisons."); } return [[FSTNullFilter alloc] initWithField:field]; } else if ([value isEqual:[FSTDoubleValue nanValue]]) { - if (op != FSTRelationFilterOperatorEqual) { + if (op != Filter::Operator::Equal) { ThrowInvalidArgument("Invalid Query. NaN only supports equality comparisons."); } return [[FSTNanFilter alloc] initWithField:field]; @@ -120,7 +122,7 @@ @interface FSTRelationFilter () { * @param value A constant value to compare @a field to. The RHS of the expression. */ - (instancetype)initWithField:(FieldPath)field - filterOperator:(FSTRelationFilterOperator)filterOperator + filterOperator:(Filter::Operator)filterOperator value:(FSTFieldValue *)value NS_DESIGNATED_INITIALIZER; /** Returns YES if @a document matches the receiver's constraint. */ @@ -139,7 +141,7 @@ @implementation FSTRelationFilter #pragma mark - Constructor methods - (instancetype)initWithField:(FieldPath)field - filterOperator:(FSTRelationFilterOperator)filterOperator + filterOperator:(Filter::Operator)filterOperator value:(FSTFieldValue *)value { self = [super init]; if (self) { @@ -153,8 +155,8 @@ - (instancetype)initWithField:(FieldPath)field #pragma mark - Public Methods - (BOOL)isInequality { - return self.filterOperator != FSTRelationFilterOperatorEqual && - self.filterOperator != FSTRelationFilterOperatorArrayContains; + return self.filterOperator != Filter::Operator::Equal && + self.filterOperator != Filter::Operator::ArrayContains; } - (const firebase::firestore::model::FieldPath &)field { @@ -185,7 +187,7 @@ - (BOOL)matchesDocument:(FSTDocument *)document { if (_field.IsKeyFieldPath()) { HARD_ASSERT(self.value.type == FieldValue::Type::Reference, "Comparing on key, but filter value not a FSTReferenceValue."); - HARD_ASSERT(self.filterOperator != FSTRelationFilterOperatorArrayContains, + HARD_ASSERT(self.filterOperator != Filter::Operator::ArrayContains, "arrayContains queries don't make sense on document keys."); FSTReferenceValue *refValue = (FSTReferenceValue *)self.value; NSComparisonResult comparison = CompareKeys(document.key, refValue.value.key); @@ -217,7 +219,7 @@ - (BOOL)isEqualToFilter:(FSTRelationFilter *)other { /** Returns YES if receiver is true with the given value as its LHS. */ - (BOOL)matchesValue:(FSTFieldValue *)other { - if (self.filterOperator == FSTRelationFilterOperatorArrayContains) { + if (self.filterOperator == Filter::Operator::ArrayContains) { if ([other isMemberOfClass:[FSTArrayValue class]]) { FSTArrayValue *arrayValue = (FSTArrayValue *)other; return [arrayValue.internalValue containsObject:self.value]; @@ -234,15 +236,15 @@ - (BOOL)matchesValue:(FSTFieldValue *)other { - (BOOL)matchesComparison:(NSComparisonResult)comparison { switch (self.filterOperator) { - case FSTRelationFilterOperatorLessThan: + case Filter::Operator::LessThan: return comparison == NSOrderedAscending; - case FSTRelationFilterOperatorLessThanOrEqual: + case Filter::Operator::LessThanOrEqual: return comparison == NSOrderedAscending || comparison == NSOrderedSame; - case FSTRelationFilterOperatorEqual: + case Filter::Operator::Equal: return comparison == NSOrderedSame; - case FSTRelationFilterOperatorGreaterThanOrEqual: + case Filter::Operator::GreaterThanOrEqual: return comparison == NSOrderedDescending || comparison == NSOrderedSame; - case FSTRelationFilterOperatorGreaterThan: + case Filter::Operator::GreaterThan: return comparison == NSOrderedDescending; default: HARD_FAIL("Unknown operator: %s", self.filterOperator); @@ -771,7 +773,7 @@ - (nullable const FieldPath *)inequalityFilterField { - (BOOL)hasArrayContainsFilter { for (FSTFilter *filter in self.filters) { if ([filter isKindOfClass:[FSTRelationFilter class]] && - ((FSTRelationFilter *)filter).filterOperator == FSTRelationFilterOperatorArrayContains) { + ((FSTRelationFilter *)filter).filterOperator == Filter::Operator::ArrayContains) { return YES; } } diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm index ffa8d916037..69b7c7f3665 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.mm +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -41,6 +41,7 @@ #import "Firestore/Source/Model/FSTMutationBatch.h" #include "Firestore/core/include/firebase/firestore/firestore_errors.h" +#include "Firestore/core/src/firebase/firestore/core/filter.h" #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" @@ -60,6 +61,7 @@ namespace util = firebase::firestore::util; using firebase::Timestamp; using firebase::firestore::FirestoreErrorCode; +using firebase::firestore::core::Filter; using firebase::firestore::model::ArrayTransform; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; @@ -941,7 +943,7 @@ - (GCFSStructuredQuery_Filter *)encodedRelationFilter:(FSTRelationFilter *)filte - (FSTRelationFilter *)decodedRelationFilter:(GCFSStructuredQuery_FieldFilter *)proto { FieldPath fieldPath = FieldPath::FromServerFormat(util::MakeString(proto.field.fieldPath)); - FSTRelationFilterOperator filterOperator = [self decodedRelationFilterOperator:proto.op]; + Filter::Operator filterOperator = [self decodedRelationFilterOperator:proto.op]; FSTFieldValue *value = [self decodedFieldValue:proto.value]; return [FSTRelationFilter filterWithField:fieldPath filterOperator:filterOperator value:value]; } @@ -980,40 +982,40 @@ - (GCFSStructuredQuery_FieldReference *)encodedFieldPath:(const FieldPath &)fiel } - (GCFSStructuredQuery_FieldFilter_Operator)encodedRelationFilterOperator: - (FSTRelationFilterOperator)filterOperator { + (Filter::Operator)filterOperator { switch (filterOperator) { - case FSTRelationFilterOperatorLessThan: + case Filter::Operator::LessThan: return GCFSStructuredQuery_FieldFilter_Operator_LessThan; - case FSTRelationFilterOperatorLessThanOrEqual: + case Filter::Operator::LessThanOrEqual: return GCFSStructuredQuery_FieldFilter_Operator_LessThanOrEqual; - case FSTRelationFilterOperatorEqual: + case Filter::Operator::Equal: return GCFSStructuredQuery_FieldFilter_Operator_Equal; - case FSTRelationFilterOperatorGreaterThanOrEqual: + case Filter::Operator::GreaterThanOrEqual: return GCFSStructuredQuery_FieldFilter_Operator_GreaterThanOrEqual; - case FSTRelationFilterOperatorGreaterThan: + case Filter::Operator::GreaterThan: return GCFSStructuredQuery_FieldFilter_Operator_GreaterThan; - case FSTRelationFilterOperatorArrayContains: + case Filter::Operator::ArrayContains: return GCFSStructuredQuery_FieldFilter_Operator_ArrayContains; default: - HARD_FAIL("Unhandled FSTRelationFilterOperator: %s", filterOperator); + HARD_FAIL("Unhandled Filter::Operator: %s", filterOperator); } } -- (FSTRelationFilterOperator)decodedRelationFilterOperator: +- (Filter::Operator)decodedRelationFilterOperator: (GCFSStructuredQuery_FieldFilter_Operator)filterOperator { switch (filterOperator) { case GCFSStructuredQuery_FieldFilter_Operator_LessThan: - return FSTRelationFilterOperatorLessThan; + return Filter::Operator::LessThan; case GCFSStructuredQuery_FieldFilter_Operator_LessThanOrEqual: - return FSTRelationFilterOperatorLessThanOrEqual; + return Filter::Operator::LessThanOrEqual; case GCFSStructuredQuery_FieldFilter_Operator_Equal: - return FSTRelationFilterOperatorEqual; + return Filter::Operator::Equal; case GCFSStructuredQuery_FieldFilter_Operator_GreaterThanOrEqual: - return FSTRelationFilterOperatorGreaterThanOrEqual; + return Filter::Operator::GreaterThanOrEqual; case GCFSStructuredQuery_FieldFilter_Operator_GreaterThan: - return FSTRelationFilterOperatorGreaterThan; + return Filter::Operator::GreaterThan; case GCFSStructuredQuery_FieldFilter_Operator_ArrayContains: - return FSTRelationFilterOperatorArrayContains; + return Filter::Operator::ArrayContains; default: HARD_FAIL("Unhandled FieldFilter.operator: %s", filterOperator); } diff --git a/Firestore/core/src/firebase/firestore/core/filter.h b/Firestore/core/src/firebase/firestore/core/filter.h index 47485667e17..8f6cadd2b63 100644 --- a/Firestore/core/src/firebase/firestore/core/filter.h +++ b/Firestore/core/src/firebase/firestore/core/filter.h @@ -31,12 +31,18 @@ namespace core { /** Interface used for all query filters. All filters are immutable. */ class Filter { public: + /** + * Operator is a value relation operator that can be used to filter documents. + * It is similar to NSPredicateOperatorType, but only has operators supported + * by Firestore. + */ enum class Operator { LessThan, LessThanOrEqual, Equal, - GreaterThan, GreaterThanOrEqual, + GreaterThan, + ArrayContains, }; /** diff --git a/Firestore/core/src/firebase/firestore/core/relation_filter.cc b/Firestore/core/src/firebase/firestore/core/relation_filter.cc index 07087e6e121..731d1f59259 100644 --- a/Firestore/core/src/firebase/firestore/core/relation_filter.cc +++ b/Firestore/core/src/firebase/firestore/core/relation_filter.cc @@ -17,7 +17,9 @@ #include "Firestore/core/src/firebase/firestore/core/relation_filter.h" #include +#include +#include "absl/algorithm/container.h" #include "absl/types/optional.h" namespace firebase { @@ -48,9 +50,16 @@ bool RelationFilter::Matches(const model::Document& doc) const { } bool RelationFilter::MatchesValue(const FieldValue& other) const { - // Only compare types with matching backend order (such as double and int). - return FieldValue::Comparable(other.type(), value_rhs_.type()) && - MatchesComparison(other); + if (op_ == Filter::Operator::ArrayContains) { + if (other.type() != FieldValue::Type::Array) return false; + + const std::vector& contents = other.array_value(); + return absl::c_linear_search(contents, value_rhs_); + } else { + // Only compare types with matching backend order (such as double and int). + return FieldValue::Comparable(other.type(), value_rhs_.type()) && + MatchesComparison(other); + } } bool RelationFilter::MatchesComparison(const FieldValue& other) const { @@ -65,6 +74,8 @@ bool RelationFilter::MatchesComparison(const FieldValue& other) const { return other > value_rhs_; case Operator::GreaterThanOrEqual: return other >= value_rhs_; + case Operator::ArrayContains: + HARD_FAIL("Should have been handled in MatchesValue()"); } UNREACHABLE(); } From 8109c832bd8004a454611f84cad698df168d2809 Mon Sep 17 00:00:00 2001 From: davidair Date: Thu, 11 Apr 2019 17:05:08 -0400 Subject: [PATCH 203/214] Adding a missing LorOrRun (#2814) --- Releases/update-versions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Releases/update-versions.py b/Releases/update-versions.py index c05f30156dc..f649da2434a 100755 --- a/Releases/update-versions.py +++ b/Releases/update-versions.py @@ -204,7 +204,7 @@ def PushPodspecs(version_data): podspec = '{}.podspec'.format(pod) json = os.path.join(tmp_dir, '{}.json'.format(podspec)) - os.system('pod ipc spec {} > {}'.format(podspec, json)) + LogOrRun('pod ipc spec {} > {}'.format(podspec, json)) LogOrRun('pod repo push {} {}{}'.format(GetCpdcInternal(), json, warnings_ok)) os.system('rm -rf {}'.format(tmp_dir)) From e3cafd4dfd1079cca0f0393c73813839c31f03fb Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 11 Apr 2019 14:19:13 -0700 Subject: [PATCH 204/214] GoogleUtilities to 5.8.0 (#2813) --- FirebaseAuth.podspec | 4 ++-- FirebaseMessaging.podspec | 8 ++++---- GoogleUtilities.podspec | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index f079b7f5da8..132174a1e88 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -63,7 +63,7 @@ supports email and password accounts, as well as several 3rd party authenticatio s.ios.framework = 'SafariServices' s.dependency 'FirebaseAuthInterop', '~> 1.0' s.dependency 'FirebaseCore', '~> 6.0' - s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.6' - s.dependency 'GoogleUtilities/Environment', '~> 5.6' + s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.8' + s.dependency 'GoogleUtilities/Environment', '~> 5.8' s.dependency 'GTMSessionFetcher/Core', '~> 1.1' end diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index 3a04fd19a62..d0bcef47fe1 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -41,9 +41,9 @@ device, and it is completely free. s.dependency 'FirebaseAnalyticsInterop', '~> 1.1' s.dependency 'FirebaseCore', '~> 6.0' s.dependency 'FirebaseInstanceID', '~> 4.0' - s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.6' - s.dependency 'GoogleUtilities/Reachability', '~> 5.6' - s.dependency 'GoogleUtilities/Environment', '~> 5.6' - s.dependency 'GoogleUtilities/UserDefaults', '~> 5.6' + s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 5.8' + s.dependency 'GoogleUtilities/Reachability', '~> 5.8' + s.dependency 'GoogleUtilities/Environment', '~> 5.8' + s.dependency 'GoogleUtilities/UserDefaults', '~> 5.8' s.dependency 'Protobuf', '~> 3.1' end diff --git a/GoogleUtilities.podspec b/GoogleUtilities.podspec index 4ac8a4324c9..8703e2d5c15 100644 --- a/GoogleUtilities.podspec +++ b/GoogleUtilities.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleUtilities' - s.version = '5.6.0' + s.version = '5.8.0' s.summary = 'Google Utilities for iOS (plus community support for macOS and tvOS)' s.description = <<-DESC From db2701b480ba5795c2189e80fe2bbae4e1c127f5 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Thu, 11 Apr 2019 14:44:11 -0700 Subject: [PATCH 205/214] Take 2: Move input validation logic from FIRFieldPath to model::FieldPath. (#2727) (#2790) * Move input validation logic from FIRFieldPath to model::FieldPath. (#2727) --- Firestore/Source/API/FIRFieldPath.mm | 22 ++---------- .../firebase/firestore/model/CMakeLists.txt | 1 + .../firebase/firestore/model/field_path.cc | 25 +++++++++++++ .../src/firebase/firestore/model/field_path.h | 36 +++++++++++++++++++ 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/Firestore/Source/API/FIRFieldPath.mm b/Firestore/Source/API/FIRFieldPath.mm index 9ffbc3ea60f..cdf67196e6b 100644 --- a/Firestore/Source/API/FIRFieldPath.mm +++ b/Firestore/Source/API/FIRFieldPath.mm @@ -51,13 +51,10 @@ - (instancetype)initWithFields:(NSArray *)fieldNames { std::vector field_names; field_names.reserve(fieldNames.count); for (int i = 0; i < fieldNames.count; ++i) { - if (fieldNames[i].length == 0) { - ThrowInvalidArgument("Invalid field name at index %s. Field names must not be empty.", i); - } field_names.emplace_back(util::MakeString(fieldNames[i])); } - return [self initPrivate:FieldPath(std::move(field_names))]; + return [self initPrivate:FieldPath::FromSegments(std::move(field_names))]; } + (instancetype)documentID { @@ -72,21 +69,8 @@ - (instancetype)initPrivate:(FieldPath)fieldPath { } + (instancetype)pathWithDotSeparatedString:(NSString *)path { - if ([[FIRFieldPath reservedCharactersRegex] - numberOfMatchesInString:path - options:0 - range:NSMakeRange(0, path.length)] > 0) { - ThrowInvalidArgument( - "Invalid field path (%s). Paths must not contain '~', '*', '/', '[', or ']'", path); - } - @try { - return [[FIRFieldPath alloc] initWithFields:[path componentsSeparatedByString:@"."]]; - } @catch (NSException *exception) { - ThrowInvalidArgument( - "Invalid field path (%s). Paths must not be empty, begin with '.', end with '.', or " - "contain '..'", - path); - } + return + [[FIRFieldPath alloc] initPrivate:FieldPath::FromDotSeparatedString(util::MakeString(path))]; } /** Matches any characters in a field path string that are reserved. */ diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt index 170bf8e83a3..c83dd12b726 100644 --- a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt @@ -50,6 +50,7 @@ cc_library( DEPENDS absl_optional absl_strings + firebase_firestore_api_input_validation firebase_firestore_immutable firebase_firestore_util firebase_firestore_types diff --git a/Firestore/core/src/firebase/firestore/model/field_path.cc b/Firestore/core/src/firebase/firestore/model/field_path.cc index b1dcad75a65..fd06e98eb79 100644 --- a/Firestore/core/src/firebase/firestore/model/field_path.cc +++ b/Firestore/core/src/firebase/firestore/model/field_path.cc @@ -19,6 +19,7 @@ #include #include +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" @@ -28,6 +29,8 @@ namespace firebase { namespace firestore { namespace model { +using api::ThrowInvalidArgument; + namespace { /** @@ -78,6 +81,28 @@ struct JoinEscaped { }; } // namespace +FieldPath FieldPath::FromDotSeparatedString(absl::string_view path) { + if (path.find_first_of("~*/[]") != absl::string_view::npos) { + ThrowInvalidArgument( + "Invalid field path (%s). Paths must not contain '~', '*', '/', '[', " + "or ']'", + path); + } + + SegmentsT segments = + absl::StrSplit(path, '.', [path](absl::string_view segment) { + if (segment.empty()) { + ThrowInvalidArgument( + "Invalid field path (%s). Paths must not be empty, begin with " + "'.', end with '.', or contain '..'", + path); + } + return true; + }); + + return FieldPath(std::move(segments)); +} + FieldPath FieldPath::FromServerFormat(const absl::string_view path) { SegmentsT segments; std::string segment; diff --git a/Firestore/core/src/firebase/firestore/model/field_path.h b/Firestore/core/src/firebase/firestore/model/field_path.h index ce3527d920c..609c9f54307 100644 --- a/Firestore/core/src/firebase/firestore/model/field_path.h +++ b/Firestore/core/src/firebase/firestore/model/field_path.h @@ -21,6 +21,7 @@ #include #include +#include "Firestore/core/src/firebase/firestore/api/input_validation.h" #include "Firestore/core/src/firebase/firestore/model/base_path.h" #include "absl/strings/string_view.h" @@ -51,6 +52,25 @@ class FieldPath : public impl::BasePath { explicit FieldPath(SegmentsT&& segments) : BasePath{std::move(segments)} { } + /** + * Creates and returns a new path from a dot-separated field-path string, + * where path segments are separated by a dot ".". + * + * PORTING NOTE: We define this on the model class to avoid having a tiny + * api::FieldPath wrapper class. + */ + static FieldPath FromDotSeparatedString(absl::string_view path); + + /** + * Creates and returns a new path from a set of segments received from the + * public API. + */ + static FieldPath FromSegments(SegmentsT&& segments) { + ValidateSegments(segments); + FieldPath path(std::move(segments)); + return path; + } + /** * Creates and returns a new path from the server formatted field-path string, * where path segments are separated by a dot "." and optionally encoded using @@ -85,6 +105,22 @@ class FieldPath : public impl::BasePath { bool operator>=(const FieldPath& rhs) const { return BasePath::operator>=(rhs); } + + private: + static void ValidateSegments(const SegmentsT& segments) { + if (segments.empty()) { + api::ThrowInvalidArgument( + "Invalid field path. Provided names must not be empty."); + } + + for (size_t i = 0; i < segments.size(); i++) { + if (segments[i].empty()) { + api::ThrowInvalidArgument( + "Invalid field name at index %s. Field names must not be empty.", + i); + } + } + } }; } // namespace model From 8c356798fc603157bf0766bd4f6f4c1c2e55cc32 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 11 Apr 2019 17:39:52 -0700 Subject: [PATCH 206/214] Skip tests on pod repo push (#2815) --- Releases/update-versions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Releases/update-versions.py b/Releases/update-versions.py index f649da2434a..1a06ac16a51 100755 --- a/Releases/update-versions.py +++ b/Releases/update-versions.py @@ -205,8 +205,8 @@ def PushPodspecs(version_data): podspec = '{}.podspec'.format(pod) json = os.path.join(tmp_dir, '{}.json'.format(podspec)) LogOrRun('pod ipc spec {} > {}'.format(podspec, json)) - LogOrRun('pod repo push {} {}{}'.format(GetCpdcInternal(), json, - warnings_ok)) + LogOrRun('pod repo push --skip-tests {} {}{}'.format(GetCpdcInternal(), + json, warnings_ok)) os.system('rm -rf {}'.format(tmp_dir)) From 00feaca45a82552d46f09fb0a69544bf89a05034 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 12 Apr 2019 07:25:27 -0700 Subject: [PATCH 207/214] Update GoogleUtilities CHANGELOG (#2817) --- GoogleUtilities/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/GoogleUtilities/CHANGELOG.md b/GoogleUtilities/CHANGELOG.md index a4f452dfaf9..1335e961b83 100644 --- a/GoogleUtilities/CHANGELOG.md +++ b/GoogleUtilities/CHANGELOG.md @@ -1,4 +1,9 @@ # Unreleased + +# 5.7.0 +- Restore to 5.5.0 tag after increased App Store warnings. (#2807) + +# 5.6.0 - `GULAppDelegateSwizzler`: support of remote notification methods. (#2698) - `GULAppDelegateSwizzler`: tvOS support. (#2698) From 63e3cb682f92c6e689393e84b72b9b6bfc72d789 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 12 Apr 2019 10:45:57 -0400 Subject: [PATCH 208/214] Add quick instructions for building the Zip. (#2819) Added some instructions to build the zip. Will expand further with releasing information. --- ZipBuilder/README.md | 69 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/ZipBuilder/README.md b/ZipBuilder/README.md index 8759b101857..54586f73e46 100644 --- a/ZipBuilder/README.md +++ b/ZipBuilder/README.md @@ -1,7 +1,74 @@ # Firebase Zip File Builder This project builds the Firebase iOS Zip file for distribution. -More instructions to come. + +## Overview + +This is a small Swift Package Manager project that allows users to package a Firebase iOS Zip file. With no launch +arguments, it will use the most recent public versions of all SDKs included in the zip file. + +It was designed to fail fast with an explanation of what went wrong, so you can fix issues or dig in without having to dig +too deep into the code. + +## Requirements + +In order to build the Zip file, you will need: + +- Xcode 10.1 +- CocoaPods +- An internet connection to fetch CocoaPods + +## Running the Tool + +You can run the tool with `swift run ZipBuilder [ARGS]` or generate an Xcode project with +`swift package generate-xcodeproj` and run within Xcode. + +In the near future, releases will be built via a builder server instead of on the release engineer's machine, making these +instructions more of a reference to understand what's going on instead of how to build it yourself. + +## Launch Arguments + +See `main.swift` and the `LaunchArgs` struct for information on specific launch arguments. + +You can pass in launch arguments with Xcode by clicking "ZipBuilder" beside the Run/Stop buttons, clicking "Edit +Scheme" and adding them in the "Arguments Passed On Launch" section. + +### Common Arguments + +These arguments assume you're running the command from the `ZipBuilder` directory. + +**Required** arguments: +- `-templateDir $(pwd)/Template` + - This should always be the same. +- `-coreDiagnosticsDir ` + - Needed to overwrite the existing Core Diagnostics framework. + +Optional comon arguments: +- `-updatePodRepo false` + - This is for speedups when `pod repo update` has already been run recently. + +For release engineers (Googlers packaging an upcoming Firebase release) these commands should also be used: +- `-customSpecRepos sso://cpdc-internal/firebase` + - This pulls the latest podspecs from the CocoaPods staging area. +- `-releasingSDKs ` and +- `-existingVersions ` + - Validates the version numbers fetched from CocoaPods staging against the expected released versions from these + textprotos. + +Putting them all together, here's a common command to build a releaseable Zip file: + +``` +swift run ZipBuilder -templateDir $(pwd)/Template -updatePodRepo false \ +-coreDiagnosticsDir /private/tmp/tmpUqBxKN/FirebaseCoreDiagnostics.framework \ +-releasingSDKs \ +-existingVersions \ +-customSpecRepos sso://cpdc-internal/firebase +``` + +## Debugging + +You can generate an Xcode project for the tool by running `swift package generate-xcodeproj` in this directory. +See the above instructions for adding Launch Arguments to the Xcode build. ## Priorities From b6a0980fc5939f026f0f61467c711bc6faa9d27e Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 12 Apr 2019 11:03:32 -0400 Subject: [PATCH 209/214] Push `firebase_version` tag after creating it. (#2820) We currently tag it locally but don't push it to remote like we do with the other tags. --- Releases/update-versions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Releases/update-versions.py b/Releases/update-versions.py index 1a06ac16a51..7dd00d3060a 100755 --- a/Releases/update-versions.py +++ b/Releases/update-versions.py @@ -163,6 +163,7 @@ def UpdateTags(version_data, firebase_version, first=False): LogOrRun("git push --delete origin '{}'".format(firebase_version)) LogOrRun("git tag --delete '{}'".format(firebase_version)) LogOrRun("git tag '{}'".format(firebase_version)) + LogOrRun("git push origin '{}'".format(firebase_version)) for pod, version in version_data.items(): name = pod[len('Firebase'):] tag = '{}-{}'.format(name, version) From 89cc53ed498a7c6ad703d63fac872ca04355feb9 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 12 Apr 2019 11:16:23 -0400 Subject: [PATCH 210/214] Update FirebaseSDKs.textproto with new fields. (#2818) * Update FirebaseSDKs.textproto with new fields. * Update message about strip_32bits flag. --- ZipBuilder/FirebaseSDKs.proto | 7 +++++ .../Sources/ZipBuilder/FirebaseSDKs.pb.swift | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/ZipBuilder/FirebaseSDKs.proto b/ZipBuilder/FirebaseSDKs.proto index b06d0a38877..817b6165909 100644 --- a/ZipBuilder/FirebaseSDKs.proto +++ b/ZipBuilder/FirebaseSDKs.proto @@ -32,6 +32,13 @@ message SDK { // Whether or not to strip the i386 architecture from the build. bool strip_i386 = 8; + + // List of build targets. For internal use only. + repeated string build_target = 9; + + // Whether or not to strip both the i386 and armv7 architectures from the + // build. All SDKs that use this flag are built internally so this should be ignored. + bool strip_32bits = 10; } // Any extra build flags needed to build the SDK. For internal use only. diff --git a/ZipBuilder/Sources/ZipBuilder/FirebaseSDKs.pb.swift b/ZipBuilder/Sources/ZipBuilder/FirebaseSDKs.pb.swift index c31bde14206..66b9054b458 100644 --- a/ZipBuilder/Sources/ZipBuilder/FirebaseSDKs.pb.swift +++ b/ZipBuilder/Sources/ZipBuilder/FirebaseSDKs.pb.swift @@ -106,6 +106,19 @@ struct ZipBuilder_SDK { set {_uniqueStorage()._stripI386 = newValue} } + /// List of build targets. For internal use only. + var buildTarget: [String] { + get {return _storage._buildTarget} + set {_uniqueStorage()._buildTarget = newValue} + } + + /// Whether or not to strip both the i386 and armv7 architectures from the + /// build. For internal use only. + var strip32Bits: Bool { + get {return _storage._strip32Bits} + set {_uniqueStorage()._strip32Bits = newValue} + } + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -171,6 +184,8 @@ extension ZipBuilder_SDK: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement 6: .standard(proto: "nightly_mpm_pattern"), 7: .standard(proto: "open_source"), 8: .standard(proto: "strip_i386"), + 9: .standard(proto: "build_target"), + 10: .standard(proto: "strip_32bits"), ] fileprivate class _StorageClass { @@ -182,6 +197,8 @@ extension ZipBuilder_SDK: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement var _nightlyMpmPattern: [String] = [] var _openSource: Bool = false var _stripI386: Bool = false + var _buildTarget: [String] = [] + var _strip32Bits: Bool = false static let defaultInstance = _StorageClass() @@ -196,6 +213,8 @@ extension ZipBuilder_SDK: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement _nightlyMpmPattern = source._nightlyMpmPattern _openSource = source._openSource _stripI386 = source._stripI386 + _buildTarget = source._buildTarget + _strip32Bits = source._strip32Bits } } @@ -219,6 +238,8 @@ extension ZipBuilder_SDK: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement case 6: try decoder.decodeRepeatedStringField(value: &_storage._nightlyMpmPattern) case 7: try decoder.decodeSingularBoolField(value: &_storage._openSource) case 8: try decoder.decodeSingularBoolField(value: &_storage._stripI386) + case 9: try decoder.decodeRepeatedStringField(value: &_storage._buildTarget) + case 10: try decoder.decodeSingularBoolField(value: &_storage._strip32Bits) default: break } } @@ -251,6 +272,12 @@ extension ZipBuilder_SDK: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement if _storage._stripI386 != false { try visitor.visitSingularBoolField(value: _storage._stripI386, fieldNumber: 8) } + if !_storage._buildTarget.isEmpty { + try visitor.visitRepeatedStringField(value: _storage._buildTarget, fieldNumber: 9) + } + if _storage._strip32Bits != false { + try visitor.visitSingularBoolField(value: _storage._strip32Bits, fieldNumber: 10) + } } try unknownFields.traverse(visitor: &visitor) } @@ -268,6 +295,8 @@ extension ZipBuilder_SDK: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement if _storage._nightlyMpmPattern != rhs_storage._nightlyMpmPattern {return false} if _storage._openSource != rhs_storage._openSource {return false} if _storage._stripI386 != rhs_storage._stripI386 {return false} + if _storage._buildTarget != rhs_storage._buildTarget {return false} + if _storage._strip32Bits != rhs_storage._strip32Bits {return false} return true } if !storagesAreEqual {return false} From be25fd6ea275387d04b19313ac0ee514a78bfbba Mon Sep 17 00:00:00 2001 From: Marek Gilbert Date: Fri, 12 Apr 2019 10:48:29 -0700 Subject: [PATCH 211/214] Revert "Have Travis test firestore-master" This reverts commit 9d0f07b2aae4843cfbd21f1ee3b23beed6175914. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4f207c5a174..5c93cd05aff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -293,4 +293,3 @@ jobs: branches: only: - master - - firestore-master From a3adb636a9e18be4826e051b5f5744b6321ab03a Mon Sep 17 00:00:00 2001 From: Marek Gilbert Date: Fri, 12 Apr 2019 10:48:35 -0700 Subject: [PATCH 212/214] Revert "log command execution (#2811)" This reverts commit 279a095ae65176f130c1e960be90eaf8fcaf2889. --- scripts/check.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/check.sh b/scripts/check.sh index 29731d93684..ad5c18ab0eb 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -131,8 +131,6 @@ while [[ $# -gt 0 ]]; do shift done -set -x - if [[ "${TEST_ONLY}" == true && "${COMMIT_METHOD}" != "none" ]]; then echo "--test-only cannot be combined with --amend, --fixup, or --commit" exit 1 From 58205aa51350bf9abab23c115aa9198050aa97db Mon Sep 17 00:00:00 2001 From: Gil Date: Fri, 12 Apr 2019 12:47:56 -0700 Subject: [PATCH 213/214] Tunnel underlying NSErrors through C++ Status (#2808) * Tunnel underlying NSErrors through Status * Make FIRFirestoreErrorDomain available to CMake builds The prior state was actually semi-broken. The firebase_credentials_provider_apple definition wasn't extern "C" so it was defining the value in addition to whatever FIRFirestore was doing. FIRFirestore's value isn't available to straight C++ though, so this change pushes the definition down into error_apple.mm so that it can be shared by the Objective-C API and MakeNSError. Pushing MakeNSError up into the API layer isn't practical yet so this change is a compromise that makes things work --- .../Firestore.xcodeproj/project.pbxproj | 8 ++ Firestore/Source/API/FIRFirestore.mm | 2 - .../firebase_credentials_provider_apple.mm | 5 +- .../firebase/firestore/util/CMakeLists.txt | 2 + .../src/firebase/firestore/util/error_apple.h | 28 ++++-- .../firebase/firestore/util/error_apple.mm | 22 +++++ .../src/firebase/firestore/util/status.cc | 17 ++++ .../core/src/firebase/firestore/util/status.h | 39 +++++++- .../firebase/firestore/util/status_apple.mm | 55 ++++++++++- .../firebase/firestore/util/status_posix.cc | 4 + .../firebase/firestore/util/CMakeLists.txt | 1 + .../firestore/util/status_apple_test.mm | 91 +++++++++++++++++++ .../firebase/firestore/util/status_test.cc | 3 + 13 files changed, 259 insertions(+), 18 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/util/error_apple.mm create mode 100644 Firestore/core/test/firebase/firestore/util/status_apple_test.mm diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 7b76b035360..75064ae651e 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -259,6 +259,9 @@ 5492E0BF2021555100B64F25 /* FSTFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */; }; 5492E0C72021557E00B64F25 /* FSTSerializerBetaTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */; }; 5492E0C92021557E00B64F25 /* FSTRemoteEventTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */; }; + 5493A424225F9990006DE7BA /* status_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5493A423225F9990006DE7BA /* status_apple_test.mm */; }; + 5493A425225F9990006DE7BA /* status_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5493A423225F9990006DE7BA /* status_apple_test.mm */; }; + 5493A426225F9990006DE7BA /* status_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5493A423225F9990006DE7BA /* status_apple_test.mm */; }; 5495EB032040E90200EBA509 /* CodableGeoPointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5495EB022040E90200EBA509 /* CodableGeoPointTests.swift */; }; 54995F6F205B6E12004EFFA0 /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; 549CCA5020A36DBC00BCEB75 /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; @@ -831,6 +834,7 @@ 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTFieldValueTests.mm; sourceTree = ""; }; 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSerializerBetaTests.mm; sourceTree = ""; }; 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteEventTests.mm; sourceTree = ""; }; + 5493A423225F9990006DE7BA /* status_apple_test.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = status_apple_test.mm; sourceTree = ""; }; 5495EB022040E90200EBA509 /* CodableGeoPointTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodableGeoPointTests.swift; sourceTree = ""; }; 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_key_test.cc; sourceTree = ""; }; 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sorted_set_test.cc; sourceTree = ""; }; @@ -1251,6 +1255,7 @@ AB380D03201BC6E400D97691 /* ordered_code_test.cc */, 403DBF6EFB541DFD01582AA3 /* path_test.cc */, 54740A531FC913E500713A1A /* secure_random_test.cc */, + 5493A423225F9990006DE7BA /* status_apple_test.mm */, 54A0352C20A3B3D7003E0143 /* status_test.cc */, 54A0352B20A3B3D7003E0143 /* status_test_util.h */, 54A0352D20A3B3D7003E0143 /* statusor_test.cc */, @@ -3051,6 +3056,7 @@ 862B1AC9EDAB309BBF4FB18C /* sorted_map_test.cc in Sources */, 4A62B708A6532DD45414DA3A /* sorted_set_test.cc in Sources */, C9F96C511F45851D38EC449C /* status.pb.cc in Sources */, + 5493A425225F9990006DE7BA /* status_apple_test.mm in Sources */, 4DC660A62BC2B6369DA5C563 /* status_test.cc in Sources */, 74985DE2C7EF4150D7A455FD /* statusor_test.cc in Sources */, C5C01A1FB216DA4BA8BF1A02 /* stream_test.mm in Sources */, @@ -3218,6 +3224,7 @@ 86E6FC2B7657C35B342E1436 /* sorted_map_test.cc in Sources */, 8413BD9958F6DD52C466D70F /* sorted_set_test.cc in Sources */, 0D2D25522A94AA8195907870 /* status.pb.cc in Sources */, + 5493A426225F9990006DE7BA /* status_apple_test.mm in Sources */, C0AD8DB5A84CAAEE36230899 /* status_test.cc in Sources */, DC48407370E87F2233D7AB7E /* statusor_test.cc in Sources */, 215643858470A449D3A3E168 /* stream_test.mm in Sources */, @@ -3460,6 +3467,7 @@ 549CCA5220A36DBC00BCEB75 /* sorted_map_test.cc in Sources */, 549CCA5020A36DBC00BCEB75 /* sorted_set_test.cc in Sources */, 618BBEB120B89AAC00B5BCE7 /* status.pb.cc in Sources */, + 5493A424225F9990006DE7BA /* status_apple_test.mm in Sources */, 54A0352F20A3B3D8003E0143 /* status_test.cc in Sources */, 54A0353020A3B3D8003E0143 /* statusor_test.cc in Sources */, B66D8996213609EE0086DA0C /* stream_test.mm in Sources */, diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index 1853434e76b..fb0a5551d81 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -55,8 +55,6 @@ NS_ASSUME_NONNULL_BEGIN -extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; - #pragma mark - FIRFirestore @interface FIRFirestore () 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 f167d3f8d67..582e8374dde 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 @@ -22,13 +22,10 @@ #import #import +#include "Firestore/core/src/firebase/firestore/util/error_apple.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #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 { namespace firestore { namespace auth { diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index 28b29ec6aa9..e7460300bce 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -246,6 +246,8 @@ cc_library( comparison.h config.h delayed_constructor.h + error_apple.h + error_apple.mm hashing.h iterator_adaptors.h objc_compatibility.h diff --git a/Firestore/core/src/firebase/firestore/util/error_apple.h b/Firestore/core/src/firebase/firestore/util/error_apple.h index a091840dcaf..88254bc4a62 100644 --- a/Firestore/core/src/firebase/firestore/util/error_apple.h +++ b/Firestore/core/src/firebase/firestore/util/error_apple.h @@ -22,31 +22,43 @@ #import -#import // for FIRFirestoreErrorDomain - #include "Firestore/core/include/firebase/firestore/firestore_errors.h" #include "Firestore/core/src/firebase/firestore/util/status.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "absl/strings/string_view.h" +// The Cloud Firestore error domain. Keep in sync with FIRFirestoreErrors.h. +// Exposed here to make it possible to build in CMake without bringing in the +// sources under Firestore/Source. +FOUNDATION_EXPORT NSString* const FIRFirestoreErrorDomain + NS_SWIFT_NAME(FirestoreErrorDomain); + namespace firebase { namespace firestore { namespace util { // Translates a set of error_code and error_msg to an NSError. inline NSError* MakeNSError(const int64_t error_code, - const absl::string_view error_msg) { + const absl::string_view error_msg, + NSError* cause = nil) { if (error_code == FirestoreErrorCode::Ok) { return nil; } - return [NSError - errorWithDomain:FIRFirestoreErrorDomain - code:static_cast(error_code) - userInfo:@{NSLocalizedDescriptionKey : WrapNSString(error_msg)}]; + + NSMutableDictionary* user_info = + [NSMutableDictionary dictionary]; + user_info[NSLocalizedDescriptionKey] = WrapNSString(error_msg); + if (cause) { + user_info[NSUnderlyingErrorKey] = cause; + } + + return [NSError errorWithDomain:FIRFirestoreErrorDomain + code:static_cast(error_code) + userInfo:user_info]; } inline NSError* MakeNSError(const util::Status& status) { - return MakeNSError(status.code(), status.error_message()); + return status.ToNSError(); } inline Status MakeStatus(NSError* error) { diff --git a/Firestore/core/src/firebase/firestore/util/error_apple.mm b/Firestore/core/src/firebase/firestore/util/error_apple.mm new file mode 100644 index 00000000000..b558b6062a4 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/error_apple.mm @@ -0,0 +1,22 @@ +/* + * Copyright 2019 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/util/error_apple.h" + +// NB: This is also defined in Firestore/Source/Public/FIRFirestoreErrors.h +// NOLINTNEXTLINE: public constant +FOUNDATION_EXPORT NSString* const FIRFirestoreErrorDomain = + @"FIRFirestoreErrorDomain"; diff --git a/Firestore/core/src/firebase/firestore/util/status.cc b/Firestore/core/src/firebase/firestore/util/status.cc index 06a643daa7a..6f36cafea21 100644 --- a/Firestore/core/src/firebase/firestore/util/status.cc +++ b/Firestore/core/src/firebase/firestore/util/status.cc @@ -17,6 +17,7 @@ #include "Firestore/core/src/firebase/firestore/util/status.h" #include +#include #include "Firestore/core/src/firebase/firestore/util/string_format.h" #include "absl/memory/memory.h" @@ -49,6 +50,22 @@ Status& Status::CausedBy(const Status& cause) { } absl::StrAppend(&state_->msg, ": ", cause.error_message()); + + // If this Status has no accompanying PlatformError but the cause does, create + // an PlatformError for this Status ahead of time to preserve the causal chain + // that Status doesn't otherwise support. + if (state_->platform_error == nullptr && + cause.state_->platform_error != nullptr) { + state_->platform_error = + cause.state_->platform_error->WrapWith(code(), error_message()); + } + + return *this; +} + +Status& Status::WithPlatformError(std::unique_ptr error) { + HARD_ASSERT(!ok(), "Platform errors should not be applied to Status::OK()"); + state_->platform_error = std::move(error); return *this; } diff --git a/Firestore/core/src/firebase/firestore/util/status.h b/Firestore/core/src/firebase/firestore/util/status.h index d49089ee0d6..3937d6c897c 100644 --- a/Firestore/core/src/firebase/firestore/util/status.h +++ b/Firestore/core/src/firebase/firestore/util/status.h @@ -35,6 +35,8 @@ namespace firebase { namespace firestore { namespace util { +class PlatformError; + /// Denotes success or failure of a call. class ABSL_MUST_USE_RESULT Status { public: @@ -63,6 +65,8 @@ class ABSL_MUST_USE_RESULT Status { #if defined(__OBJC__) static Status FromNSError(NSError* error); + + NSError* ToNSError() const; #endif // defined(__OBJC__) /// Returns true iff the status indicates success. @@ -97,6 +101,8 @@ class ABSL_MUST_USE_RESULT Status { /// \return *this Status& CausedBy(const Status& cause); + Status& WithPlatformError(std::unique_ptr error); + /// \brief Return a string representation of this status suitable for /// printing. Returns the string `"OK"` for success. std::string ToString() const; @@ -110,20 +116,51 @@ class ABSL_MUST_USE_RESULT Status { private: static const std::string& empty_string(); struct State { + State() = default; + State(const State& other); + FirestoreErrorCode code; std::string msg; + + // An additional platform-specific error representation that was used to + // generate this Status. The PlatformError does not meaningfully contribute + // to the identity of this Status: it exists to allow tunneling e.g. + // NSError* to Status and back to NSError* losslessly. + std::unique_ptr platform_error; }; - // OK status has a `NULL` state_. Otherwise, `state_` points to + // OK status has a `nullptr` state_. Otherwise, `state_` points to // a `State` structure containing the error code and message(s) std::unique_ptr state_; void SlowCopyFrom(const State* src); }; +class PlatformError { + public: + virtual ~PlatformError() { + } + + virtual std::unique_ptr Copy() = 0; + + /** + * Creates a new PlatformError with the given code and message, whose cause is + * this PlatformError. + */ + virtual std::unique_ptr WrapWith(FirestoreErrorCode code, + std::string message) = 0; +}; + inline Status::Status(const Status& s) : state_((s.state_ == nullptr) ? nullptr : new State(*s.state_)) { } +inline Status::State::State(const State& s) + : code(s.code), + msg(s.msg), + platform_error((s.platform_error == nullptr) ? nullptr + : s.platform_error->Copy()) { +} + inline void Status::operator=(const Status& s) { // The following condition catches both aliasing (when this == &s), // and the common case where both s and *this are ok. diff --git a/Firestore/core/src/firebase/firestore/util/status_apple.mm b/Firestore/core/src/firebase/firestore/util/status_apple.mm index f0bf7a6809f..d248f6721da 100644 --- a/Firestore/core/src/firebase/firestore/util/status_apple.mm +++ b/Firestore/core/src/firebase/firestore/util/status_apple.mm @@ -18,30 +18,79 @@ #if defined(__APPLE__) +#include "Firestore/core/src/firebase/firestore/util/error_apple.h" #include "Firestore/core/src/firebase/firestore/util/string_format.h" +#include "absl/memory/memory.h" namespace firebase { namespace firestore { namespace util { +class UnderlyingNSError : public PlatformError { + public: + explicit UnderlyingNSError(NSError* error) : error_(error) { + } + + static std::unique_ptr Create(NSError* error) { + return absl::make_unique(error); + } + + static NSError* Recover( + const std::unique_ptr& platform_error) { + if (platform_error == nullptr) { + return nil; + } + + return static_cast(platform_error.get())->error(); + } + + std::unique_ptr Copy() override { + return absl::make_unique(error_); + } + + std::unique_ptr WrapWith(FirestoreErrorCode code, + std::string message) override { + NSError* chain = MakeNSError(code, message, error_); + return Create(chain); + } + + NSError* error() const { + return error_; + } + + private: + NSError* error_; +}; + Status Status::FromNSError(NSError* error) { if (!error) { return Status::OK(); } - NSError* original = error; + auto original = UnderlyingNSError::Create(error); while (error) { if ([error.domain isEqualToString:NSPOSIXErrorDomain]) { return FromErrno(static_cast(error.code), - MakeString(original.localizedDescription)); + MakeString(original->error().localizedDescription)) + .WithPlatformError(std::move(original)); } error = error.userInfo[NSUnderlyingErrorKey]; } return Status{FirestoreErrorCode::Unknown, - StringFormat("Unknown error: %s", original)}; + StringFormat("Unknown error: %s", original->error())} + .WithPlatformError(std::move(original)); +} + +NSError* Status::ToNSError() const { + if (ok()) return nil; + + NSError* error = UnderlyingNSError::Recover(state_->platform_error); + if (error) return error; + + return MakeNSError(code(), error_message()); } } // namespace util diff --git a/Firestore/core/src/firebase/firestore/util/status_posix.cc b/Firestore/core/src/firebase/firestore/util/status_posix.cc index 79c651bfbb0..c40e6cbabfb 100644 --- a/Firestore/core/src/firebase/firestore/util/status_posix.cc +++ b/Firestore/core/src/firebase/firestore/util/status_posix.cc @@ -184,6 +184,10 @@ static FirestoreErrorCode CodeForErrno(int errno_code) { } Status Status::FromErrno(int errno_code, absl::string_view msg) { + if (errno_code == 0) { + return Status::OK(); + } + FirestoreErrorCode canonical_code = CodeForErrno(errno_code); return Status{canonical_code, util::StringFormat("%s (errno %s: %s)", msg, errno_code, diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt index 860578f4895..62d46182010 100644 --- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt @@ -154,6 +154,7 @@ cc_test( hashing_test.cc iterator_adaptors_test.cc ordered_code_test.cc + status_apple_test.mm status_test.cc status_test_util.h statusor_test.cc diff --git a/Firestore/core/test/firebase/firestore/util/status_apple_test.mm b/Firestore/core/test/firebase/firestore/util/status_apple_test.mm new file mode 100644 index 00000000000..3f466a8c491 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/status_apple_test.mm @@ -0,0 +1,91 @@ +/* + * Copyright 2019 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/util/status.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/error_apple.h" +#include "Firestore/core/test/firebase/firestore/util/status_test_util.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace { + +NSError* MakeNotFound() { + return [NSError + errorWithDomain:NSPOSIXErrorDomain + code:ENOENT + userInfo:@{NSLocalizedDescriptionKey : @"Some file not found"}]; +} + +NSError* MakeCocoaNotFound() { + return [NSError errorWithDomain:NSCocoaErrorDomain + code:NSFileNoSuchFileError + userInfo:@{ + NSLocalizedDescriptionKey : @"Some file not found", + NSUnderlyingErrorKey : MakeNotFound() + }]; +} + +} // namespace + +TEST(Status, MapsPosixErrorCodes) { + Status s = Status::FromNSError(MakeNotFound()); + EXPECT_EQ(FirestoreErrorCode::NotFound, s.code()); + + s = Status::FromNSError(MakeCocoaNotFound()); + EXPECT_EQ(FirestoreErrorCode::NotFound, s.code()); +} + +TEST(Status, PreservesNSError) { + NSError* expected = MakeCocoaNotFound(); + Status s = Status::FromNSError(expected); + + NSError* actual = s.ToNSError(); + EXPECT_TRUE([expected isEqual:actual]); +} + +TEST(Status, CausedBy_Chain_NSError) { + NSError* not_found_nserror = MakeNotFound(); + Status internal_error(FirestoreErrorCode::Internal, "Something broke"); + + Status result = internal_error; + result.CausedBy(Status::FromNSError(not_found_nserror)); + EXPECT_NE(internal_error, result); + + // Outer should prevail + EXPECT_EQ(internal_error.code(), result.code()); + ASSERT_THAT( + result.ToString(), + testing::MatchesRegex( + "Internal: Something broke: Some file not found \\(errno .*\\)")); + + NSError* error = result.ToNSError(); + EXPECT_EQ(internal_error.code(), error.code); + EXPECT_EQ(FIRFirestoreErrorDomain, error.domain); + + NSError* cause = error.userInfo[NSUnderlyingErrorKey]; + EXPECT_TRUE([not_found_nserror isEqual:cause]); +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/status_test.cc b/Firestore/core/test/firebase/firestore/util/status_test.cc index 17cc0d2c85f..9fb980397c6 100644 --- a/Firestore/core/test/firebase/firestore/util/status_test.cc +++ b/Firestore/core/test/firebase/firestore/util/status_test.cc @@ -111,6 +111,9 @@ TEST(Status, FromErrno) { a.ToString(), testing::MatchesRegex( "Already exists: Cannot write file \\(errno .*: File exists\\)")); + + Status b = Status::FromErrno(0, "Nothing wrong"); + ASSERT_EQ(Status::OK(), b); } TEST(Status, CausedBy_OK) { From 97d4ab1410f4c69dd6e053b7a41bcb3be433e779 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Fri, 12 Apr 2019 13:15:32 -0700 Subject: [PATCH 214/214] Add travis check for filename spaces (#2821) * Proposed improvements to space checking (#2824) --- ...t System.md => FirebaseComponentSystem.md} | 0 scripts/check.sh | 1 + scripts/check_filename_spaces.sh | 39 +++++++++++++++++++ 3 files changed, 40 insertions(+) rename Interop/{Firebase Component System.md => FirebaseComponentSystem.md} (100%) create mode 100755 scripts/check_filename_spaces.sh diff --git a/Interop/Firebase Component System.md b/Interop/FirebaseComponentSystem.md similarity index 100% rename from Interop/Firebase Component System.md rename to Interop/FirebaseComponentSystem.md diff --git a/scripts/check.sh b/scripts/check.sh index ad5c18ab0eb..7e1243cde86 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -212,6 +212,7 @@ fi # Check lint errors. "${top_dir}/scripts/check_whitespace.sh" +"${top_dir}/scripts/check_filename_spaces.sh" "${top_dir}/scripts/check_copyright.sh" "${top_dir}/scripts/check_no_module_imports.sh" "${top_dir}/scripts/check_test_inclusion.py" diff --git a/scripts/check_filename_spaces.sh b/scripts/check_filename_spaces.sh new file mode 100755 index 00000000000..5e48c489d92 --- /dev/null +++ b/scripts/check_filename_spaces.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Copyright 2019 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. + +# Fail on spaces in file names, excluding the patterns listed below. + +# A sed program that removes filename patterns that are allowed to have spaces +# in them. +function remove_valid_names() { + sed ' + # Xcode-generated asset files + /Assets.xcassets/ d + + # Files without spaces + /^[^ ]*$/ d + ' +} + +count=$(git ls-files | remove_valid_names | wc -l | xargs) + +if [[ ${count} != 0 ]]; then + echo 'ERROR: Spaces in filenames are not permitted in this repo. Please fix.' + echo '' + + git ls-files | remove_valid_names + exit 1 +fi