mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-11 22:55:59 +05:45
Merge FLoC SDK into master branch. (#6466)
* Create Segmentation SDK structure for source and unit tests. (#3214) * Create Segmentation SDK structure for source and unit tests. * Review changes: Remove unnecessary files in test folder. Add md-floc-master to CI * Rename Segmentation directory to FirebaseSegmentation directory. Update podspec to include search header path. * Add core support with interop for Segmentation SDK. (#3430) * Add core support with interop for Segmentation SDK. Also update headers to be under sources folder. * Review fixes. * Minor changes. * Fix style. * Fix style. * Style changes. * Fix whitespace in travis.yml * Fix style. * Travis CI is stuck..try updating the travis.yml * Undo travis.yml change. * Working drop of Segmentation SDK along with test app and unit tests. (#4574) * Working drop of Segmentation SDK along with sample app and unit tests. * Update if_changed.sh to include FirebaseSegmentation. * Complete NS_ASSUME_NON_NULL_START with NS_ASSUME_NON_NULL_END in header file. * Fix unit tests. * Fix style. * Fixes after running XCode's static analyzer. * Fix style. * fix style. * 'pod lib lint' fixes. * Fix analyzer errors. * Address review comments. * Minor changes for review comments. * Address review comments. * Address review comments. * stop mocking in tear down method for tests. * Minor update to sample app project. * Fix trailing whitespace in Podfile. * Add set -x to check.sh * update segmetation dependency version * migrate FloC SDK to depend on FIS SDK directly * format floc * format * using customized FIRapp * remove test plist file * refactor to capture weakself * format * replace partial mock with class mock * minor refactoring * use subscript to manipulate dictionary instance * fix import error * minor refoctoring, addressing comments * address comments * fix configurations * format Co-authored-by: dmandar <dmandar@users.noreply.github.com> Co-authored-by: ChaoqunCHEN <cqchen93@gmail.com>
This commit is contained in:
34 files changed
+2477
-3
No files matched your search
@@ -29,6 +29,9 @@ FirebaseStorage/Tests/Integration/Credentials.h
|
||||
FirebaseStorage/Tests/SwiftIntegration/Credentials.swift
|
||||
FirebaseStorageSwift/Tests/Integration/Credentials.swift
|
||||
|
||||
# FirebaseSegmentation integration tests GoogleService-Info.plist
|
||||
FirebaseSegmentation/Tests/Sample/GoogleService-Info.plist
|
||||
|
||||
Secrets.tar
|
||||
|
||||
# OS X
|
||||
|
||||
@@ -49,6 +49,7 @@ def labelsForModifiedFiles()
|
||||
labels.push("api: instanceid") if @has_instanceid_changes
|
||||
labels.push("api: messaging") if @has_messaging_changes
|
||||
labels.push("api: remoteconfig") if @has_remoteconfig_changes
|
||||
labels.push("api: segmentation") if @has_segmentation_changes
|
||||
labels.push("api: storage") if @has_storage_changes
|
||||
labels.push("GoogleDataTransport") if @has_gdt_changes
|
||||
labels.push("GoogleUtilities") if @has_googleutilities_changes
|
||||
@@ -102,6 +103,8 @@ has_license_changes = didModify(["LICENSE"])
|
||||
@has_messaging_api_changes = hasChangesIn("FirebaseMessaging/Sources/Public/")
|
||||
@has_remoteconfig_changes = hasChangesIn("FirebaseRemoteConfig/")
|
||||
@has_remoteconfig_api_changes = hasChangesIn("FirebaseRemoteConfig/Sources/Public/")
|
||||
@has_segmentation_changes = hasChangesIn("FirebaseSegmentation/")
|
||||
@has_segmentation_api_changes = hasChangesIn("FirebaseSegmentation/Source/Public/
|
||||
@has_storage_changes = hasChangesIn("FirebaseStorage/")
|
||||
@has_storage_api_changes = hasChangesIn("FirebaseStorage/Sources/Public/")
|
||||
|
||||
@@ -125,6 +128,7 @@ has_license_changes = didModify(["LICENSE"])
|
||||
@has_instanceid_api_changes ||
|
||||
@has_messaging_api_changes ||
|
||||
@has_remoteconfig_api_changes ||
|
||||
@has_segmentation_api_changes ||
|
||||
@has_storage_api_changes ||
|
||||
@has_gdt_api_changes
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'FirebaseSegmentation'
|
||||
s.version = '0.1.0'
|
||||
s.summary = 'Firebase Segmentation SDK'
|
||||
s.description = <<-DESC
|
||||
Firebase Segmentation enables you to associate your custom application instance ID with Firebase for user segmentation.
|
||||
DESC
|
||||
|
||||
s.homepage = 'https://firebase.google.com'
|
||||
s.license = { :type => 'Apache', :file => 'LICENSE' }
|
||||
s.authors = 'Google, Inc.'
|
||||
s.source = {
|
||||
:git => 'https://github.com/firebase/firebase-ios-sdk.git',
|
||||
:tag => 'Segmentation-' + s.version.to_s
|
||||
}
|
||||
|
||||
s.ios.deployment_target = '8.0'
|
||||
s.cocoapods_version = '>= 1.4.0'
|
||||
s.static_framework = true
|
||||
s.prefix_header_file = false
|
||||
|
||||
s.source_files = [
|
||||
'FirebaseSegmentation/Sources/**/*.[mh]',
|
||||
'FirebaseCore/Sources/Private/*.h',
|
||||
]
|
||||
s.public_header_files = 'FirebaseSegmentation/Sources/Public/*.h'
|
||||
|
||||
s.dependency 'FirebaseCore', '~> 6.7'
|
||||
s.dependency 'FirebaseInstallations', '~> 1.7'
|
||||
|
||||
header_search_paths = {
|
||||
'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}"'
|
||||
}
|
||||
|
||||
s.pod_target_xcconfig = {
|
||||
'GCC_C_LANGUAGE_STANDARD' => 'c99',
|
||||
'GCC_PREPROCESSOR_DEFINITIONS' => 'FIRSegmentation_VERSION=' + s.version.to_s
|
||||
}.merge(header_search_paths)
|
||||
|
||||
s.test_spec 'unit' do |unit_tests|
|
||||
unit_tests.source_files = 'FirebaseSegmentation/Tests/Unit/*.[mh]'
|
||||
unit_tests.dependency 'OCMock'
|
||||
unit_tests.requires_app_host = true
|
||||
end
|
||||
end
|
||||
Whitespace-only changes.
@@ -0,0 +1,78 @@
|
||||
// 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 "FirebaseSegmentation/Sources/Public/FIRSegmentation.h"
|
||||
|
||||
#import "FirebaseCore/Sources/Private/FIRComponentContainer.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRLogger.h"
|
||||
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
|
||||
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
|
||||
#import "FirebaseSegmentation/Sources/Private/FIRSegmentationComponent.h"
|
||||
#import "FirebaseSegmentation/Sources/SEGContentManager.h"
|
||||
|
||||
@implementation FIRSegmentation {
|
||||
NSString *_firebaseAppName;
|
||||
SEGContentManager *_contentManager;
|
||||
}
|
||||
|
||||
+ (nonnull FIRSegmentation *)segmentation {
|
||||
if (![FIRApp isDefaultAppConfigured]) {
|
||||
[NSException
|
||||
raise:kFirebaseSegmentationErrorDomain
|
||||
format:@"FIRApp not configured. Please make sure you have called [FIRApp configure]"];
|
||||
}
|
||||
|
||||
return [FIRSegmentation segmentationWithApp:[FIRApp defaultApp]];
|
||||
}
|
||||
|
||||
+ (nonnull FIRSegmentation *)segmentationWithApp:(nonnull FIRApp *)firebaseApp {
|
||||
// Use the provider to generate and return instances of FIRSegmentation for this specific app and
|
||||
// namespace. This will ensure the app is configured before Remote Config can return an instance.
|
||||
id<FIRSegmentationProvider> provider =
|
||||
FIR_COMPONENT(FIRSegmentationProvider, firebaseApp.container);
|
||||
return [provider segmentation];
|
||||
}
|
||||
|
||||
- (void)setCustomInstallationID:(NSString *)customInstallationID
|
||||
completion:(void (^)(NSError *))completionHandler {
|
||||
[_contentManager
|
||||
associateCustomInstallationIdentiferNamed:customInstallationID
|
||||
firebaseApp:_firebaseAppName
|
||||
completion:^(BOOL success, NSDictionary *result) {
|
||||
if (!success) {
|
||||
// TODO(dmandar) log; pass along internal error code.
|
||||
NSError *error = [NSError
|
||||
errorWithDomain:kFirebaseSegmentationErrorDomain
|
||||
code:FIRSegmentationErrorCodeInternal
|
||||
userInfo:result];
|
||||
completionHandler(error);
|
||||
} else {
|
||||
completionHandler(nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/// Designated initializer
|
||||
- (instancetype)initWithAppName:(NSString *)appName FIROptions:(FIROptions *)options {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_firebaseAppName = appName;
|
||||
|
||||
// Initialize the content manager.
|
||||
_contentManager = [SEGContentManager sharedInstanceWithOptions:options];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 "FirebaseSegmentation/Sources/Private/FIRSegmentationComponent.h"
|
||||
|
||||
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRComponentContainer.h"
|
||||
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
|
||||
#import "FirebaseSegmentation/Sources/Private/FIRSegmentationInternal.h"
|
||||
#import "FirebaseSegmentation/Sources/SEGSegmentationConstants.h"
|
||||
|
||||
#ifndef FIRSegmentation_VERSION
|
||||
#error "FIRSegmentation_VERSION is not defined: \
|
||||
add -DFIRSegmentation_VERSION=... to the build invocation"
|
||||
#endif
|
||||
|
||||
#define STR(x) STR_EXPAND(x)
|
||||
#define STR_EXPAND(x) #x
|
||||
|
||||
@implementation FIRSegmentationComponent
|
||||
|
||||
/// Default method for retrieving a Segmentation instance, or creating one if it doesn't exist.
|
||||
- (FIRSegmentation *)segmentation {
|
||||
// Validate the required information is available.
|
||||
FIROptions *options = self.app.options;
|
||||
NSString *errorPropertyName;
|
||||
if (options.googleAppID.length == 0) {
|
||||
errorPropertyName = @"googleAppID";
|
||||
} else if (options.GCMSenderID.length == 0) {
|
||||
errorPropertyName = @"GCMSenderID";
|
||||
}
|
||||
|
||||
if (errorPropertyName) {
|
||||
[NSException
|
||||
raise:kFirebaseSegmentationErrorDomain
|
||||
format:@"%@",
|
||||
[NSString
|
||||
stringWithFormat:
|
||||
@"Firebase Segmentation is missing the required %@ property from the "
|
||||
@"configured FirebaseApp and will not be able to function properly. Please "
|
||||
@"fix this issue to ensure that Firebase is correctly configured.",
|
||||
errorPropertyName]];
|
||||
}
|
||||
|
||||
FIRSegmentation *instance = self.segmentationInstance;
|
||||
if (!instance) {
|
||||
instance = [[FIRSegmentation alloc] initWithAppName:self.app.name FIROptions:self.app.options];
|
||||
self.segmentationInstance = instance;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Default initializer.
|
||||
- (instancetype)initWithApp:(FIRApp *)app {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_app = app;
|
||||
if (!_segmentationInstance) {
|
||||
_segmentationInstance = [[FIRSegmentation alloc] initWithAppName:app.name
|
||||
FIROptions:app.options];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
+ (void)load {
|
||||
// Register as an internal library to be part of the initialization process. The name comes from
|
||||
// go/firebase-sdk-platform-info.
|
||||
[FIRApp registerInternalLibrary:self
|
||||
withName:@"fire-seg"
|
||||
withVersion:[NSString stringWithUTF8String:STR(FIRSegmentation_VERSION)]];
|
||||
}
|
||||
|
||||
#pragma mark - Interoperability
|
||||
|
||||
+ (NSArray<FIRComponent *> *)componentsToRegister {
|
||||
FIRComponent *segProvider = [FIRComponent
|
||||
componentWithProtocol:@protocol(FIRSegmentationProvider)
|
||||
instantiationTiming:FIRInstantiationTimingAlwaysEager
|
||||
dependencies:@[]
|
||||
creationBlock:^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
|
||||
// Cache the component so instances of Segmentation are cached.
|
||||
*isCacheable = YES;
|
||||
return [[FIRSegmentationComponent alloc] initWithApp:container.app];
|
||||
}];
|
||||
return @[ segProvider ];
|
||||
}
|
||||
|
||||
@synthesize instances;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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/Foundation.h>
|
||||
|
||||
#import "FIRLibrary.h"
|
||||
|
||||
@class FIRApp;
|
||||
@class FIRSegmentation;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Provides and creates instances of Segmentation. Used in the
|
||||
/// interop registration process to keep track of Segmentation instances for each `FIRApp` instance.
|
||||
@protocol FIRSegmentationProvider
|
||||
|
||||
/// Cached instances of Segmentation objects.
|
||||
@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRSegmentation *> *instances;
|
||||
|
||||
/// Default method for retrieving a Segmentation instance, or creating one if it doesn't exist.
|
||||
- (FIRSegmentation *)segmentation;
|
||||
|
||||
@end
|
||||
|
||||
/// A concrete implementation for FIRSegmentationInterop to create Segmentation instances and
|
||||
/// register with Core's component system.
|
||||
@interface FIRSegmentationComponent : NSObject <FIRSegmentationProvider, FIRLibrary>
|
||||
|
||||
/// The FIRApp that instances will be set up with.
|
||||
@property(nonatomic, weak, readonly) FIRApp *app;
|
||||
|
||||
/// Cached instances of Segmentation objects.
|
||||
@property(nonatomic, strong) FIRSegmentation *segmentationInstance;
|
||||
|
||||
/// Default method for retrieving a Segmentation instance, or creating one if it doesn't exist.
|
||||
- (FIRSegmentation *)segmentation;
|
||||
|
||||
/// Default initializer.
|
||||
- (instancetype)initWithApp:(FIRApp *)app NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init __attribute__((unavailable("Use `initWithApp:`.")));
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -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 <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FIRApp;
|
||||
|
||||
NS_SWIFT_NAME(Segmentation)
|
||||
@interface FIRSegmentation : NSObject
|
||||
|
||||
/// Initialize a Segmentation instance with all the required parameters directly.
|
||||
- (instancetype)initWithAppName:(NSString *)appName FIROptions:(FIROptions *)options;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FIRApp;
|
||||
/**
|
||||
* The Firebase Segmentation SDK is used to associate a custom, non-Firebase custom installation
|
||||
* identifier to Firebase. Once this custom installation identifier is set, developers can use the
|
||||
* current app installation for segmentation purposes. If the custom installation identifier is
|
||||
* explicitely set to nil, any existing custom installation identifier data will be removed.
|
||||
*/
|
||||
NS_SWIFT_NAME(Segmentation)
|
||||
@interface FIRSegmentation : NSObject
|
||||
|
||||
/// Firebase Segmentation service fetch error.
|
||||
typedef NS_ENUM(NSInteger, FIRSegmentationErrorCode) {
|
||||
/// No error. The operation was successful.
|
||||
FIRSegmentationErrorCodeNone = 8001,
|
||||
/// An internal error occurred.
|
||||
FIRSegmentationErrorCodeInternal = 8002,
|
||||
/// Error indicating that backend reports an existing association for this custom installation
|
||||
/// identifier.
|
||||
FIRSegmentationErrorCodeConflict = 8003,
|
||||
/// Error indicating that a network error occurred during association.
|
||||
FIRSegmentationErrorCodeNetwork = 8004,
|
||||
} NS_SWIFT_NAME(SegmentationErrorCode);
|
||||
|
||||
/**
|
||||
* Singleton instance (scoped to the default FIRApp)
|
||||
* Returns the FIRSegmentation instance for the default Firebase application. Please make sure you
|
||||
* call [FIRApp configure] beforehand for a default Firebase app to already be initialized and
|
||||
* available. This singleton class instance lets you set your own custom identifier to be used for
|
||||
* user segmentation purposes within Firebase.
|
||||
*
|
||||
* @return A shared instance of FIRSegmentation.
|
||||
*/
|
||||
+ (instancetype)segmentation;
|
||||
|
||||
/// Singleton instance (scoped to FIRApp)
|
||||
/// Returns the FIRSegmentation instance for your Firebase application. This singleton class
|
||||
/// instance lets you set your own custom identifier to be used for targeting purposes within
|
||||
/// Firebase.
|
||||
+ (instancetype)segmentationWithApp:(nonnull FIRApp *)app;
|
||||
|
||||
/**
|
||||
* :nodoc:
|
||||
* Unavailable. Use +segmentation instead.
|
||||
*/
|
||||
- (instancetype)init __attribute__((unavailable("Use +segmentation instead.")));
|
||||
|
||||
/// Set your own custom installation ID to be used for segmentation purposes.
|
||||
/// This method needs to be called every time (and immediately) upon any changes to the custom
|
||||
/// installation ID.
|
||||
/// @param completionHandler Set custom installation ID completion. Returns nil if initialization
|
||||
/// succeeded or an NSError object if initialization failed.
|
||||
- (void)setCustomInstallationID:(nullable NSString *)customInstallationID
|
||||
completion:(nullable void (^)(NSError *))completionHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 "FIRSegmentation.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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "FirebaseSegmentation/Sources/SEGSegmentationConstants.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FIROptions;
|
||||
|
||||
@interface SEGContentManager : NSObject
|
||||
|
||||
/// Shared Singleton Instance
|
||||
+ (instancetype)sharedInstanceWithOptions:(FIROptions*)options;
|
||||
|
||||
- (void)associateCustomInstallationIdentiferNamed:(NSString*)customInstallationID
|
||||
firebaseApp:(NSString*)appName
|
||||
completion:(SEGRequestCompletion)completionHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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 "FirebaseSegmentation/Sources/SEGContentManager.h"
|
||||
|
||||
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
|
||||
#import "FirebaseInstallations/Source/Library/Public/FirebaseInstallations/FirebaseInstallations.h"
|
||||
#import "FirebaseSegmentation/Sources/Public/FIRSegmentation.h"
|
||||
#import "FirebaseSegmentation/Sources/SEGDatabaseManager.h"
|
||||
#import "FirebaseSegmentation/Sources/SEGNetworkManager.h"
|
||||
#import "FirebaseSegmentation/Sources/SEGSegmentationConstants.h"
|
||||
|
||||
NSString *const kErrorDescription = @"ErrorDescription";
|
||||
|
||||
@interface SEGContentManager () {
|
||||
NSMutableDictionary<NSString *, id> *_associationData;
|
||||
NSString *_installationIdentifier;
|
||||
NSString *_installationIdentifierToken;
|
||||
SEGDatabaseManager *_databaseManager;
|
||||
SEGNetworkManager *_networkManager;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SEGContentManager
|
||||
|
||||
+ (instancetype)sharedInstanceWithOptions:(FIROptions *)options {
|
||||
static dispatch_once_t onceToken;
|
||||
static SEGContentManager *sharedInstance;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[SEGContentManager alloc]
|
||||
initWithDatabaseManager:[SEGDatabaseManager sharedInstance]
|
||||
networkManager:[[SEGNetworkManager alloc] initWithOptions:options]];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDatabaseManager:databaseManager networkManager:networkManager {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// Initialize the database manager.
|
||||
_databaseManager = databaseManager;
|
||||
|
||||
// Initialize the network manager.
|
||||
_networkManager = networkManager;
|
||||
|
||||
// Load all data from the database.
|
||||
[_databaseManager createOrOpenDatabaseWithCompletion:^(BOOL success, NSDictionary *result) {
|
||||
self->_associationData = [result mutableCopy];
|
||||
}];
|
||||
// TODO(dmandar) subscribe to FIS notifications once integrated.
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FIRInstallations *)installationForApp:(NSString *)firebaseApp {
|
||||
return [FIRInstallations installationsWithApp:[FIRApp appNamed:firebaseApp]];
|
||||
}
|
||||
|
||||
- (void)associateCustomInstallationIdentiferNamed:(NSString *)customInstallationID
|
||||
firebaseApp:(NSString *)firebaseApp
|
||||
completion:(SEGRequestCompletion)completionHandler {
|
||||
// Get the latest installation identifier
|
||||
FIRInstallations *installation = [self installationForApp:firebaseApp];
|
||||
if (installation == nil) {
|
||||
completionHandler(NO, @{kErrorDescription : @"Firebase Installations SDK not available"});
|
||||
}
|
||||
__weak SEGContentManager *weakSelf = self;
|
||||
[installation
|
||||
installationIDWithCompletion:^(NSString *_Nullable identifier, NSError *_Nullable error) {
|
||||
SEGContentManager *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
completionHandler(NO, @{kErrorDescription : @"Internal Error getting installation ID."});
|
||||
return;
|
||||
}
|
||||
|
||||
[strongSelf associateInstallationWithLatestIdentifier:identifier
|
||||
installation:installation
|
||||
customizedIdentifier:customInstallationID
|
||||
firebaseApp:firebaseApp
|
||||
error:error
|
||||
completion:completionHandler];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)associateInstallationWithLatestIdentifier:(NSString *_Nullable)identifier
|
||||
installation:(FIRInstallations *)installation
|
||||
customizedIdentifier:(NSString *)customInstallationID
|
||||
firebaseApp:(NSString *)firebaseApp
|
||||
error:(NSError *_Nullable)error
|
||||
completion:(SEGRequestCompletion)completionHandler {
|
||||
if (!identifier || error) {
|
||||
NSString *errorMessage = @"Error getting installation ID.";
|
||||
if (error) {
|
||||
errorMessage = [errorMessage stringByAppendingString:error.description];
|
||||
}
|
||||
NSDictionary *errorDictionary = @{kErrorDescription : errorMessage};
|
||||
completionHandler(NO, errorDictionary);
|
||||
return;
|
||||
}
|
||||
|
||||
_installationIdentifier = identifier;
|
||||
|
||||
__weak SEGContentManager *weakSelf = self;
|
||||
[installation authTokenWithCompletion:^(FIRInstallationsAuthTokenResult *_Nullable tokenResult,
|
||||
NSError *_Nullable error) {
|
||||
SEGContentManager *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
completionHandler(NO, @{kErrorDescription : @"Internal Error getting installation token."});
|
||||
return;
|
||||
}
|
||||
[strongSelf associateInstallationWithToken:tokenResult
|
||||
customizedIdentifier:customInstallationID
|
||||
firebaseApp:firebaseApp
|
||||
error:error
|
||||
completion:completionHandler];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)associateInstallationWithToken:(FIRInstallationsAuthTokenResult *_Nullable)tokenResult
|
||||
customizedIdentifier:(NSString *)customInstallationID
|
||||
firebaseApp:(NSString *)firebaseApp
|
||||
error:(NSError *_Nullable)error
|
||||
completion:(SEGRequestCompletion)completionHandler {
|
||||
if (!tokenResult || error) {
|
||||
NSString *errorMessage = @"Error getting AuthToken.";
|
||||
if (error) {
|
||||
errorMessage = [errorMessage stringByAppendingString:error.description];
|
||||
}
|
||||
NSDictionary *errorDictionary = @{kErrorDescription : errorMessage};
|
||||
completionHandler(NO, errorDictionary);
|
||||
return;
|
||||
}
|
||||
_installationIdentifierToken = tokenResult.authToken;
|
||||
|
||||
NSMutableDictionary<NSString *, NSString *> *appAssociationData =
|
||||
[[NSMutableDictionary alloc] init];
|
||||
appAssociationData[kSEGCustomInstallationIdentifierKey] = customInstallationID;
|
||||
appAssociationData[kSEGFirebaseInstallationIdentifierKey] = _installationIdentifier;
|
||||
appAssociationData[kSEGAssociationStatusKey] = kSEGAssociationStatusPending;
|
||||
_associationData[firebaseApp] = appAssociationData;
|
||||
|
||||
// Update the database async.
|
||||
// TODO(mandard) The database write and corresponding completion handler needs to be wired up
|
||||
// once we support listening to FID changes.
|
||||
[_databaseManager insertMainTableApplicationNamed:firebaseApp
|
||||
customInstanceIdentifier:customInstallationID
|
||||
firebaseInstanceIdentifier:_installationIdentifier
|
||||
associationStatus:kSEGAssociationStatusPending
|
||||
completionHandler:nil];
|
||||
|
||||
// Send the change up to the backend. Also add the token.
|
||||
[_networkManager
|
||||
makeAssociationRequestToBackendWithData:appAssociationData
|
||||
token:_installationIdentifierToken
|
||||
completion:^(BOOL status, NSDictionary<NSString *, id> *result) {
|
||||
// TODO: log, update database.
|
||||
completionHandler(status, result);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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/Foundation.h>
|
||||
|
||||
#import "FirebaseSegmentation/Sources/SEGSegmentationConstants.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Persist config data in sqlite database on device. Managing data read/write from/to database.
|
||||
@interface SEGDatabaseManager : NSObject
|
||||
/// Shared Singleton Instance
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/// Open the database.
|
||||
- (void)createOrOpenDatabaseWithCompletion:(SEGRequestCompletion)completionHandler;
|
||||
|
||||
/// Read all contents of main table.
|
||||
- (void)loadMainTableWithCompletion:(SEGRequestCompletion)completionHandler;
|
||||
|
||||
/// Insert a record in main table.
|
||||
/// @param firebaseApplication The name of the Firebase App that this segmentation instance is
|
||||
/// associated with.
|
||||
/// @param customInstanceIdentifier The custom instance identifier provided by the developer.
|
||||
/// @param firebaseInstanceIdentifier The firebase instance identifier provided by the IID/FIS SDK.
|
||||
/// @param associationStatus The current status of the association - Pending until reported to the
|
||||
/// backend.
|
||||
- (void)insertMainTableApplicationNamed:(NSString *)firebaseApplication
|
||||
customInstanceIdentifier:(NSString *)customInstanceIdentifier
|
||||
firebaseInstanceIdentifier:(NSString *)firebaseInstanceIdentifier
|
||||
associationStatus:(NSString *)associationStatus
|
||||
completionHandler:(nullable SEGRequestCompletion)handler;
|
||||
|
||||
/// Clear the record of given namespace and package name
|
||||
/// before updating the table.//TODO: Add delete.
|
||||
- (void)deleteRecordFromMainTableWithCustomInstanceIdentifier:(NSString *)customInstanceIdentifier;
|
||||
|
||||
/// Remove all the records from a config content table.
|
||||
- (void)deleteAllRecordsFromTable;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,417 @@
|
||||
// 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 "FirebaseSegmentation/Sources/SEGDatabaseManager.h"
|
||||
|
||||
#import <sqlite3.h>
|
||||
#import "FirebaseCore/Sources/Private/FIRLogger.h"
|
||||
|
||||
/// SQLite file name.
|
||||
static NSString *const kDatabaseName = @"FirebaseSegmentation.sqlite3";
|
||||
/// The application support sub-directory that the Segmentation database resides in.
|
||||
static NSString *const kApplicationSupportSubDirectory = @"Google/FirebaseSegmentation";
|
||||
/// Column names
|
||||
static NSString *const kMainTableName = @"main";
|
||||
static NSString *const kMainTableColumnApplicationIdentifier = @"firebase_app_identifier";
|
||||
static NSString *const kMainTableColumnCustomInstallationIdentifier =
|
||||
@"custom_installation_identifier";
|
||||
static NSString *const kMainTableColumnFirebaseInstallationIdentifier =
|
||||
@"firebase_installation_identifier";
|
||||
static NSString *const kMainTableColumnAssociationStatus = @"association_status";
|
||||
|
||||
// Exclude the database from iCloud backup.
|
||||
static BOOL SEGAddSkipBackupAttributeToItemAtPath(NSString *filePathString) {
|
||||
NSURL *URL = [NSURL fileURLWithPath:filePathString];
|
||||
assert([[NSFileManager defaultManager] fileExistsAtPath:URL.path]);
|
||||
|
||||
NSError *error = nil;
|
||||
BOOL success = [URL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:&error];
|
||||
if (!success) {
|
||||
// TODO(dmandar): log error.
|
||||
NSLog(@"Error excluding %@ from backup %@.", [URL lastPathComponent], error);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static BOOL SEGCreateFilePathIfNotExist(NSString *filePath) {
|
||||
if (!filePath || !filePath.length) {
|
||||
// TODO(dmandar) log error.
|
||||
NSLog(@"Failed to create subdirectory for an empty file path.");
|
||||
return NO;
|
||||
}
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
if (![fileManager fileExistsAtPath:filePath]) {
|
||||
NSError *error;
|
||||
[fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&error];
|
||||
if (error) {
|
||||
// TODO(dmandar) log error.
|
||||
NSLog(@"Failed to create subdirectory for database file: %@.", error);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@interface SEGDatabaseManager () {
|
||||
/// Database storing all the config information.
|
||||
sqlite3 *_database;
|
||||
/// Serial queue for database read/write operations.
|
||||
dispatch_queue_t _databaseOperationQueue;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SEGDatabaseManager
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static SEGDatabaseManager *sharedInstance;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[SEGDatabaseManager alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_databaseOperationQueue =
|
||||
dispatch_queue_create("com.google.firebasesegmentation.database", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Public Methods
|
||||
|
||||
- (void)loadMainTableWithCompletion:(SEGRequestCompletion)completionHandler {
|
||||
__weak SEGDatabaseManager *weakSelf = self;
|
||||
dispatch_async(_databaseOperationQueue, ^{
|
||||
SEGDatabaseManager *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
completionHandler(NO, @{@"Database Error" : @"Internal database error"});
|
||||
}
|
||||
|
||||
// Read the database into memory.
|
||||
NSDictionary<NSString *, NSDictionary<NSString *, NSString *> *> *associations =
|
||||
[self loadMainTable];
|
||||
completionHandler(YES, associations);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
- (void)createOrOpenDatabaseWithCompletion:(SEGRequestCompletion)completionHandler {
|
||||
__weak SEGDatabaseManager *weakSelf = self;
|
||||
dispatch_async(_databaseOperationQueue, ^{
|
||||
SEGDatabaseManager *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
completionHandler(NO, @{@"ErrorDescription" : @"Internal database error"});
|
||||
}
|
||||
NSString *dbPath = [SEGDatabaseManager pathForSegmentationDatabase];
|
||||
// TODO(dmandar) log.
|
||||
NSLog(@"Loading segmentation database at path %@", dbPath);
|
||||
const char *databasePath = dbPath.UTF8String;
|
||||
// Create or open database path.
|
||||
if (!SEGCreateFilePathIfNotExist(dbPath)) {
|
||||
completionHandler(NO, @{@"ErrorDescription" : @"Could not create database file at path"});
|
||||
}
|
||||
int flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FILEPROTECTION_COMPLETE |
|
||||
SQLITE_OPEN_FULLMUTEX;
|
||||
if (sqlite3_open_v2(databasePath, &strongSelf->_database, flags, NULL) == SQLITE_OK) {
|
||||
// Create table if does not exist already.
|
||||
if ([strongSelf createTableSchema]) {
|
||||
// DB file created or already exists.
|
||||
// Exclude the app data used from iCloud backup.
|
||||
SEGAddSkipBackupAttributeToItemAtPath(dbPath);
|
||||
|
||||
// Read the database into memory.
|
||||
NSDictionary<NSString *, NSString *> *associations = [self loadMainTable];
|
||||
completionHandler(YES, associations);
|
||||
|
||||
} else {
|
||||
// Remove database before fail.
|
||||
[strongSelf removeDatabase:dbPath];
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000010", @"Failed to create table.");
|
||||
// Create a new database if existing database file is corrupted.
|
||||
if (!SEGCreateFilePathIfNotExist(dbPath)) {
|
||||
completionHandler(NO,
|
||||
@{@"ErrorDescription" : @"Could not recreate database file at path"});
|
||||
}
|
||||
if (sqlite3_open_v2(databasePath, &strongSelf->_database, flags, NULL) == SQLITE_OK) {
|
||||
if (![strongSelf createTableSchema]) {
|
||||
// Remove database before fail.
|
||||
[strongSelf removeDatabase:dbPath];
|
||||
// If it failed again, there's nothing we can do here.
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000010", @"Failed to create table.");
|
||||
} else {
|
||||
// Exclude the app data used from iCloud backup.
|
||||
SEGAddSkipBackupAttributeToItemAtPath(dbPath);
|
||||
}
|
||||
} else {
|
||||
[strongSelf logDatabaseError];
|
||||
completionHandler(NO, @{@"ErrorDescription" : @"Could not create database."});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[strongSelf logDatabaseError];
|
||||
completionHandler(NO, @{@"ErrorDescription" : @"Error creating database."});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeDatabase:(NSString *)path completion:(SEGRequestCompletion)completionHandler {
|
||||
dispatch_async(_databaseOperationQueue, ^{
|
||||
SEGDatabaseManager *strongSelf = self;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
[strongSelf removeDatabase:path];
|
||||
completionHandler(YES, nil);
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Private Methods
|
||||
|
||||
- (NSDictionary *)loadMainTable {
|
||||
NSString *SQLQuery = [NSString
|
||||
stringWithFormat:@"SELECT %@, %@, %@, %@ FROM %@", kMainTableColumnApplicationIdentifier,
|
||||
kMainTableColumnCustomInstallationIdentifier,
|
||||
kMainTableColumnFirebaseInstallationIdentifier,
|
||||
kMainTableColumnAssociationStatus, kMainTableName];
|
||||
|
||||
sqlite3_stmt *statement = [self prepareSQL:[SQLQuery cStringUsingEncoding:NSUTF8StringEncoding]];
|
||||
if (!statement) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableDictionary<NSString *, NSDictionary<NSString *, NSString *> *> *associations =
|
||||
[[NSMutableDictionary alloc] init];
|
||||
while (sqlite3_step(statement) == SQLITE_ROW) {
|
||||
NSString *firebaseApplicationName =
|
||||
[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
|
||||
NSString *customInstallationIdentifier =
|
||||
[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
|
||||
NSString *firebaseInstallationIdentifier =
|
||||
[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
|
||||
NSString *associationStatus =
|
||||
[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 3)];
|
||||
NSDictionary<NSString *, NSString *> *associationData = @{
|
||||
kSEGCustomInstallationIdentifierKey : customInstallationIdentifier,
|
||||
kSEGFirebaseInstallationIdentifierKey : firebaseInstallationIdentifier,
|
||||
kSEGAssociationStatusKey : associationStatus
|
||||
};
|
||||
[associations setObject:associationData forKey:firebaseApplicationName];
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
return associations;
|
||||
}
|
||||
|
||||
/// Returns the current version of the Remote Config database.
|
||||
+ (NSString *)pathForSegmentationDatabase {
|
||||
NSArray<NSString *> *dirPaths =
|
||||
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString *appSupportPath = dirPaths.firstObject;
|
||||
NSArray<NSString *> *components =
|
||||
@[ appSupportPath, kApplicationSupportSubDirectory, kDatabaseName ];
|
||||
return [NSString pathWithComponents:components];
|
||||
}
|
||||
|
||||
- (BOOL)createTableSchema {
|
||||
SEG_MUST_NOT_BE_MAIN_THREAD();
|
||||
NSString *mainTableSchema =
|
||||
[NSString stringWithFormat:@"create TABLE IF NOT EXISTS %@ (_id INTEGER PRIMARY KEY, %@ "
|
||||
@"TEXT, %@ TEXT, %@ TEXT, %@ TEXT)",
|
||||
kMainTableName, kMainTableColumnApplicationIdentifier,
|
||||
kMainTableColumnCustomInstallationIdentifier,
|
||||
kMainTableColumnFirebaseInstallationIdentifier,
|
||||
kMainTableColumnAssociationStatus];
|
||||
|
||||
return [self executeQuery:[mainTableSchema cStringUsingEncoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
|
||||
- (void)removeDatabase:(NSString *)path {
|
||||
SEG_MUST_NOT_BE_MAIN_THREAD();
|
||||
if (sqlite3_close(self->_database) != SQLITE_OK) {
|
||||
[self logDatabaseError];
|
||||
}
|
||||
self->_database = nil;
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error;
|
||||
if (![fileManager removeItemAtPath:path error:&error]) {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000011",
|
||||
@"Failed to remove database at path %@ for error %@.", path, error);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - execute
|
||||
- (BOOL)executeQuery:(const char *)SQL {
|
||||
SEG_MUST_NOT_BE_MAIN_THREAD();
|
||||
char *error;
|
||||
if (sqlite3_exec(_database, SQL, nil, nil, &error) != SQLITE_OK) {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000012", @"Failed to execute query with error %s.",
|
||||
error);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - insert
|
||||
- (void)insertMainTableApplicationNamed:(NSString *)firebaseApplication
|
||||
customInstanceIdentifier:(NSString *)customInstanceIdentifier
|
||||
firebaseInstanceIdentifier:(NSString *)firebaseInstanceIdentifier
|
||||
associationStatus:(NSString *)associationStatus
|
||||
completionHandler:(SEGRequestCompletion)handler {
|
||||
// TODO: delete the row first.
|
||||
__weak SEGDatabaseManager *weakSelf = self;
|
||||
dispatch_async(_databaseOperationQueue, ^{
|
||||
NSArray<NSString *> *values =
|
||||
[[NSArray alloc] initWithObjects:firebaseApplication, customInstanceIdentifier,
|
||||
firebaseInstanceIdentifier, associationStatus, nil];
|
||||
BOOL success = [weakSelf insertMainTableWithValues:values];
|
||||
if (handler) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
handler(success, nil);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)insertMainTableWithValues:(NSArray<NSString *> *)values {
|
||||
SEG_MUST_NOT_BE_MAIN_THREAD();
|
||||
if (values.count != 4) {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000013",
|
||||
@"Failed to insert config record. Wrong number of give parameters, current "
|
||||
@"number is %ld, correct number is 4.",
|
||||
(long)values.count);
|
||||
return NO;
|
||||
}
|
||||
NSString *SQL = [NSString stringWithFormat:@"INSERT INTO %@ (%@, %@, %@, %@) values (?, ?, ?, ?)",
|
||||
kMainTableName, kMainTableColumnApplicationIdentifier,
|
||||
kMainTableColumnCustomInstallationIdentifier,
|
||||
kMainTableColumnFirebaseInstallationIdentifier,
|
||||
kMainTableColumnAssociationStatus];
|
||||
|
||||
sqlite3_stmt *statement = [self prepareSQL:[SQL UTF8String]];
|
||||
if (!statement) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *aString = values[0];
|
||||
if (![self bindStringToStatement:statement index:1 string:aString]) {
|
||||
return [self logErrorWithSQL:[SQL UTF8String] finalizeStatement:statement returnValue:NO];
|
||||
}
|
||||
aString = values[1];
|
||||
if (![self bindStringToStatement:statement index:2 string:aString]) {
|
||||
return [self logErrorWithSQL:[SQL UTF8String] finalizeStatement:statement returnValue:NO];
|
||||
}
|
||||
aString = values[2];
|
||||
if (![self bindStringToStatement:statement index:3 string:aString]) {
|
||||
return [self logErrorWithSQL:[SQL UTF8String] finalizeStatement:statement returnValue:NO];
|
||||
}
|
||||
aString = values[3];
|
||||
if (![self bindStringToStatement:statement index:4 string:aString]) {
|
||||
return [self logErrorWithSQL:[SQL UTF8String] finalizeStatement:statement returnValue:NO];
|
||||
}
|
||||
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||
return [self logErrorWithSQL:[SQL UTF8String] finalizeStatement:statement returnValue:NO];
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
return YES;
|
||||
}
|
||||
|
||||
/// TODO: (Check if required). Clear the record of given namespace and package name
|
||||
/// before updating the table.
|
||||
- (void)deleteRecordFromMainTableWithCustomInstanceIdentifier:
|
||||
(nonnull NSString *)customInstanceIdentifier {
|
||||
}
|
||||
|
||||
/// TODO: (Check if required). Remove all the records from a config content table.
|
||||
- (void)deleteAllRecordsFromTable {
|
||||
}
|
||||
|
||||
#pragma mark - helper
|
||||
- (BOOL)executeQuery:(const char *)SQL withParams:(NSArray *)params {
|
||||
SEG_MUST_NOT_BE_MAIN_THREAD();
|
||||
sqlite3_stmt *statement = [self prepareSQL:SQL];
|
||||
if (!statement) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self bindStringsToStatement:statement stringArray:params];
|
||||
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
return YES;
|
||||
}
|
||||
|
||||
/// Params only accept TEXT format string.
|
||||
- (BOOL)bindStringsToStatement:(sqlite3_stmt *)statement stringArray:(NSArray *)array {
|
||||
int index = 1;
|
||||
for (NSString *param in array) {
|
||||
if (![self bindStringToStatement:statement index:index string:param]) {
|
||||
return [self logErrorWithSQL:nil finalizeStatement:statement returnValue:NO];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)bindStringToStatement:(sqlite3_stmt *)statement index:(int)index string:(NSString *)value {
|
||||
if (sqlite3_bind_text(statement, index, [value UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK) {
|
||||
return [self logErrorWithSQL:nil finalizeStatement:statement returnValue:NO];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (sqlite3_stmt *)prepareSQL:(const char *)SQL {
|
||||
sqlite3_stmt *statement = nil;
|
||||
if (sqlite3_prepare_v2(_database, SQL, -1, &statement, NULL) != SQLITE_OK) {
|
||||
[self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
|
||||
return nil;
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
|
||||
- (NSString *)errorMessage {
|
||||
return [NSString stringWithFormat:@"%s", sqlite3_errmsg(_database)];
|
||||
}
|
||||
|
||||
- (int)errorCode {
|
||||
return sqlite3_errcode(_database);
|
||||
}
|
||||
|
||||
- (void)logDatabaseError {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000015", @"Error message: %@. Error code: %d.",
|
||||
[self errorMessage], [self errorCode]);
|
||||
}
|
||||
|
||||
- (BOOL)logErrorWithSQL:(const char *)SQL
|
||||
finalizeStatement:(sqlite3_stmt *)statement
|
||||
returnValue:(BOOL)returnValue {
|
||||
if (SQL) {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000016", @"Failed with SQL: %s.", SQL);
|
||||
}
|
||||
[self logDatabaseError];
|
||||
|
||||
if (statement) {
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
|
||||
|
||||
#import "FirebaseSegmentation/Sources/SEGSegmentationConstants.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SEGNetworkManager : NSObject
|
||||
|
||||
- (instancetype)initWithOptions:(FIROptions *)options;
|
||||
|
||||
- (void)makeAssociationRequestToBackendWithData:
|
||||
(nonnull NSDictionary<NSString *, id> *)associationData
|
||||
token:(NSString *)token
|
||||
completion:(SEGRequestCompletion)completionHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,223 @@
|
||||
// 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 "FirebaseSegmentation/Sources/SEGNetworkManager.h"
|
||||
|
||||
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRLogger.h"
|
||||
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
|
||||
|
||||
// TODO(dmandar): define in build file.
|
||||
#define SEG_ALPHA_SERVER
|
||||
|
||||
static NSString *const kServerURLDomain = @"https://firebasesegmentation.googleapis.com";
|
||||
|
||||
#ifdef SEG_ALPHA_SERVER
|
||||
static NSString *const kServerURLVersion = @"/v1alpha";
|
||||
#else
|
||||
static NSString *const kServerURLVersion = @"/v1";
|
||||
#endif
|
||||
|
||||
static NSString *const kServerURLStringProjects = @"/projects/";
|
||||
static NSString *const kServerURLStringInstallations = @"/installations/";
|
||||
static NSString *const kServerURLStringCustomSegmentationData = @"/customSegmentationData";
|
||||
|
||||
static NSString *const kHTTPMethodPatch = @"PATCH";
|
||||
static NSString *const kRequestHeaderAuthorizationValueString = @"FIREBASE_INSTALLATIONS_AUTH";
|
||||
static NSString *const kRequestDataCustomInstallationIdString = @"custom_installation_id";
|
||||
|
||||
// HTTP header names.
|
||||
static NSString *const kHeaderNameAPIKey = @"x-goog-api-key";
|
||||
static NSString *const kHeaderNameFirebaseAuthorizationToken = @"Authorization";
|
||||
static NSString *const kHeaderNameContentType = @"Content-Type";
|
||||
static NSString *const kHeaderNameContentEncoding = @"Content-Encoding";
|
||||
static NSString *const kHeaderNameAcceptEncoding = @"Accept-Encoding";
|
||||
|
||||
// Sends the bundle ID. Refer to b/130301479 for details.
|
||||
static NSString *const kiOSBundleIdentifierHeaderName =
|
||||
@"X-Ios-Bundle-Identifier"; ///< HTTP Header Field Name
|
||||
|
||||
/// Config HTTP request content type JSON
|
||||
static NSString *const kContentTypeValueJSON = @"application/json";
|
||||
|
||||
// TODO: Handle error codes.
|
||||
/// HTTP status codes. Ref: https://cloud.google.com/apis/design/errors#error_retries
|
||||
static NSInteger const kSEGResponseHTTPStatusCodeOK = 200;
|
||||
// static NSInteger const kSEGResponseHTTPStatusCodeConflict = 409;
|
||||
// static NSInteger const kSEGResponseHTTPStatusTooManyRequests = 429;
|
||||
// static NSInteger const kSEGResponseHTTPStatusCodeInternalError = 500;
|
||||
// static NSInteger const kSEGResponseHTTPStatusCodeServiceUnavailable = 503;
|
||||
// static NSInteger const kSEGResponseHTTPStatusCodeGatewayTimeout = 504;
|
||||
|
||||
// HTTP default timeout.
|
||||
static NSTimeInterval const kSEGHTTPRequestTimeout = 60;
|
||||
|
||||
/// Completion handler invoked by URLSession completion handler.
|
||||
typedef void (^URLSessionCompletion)(NSData *data, NSURLResponse *response, NSError *error);
|
||||
|
||||
@implementation SEGNetworkManager {
|
||||
FIROptions *_firebaseAppOptions;
|
||||
NSURLSession *_URLSession;
|
||||
}
|
||||
|
||||
- (instancetype)initWithOptions:(FIROptions *)options {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_firebaseAppOptions = options;
|
||||
_URLSession = [self newURLSession];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_URLSession invalidateAndCancel];
|
||||
}
|
||||
|
||||
- (void)makeAssociationRequestToBackendWithData:
|
||||
(NSDictionary<NSString *, NSString *> *)associationData
|
||||
token:(NSString *)token
|
||||
completion:(SEGRequestCompletion)completionHandler {
|
||||
// Construct the server URL.
|
||||
NSString *URL = [self constructServerURLWithAssociationData:associationData];
|
||||
if (!URL) {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000020", @"Could not construct backend URL.");
|
||||
completionHandler(NO, @{@"errorDescription" : @"Could not construct backend URL"});
|
||||
}
|
||||
|
||||
FIRLogDebug(kFIRLoggerSegmentation, @"I-SEG000019", @"%@",
|
||||
[NSString stringWithFormat:@"Making config request: %@", URL]);
|
||||
|
||||
// Construct the request data.
|
||||
NSString *customInstallationIdentifier =
|
||||
[associationData objectForKey:kSEGCustomInstallationIdentifierKey];
|
||||
// TODO: Add tests for nil.
|
||||
NSDictionary<NSString *, NSString *> *requestDataDictionary =
|
||||
@{kRequestDataCustomInstallationIdString : customInstallationIdentifier};
|
||||
NSError *error = nil;
|
||||
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestDataDictionary
|
||||
options:0
|
||||
error:nil];
|
||||
if (!requestData || error) {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000021", @"Could not create request data. %@",
|
||||
error.localizedDescription);
|
||||
completionHandler(NO,
|
||||
@{@"errorDescription" : @"Could not serialize JSON data for network call."});
|
||||
}
|
||||
|
||||
// Handle NSURLSession completion.
|
||||
__weak SEGNetworkManager *weakSelf = self;
|
||||
[self URLSessionDataTaskWithURL:URL
|
||||
content:requestData
|
||||
token:token
|
||||
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
SEGNetworkManager *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000022",
|
||||
@"Internal error making network request.");
|
||||
completionHandler(
|
||||
NO, @{@"errorDescription" : @"Internal error making network request."});
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger statusCode = [((NSHTTPURLResponse *)response) statusCode];
|
||||
if (!error && (statusCode == kSEGResponseHTTPStatusCodeOK)) {
|
||||
FIRLogDebug(kFIRLoggerSegmentation, @"I-SEG000017",
|
||||
@"SEGNetworkManager: Network request successful.");
|
||||
completionHandler(YES, nil);
|
||||
} else {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000018",
|
||||
@"SEGNetworkManager: Network request failed with status code:%lu",
|
||||
(long)statusCode);
|
||||
completionHandler(NO, @{
|
||||
@"ErrorDescription" :
|
||||
[NSString stringWithFormat:@"Network Error: %lu", (long)statusCode]
|
||||
});
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (NSURLSession *)newURLSession {
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
config.timeoutIntervalForRequest = kSEGHTTPRequestTimeout;
|
||||
config.timeoutIntervalForResource = kSEGHTTPRequestTimeout;
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
|
||||
return session;
|
||||
}
|
||||
|
||||
- (NSString *)constructServerURLWithAssociationData:
|
||||
(NSDictionary<NSString *, NSString *> *)associationData {
|
||||
NSString *serverURLStr = [[NSString alloc] initWithString:kServerURLDomain];
|
||||
serverURLStr = [serverURLStr stringByAppendingString:kServerURLVersion];
|
||||
serverURLStr = [serverURLStr stringByAppendingString:kServerURLStringProjects];
|
||||
|
||||
if (_firebaseAppOptions.projectID) {
|
||||
serverURLStr = [serverURLStr stringByAppendingString:_firebaseAppOptions.projectID];
|
||||
} else {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000070",
|
||||
@"Missing `projectID` from `FirebaseOptions`, please ensure the configured "
|
||||
@"`FirebaseApp` is configured with `FirebaseOptions` that contains a `projectID`.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
serverURLStr = [serverURLStr stringByAppendingString:kServerURLStringInstallations];
|
||||
|
||||
// Get the FID.
|
||||
NSString *firebaseInstallationIdentifier =
|
||||
[associationData objectForKey:kSEGFirebaseInstallationIdentifierKey];
|
||||
if (!firebaseInstallationIdentifier) {
|
||||
FIRLogError(kFIRLoggerSegmentation, @"I-SEG000071",
|
||||
@"Missing Firebase installation identifier");
|
||||
return nil;
|
||||
}
|
||||
serverURLStr = [serverURLStr stringByAppendingString:firebaseInstallationIdentifier];
|
||||
serverURLStr = [serverURLStr stringByAppendingString:kServerURLStringCustomSegmentationData];
|
||||
|
||||
return serverURLStr;
|
||||
}
|
||||
|
||||
- (void)URLSessionDataTaskWithURL:(NSString *)stringURL
|
||||
content:(NSData *)content
|
||||
token:(NSString *)token
|
||||
completionHandler:(URLSessionCompletion)completionHandler {
|
||||
NSTimeInterval timeoutInterval = kSEGHTTPRequestTimeout;
|
||||
NSURL *URL = [NSURL URLWithString:stringURL];
|
||||
NSMutableURLRequest *URLRequest =
|
||||
[[NSMutableURLRequest alloc] initWithURL:URL
|
||||
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval:timeoutInterval];
|
||||
URLRequest.HTTPMethod = kHTTPMethodPatch;
|
||||
|
||||
// Setup headers.
|
||||
[URLRequest setValue:_firebaseAppOptions.APIKey forHTTPHeaderField:kHeaderNameAPIKey];
|
||||
NSString *authorizationTokenHeaderValue =
|
||||
[NSString stringWithFormat:@"%@ %@", kRequestHeaderAuthorizationValueString, token];
|
||||
[URLRequest setValue:authorizationTokenHeaderValue
|
||||
forHTTPHeaderField:kHeaderNameFirebaseAuthorizationToken];
|
||||
// TODO: Check if we accept gzip.
|
||||
// [URLRequest setValue:@"gzip" forHTTPHeaderField:kHeaderNameContentEncoding];
|
||||
// [URLRequest setValue:@"gzip" forHTTPHeaderField:kHeaderNameAcceptEncoding];
|
||||
|
||||
// Send the bundleID for API Key restrictions.
|
||||
[URLRequest setValue:[[NSBundle mainBundle] bundleIdentifier]
|
||||
forHTTPHeaderField:kiOSBundleIdentifierHeaderName];
|
||||
[URLRequest setHTTPBody:content];
|
||||
|
||||
NSURLSessionDataTask *task = [_URLSession dataTaskWithRequest:URLRequest
|
||||
completionHandler:completionHandler];
|
||||
[task resume];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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 <Foundation/Foundation.h>
|
||||
|
||||
#ifndef SEGSegmentationConstants_h
|
||||
#define SEGSegmentationConstants_h
|
||||
|
||||
#if defined(DEBUG)
|
||||
#define SEG_MUST_NOT_BE_MAIN_THREAD() \
|
||||
do { \
|
||||
NSAssert(![NSThread isMainThread], @"Must not be executing on the main thread."); \
|
||||
} while (0);
|
||||
#else
|
||||
#define SEG_MUST_NOT_BE_MAIN_THREAD() \
|
||||
do { \
|
||||
} while (0);
|
||||
#endif
|
||||
|
||||
static NSString* const kFIRLoggerSegmentation = @"[Firebase/Segmentation]";
|
||||
|
||||
/// Keys for values stored in the Segmentation SDK.
|
||||
static NSString* const kSEGFirebaseApplicationIdentifierKey = @"firebase_app_identifier";
|
||||
static NSString* const kSEGCustomInstallationIdentifierKey = @"custom_installation_identifier";
|
||||
static NSString* const kSEGFirebaseInstallationIdentifierKey = @"firebase_installation_identifier";
|
||||
static NSString* const kSEGAssociationStatusKey = @"association_status";
|
||||
/// Association Status
|
||||
static NSString* const kSEGAssociationStatusPending = @"PENDING";
|
||||
static NSString* const kSEGAssociationStatusAssociated = @"ASSOCIATED";
|
||||
|
||||
/// Segmentation error domain when logging errors.
|
||||
static NSString* const kFirebaseSegmentationErrorDomain = @"com.firebase.segmentation";
|
||||
|
||||
/// Segmentation Request Completion callback.
|
||||
/// @param success Decide whether the network operation succeeds.
|
||||
/// @param result Return operation result data.
|
||||
typedef void (^SEGRequestCompletion)(BOOL success, NSDictionary<NSString*, id>* result);
|
||||
|
||||
#endif /* SEGSegmentationConstants_h */
|
||||
@@ -0,0 +1,15 @@
|
||||
# Uncomment the next two lines for pre-release testing on internal repo
|
||||
#source 'sso://cpdc-internal/firebase'
|
||||
#source 'https://cdn.cocoapods.org/'
|
||||
|
||||
target 'SegmentationSampleApp' do
|
||||
# Comment the next line if you don't want to use dynamic frameworks
|
||||
use_frameworks!
|
||||
inherit! :search_paths
|
||||
platform :ios, '8.0'
|
||||
|
||||
# Pods for SegmentationSampleApp
|
||||
pod 'FirebaseCore', :path => '../../../'
|
||||
pod 'FirebaseSegmentation', :path => '../../../'
|
||||
|
||||
end
|
||||
@@ -0,0 +1,426 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
5B4DE0A622F8D7B100B55A7B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B4DE0A522F8D7B100B55A7B /* AppDelegate.m */; };
|
||||
5B4DE0A922F8D7B100B55A7B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B4DE0A822F8D7B100B55A7B /* ViewController.m */; };
|
||||
5B4DE0AC22F8D7B100B55A7B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5B4DE0AA22F8D7B100B55A7B /* Main.storyboard */; };
|
||||
5B4DE0AE22F8D7B300B55A7B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5B4DE0AD22F8D7B300B55A7B /* Assets.xcassets */; };
|
||||
5B4DE0B122F8D7B300B55A7B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5B4DE0AF22F8D7B300B55A7B /* LaunchScreen.storyboard */; };
|
||||
5B4DE0B422F8D7B300B55A7B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B4DE0B322F8D7B300B55A7B /* main.m */; };
|
||||
5B4DE0BD22F8DA1300B55A7B /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5B4DE0BB22F8DA1300B55A7B /* GoogleService-Info.plist */; };
|
||||
AC252CE6B594C95D235E64F9 /* Pods_SegmentationSampleApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03FB7E52AF4C6B5EAA580F43 /* Pods_SegmentationSampleApp.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
03FB7E52AF4C6B5EAA580F43 /* Pods_SegmentationSampleApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SegmentationSampleApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
23212B47B363359777FCF914 /* Pods-SegmentationSampleApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SegmentationSampleApp.debug.xcconfig"; path = "Target Support Files/Pods-SegmentationSampleApp/Pods-SegmentationSampleApp.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
2B8D0D05EDF54422ACEF8C82 /* Pods-SegmentationSampleApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SegmentationSampleApp.release.xcconfig"; path = "Target Support Files/Pods-SegmentationSampleApp/Pods-SegmentationSampleApp.release.xcconfig"; sourceTree = "<group>"; };
|
||||
5B4DE0A122F8D7B100B55A7B /* SegmentationSampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SegmentationSampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5B4DE0A422F8D7B100B55A7B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
5B4DE0A522F8D7B100B55A7B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
5B4DE0A722F8D7B100B55A7B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
||||
5B4DE0A822F8D7B100B55A7B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
||||
5B4DE0AB22F8D7B100B55A7B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
5B4DE0AD22F8D7B300B55A7B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
5B4DE0B022F8D7B300B55A7B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
5B4DE0B222F8D7B300B55A7B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
5B4DE0B322F8D7B300B55A7B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
5B4DE0BA22F8DA1200B55A7B /* SecondApp-GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SecondApp-GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
5B4DE0BB22F8DA1300B55A7B /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
5B4DE0BF22F8E07200B55A7B /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../../../configdaily googservice/GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
5B4DE0C222F8E09900B55A7B /* SecondApp-GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SecondApp-GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
5B4DE0C322F8E09900B55A7B /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
5B4DE09E22F8D7B100B55A7B /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AC252CE6B594C95D235E64F9 /* Pods_SegmentationSampleApp.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1DAAA9CDE17A7AD3D4D58BFA /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
23212B47B363359777FCF914 /* Pods-SegmentationSampleApp.debug.xcconfig */,
|
||||
2B8D0D05EDF54422ACEF8C82 /* Pods-SegmentationSampleApp.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2074D9A873FD02A71321C678 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5B4DE0BF22F8E07200B55A7B /* GoogleService-Info.plist */,
|
||||
03FB7E52AF4C6B5EAA580F43 /* Pods_SegmentationSampleApp.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5B4DE09822F8D7B100B55A7B = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5B4DE0BB22F8DA1300B55A7B /* GoogleService-Info.plist */,
|
||||
5B4DE0C322F8E09900B55A7B /* GoogleService-Info.plist */,
|
||||
5B4DE0BA22F8DA1200B55A7B /* SecondApp-GoogleService-Info.plist */,
|
||||
5B4DE0C222F8E09900B55A7B /* SecondApp-GoogleService-Info.plist */,
|
||||
5B4DE0A322F8D7B100B55A7B /* SegmentationSampleApp */,
|
||||
5B4DE0A222F8D7B100B55A7B /* Products */,
|
||||
1DAAA9CDE17A7AD3D4D58BFA /* Pods */,
|
||||
2074D9A873FD02A71321C678 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5B4DE0A222F8D7B100B55A7B /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5B4DE0A122F8D7B100B55A7B /* SegmentationSampleApp.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5B4DE0A322F8D7B100B55A7B /* SegmentationSampleApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5B4DE0A422F8D7B100B55A7B /* AppDelegate.h */,
|
||||
5B4DE0A522F8D7B100B55A7B /* AppDelegate.m */,
|
||||
5B4DE0A722F8D7B100B55A7B /* ViewController.h */,
|
||||
5B4DE0A822F8D7B100B55A7B /* ViewController.m */,
|
||||
5B4DE0AA22F8D7B100B55A7B /* Main.storyboard */,
|
||||
5B4DE0AD22F8D7B300B55A7B /* Assets.xcassets */,
|
||||
5B4DE0AF22F8D7B300B55A7B /* LaunchScreen.storyboard */,
|
||||
5B4DE0B222F8D7B300B55A7B /* Info.plist */,
|
||||
5B4DE0B322F8D7B300B55A7B /* main.m */,
|
||||
);
|
||||
path = SegmentationSampleApp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
5B4DE0A022F8D7B100B55A7B /* SegmentationSampleApp */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5B4DE0B722F8D7B300B55A7B /* Build configuration list for PBXNativeTarget "SegmentationSampleApp" */;
|
||||
buildPhases = (
|
||||
B73B50EA390CE7F1ED248C8C /* [CP] Check Pods Manifest.lock */,
|
||||
5B4DE09D22F8D7B100B55A7B /* Sources */,
|
||||
5B4DE09E22F8D7B100B55A7B /* Frameworks */,
|
||||
5B4DE09F22F8D7B100B55A7B /* Resources */,
|
||||
F28D7493DD28A2A2A72AF1FC /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = SegmentationSampleApp;
|
||||
productName = SegmentationSampleApp;
|
||||
productReference = 5B4DE0A122F8D7B100B55A7B /* SegmentationSampleApp.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
5B4DE09922F8D7B100B55A7B /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1010;
|
||||
ORGANIZATIONNAME = "Mandar Deolalikar";
|
||||
TargetAttributes = {
|
||||
5B4DE0A022F8D7B100B55A7B = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 5B4DE09C22F8D7B100B55A7B /* Build configuration list for PBXProject "SegmentationSampleApp" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 5B4DE09822F8D7B100B55A7B;
|
||||
productRefGroup = 5B4DE0A222F8D7B100B55A7B /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
5B4DE0A022F8D7B100B55A7B /* SegmentationSampleApp */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
5B4DE09F22F8D7B100B55A7B /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5B4DE0BD22F8DA1300B55A7B /* GoogleService-Info.plist in Resources */,
|
||||
5B4DE0B122F8D7B300B55A7B /* LaunchScreen.storyboard in Resources */,
|
||||
5B4DE0AE22F8D7B300B55A7B /* Assets.xcassets in Resources */,
|
||||
5B4DE0AC22F8D7B100B55A7B /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
B73B50EA390CE7F1ED248C8C /* [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-SegmentationSampleApp-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;
|
||||
};
|
||||
F28D7493DD28A2A2A72AF1FC /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-SegmentationSampleApp/Pods-SegmentationSampleApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-SegmentationSampleApp/Pods-SegmentationSampleApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SegmentationSampleApp/Pods-SegmentationSampleApp-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
5B4DE09D22F8D7B100B55A7B /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5B4DE0A922F8D7B100B55A7B /* ViewController.m in Sources */,
|
||||
5B4DE0B422F8D7B300B55A7B /* main.m in Sources */,
|
||||
5B4DE0A622F8D7B100B55A7B /* AppDelegate.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
5B4DE0AA22F8D7B100B55A7B /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
5B4DE0AB22F8D7B100B55A7B /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5B4DE0AF22F8D7B300B55A7B /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
5B4DE0B022F8D7B300B55A7B /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
5B4DE0B522F8D7B300B55A7B /* 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;
|
||||
};
|
||||
5B4DE0B622F8D7B300B55A7B /* 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;
|
||||
};
|
||||
5B4DE0B822F8D7B300B55A7B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 23212B47B363359777FCF914 /* Pods-SegmentationSampleApp.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = EQHXZ8M8AV;
|
||||
INFOPLIST_FILE = SegmentationSampleApp/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.config.testapp.dev;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Firebase Remote Config TestApp Dev";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
5B4DE0B922F8D7B300B55A7B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 2B8D0D05EDF54422ACEF8C82 /* Pods-SegmentationSampleApp.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = EQHXZ8M8AV;
|
||||
INFOPLIST_FILE = SegmentationSampleApp/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.config.testapp.dev;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Firebase Remote Config TestApp Dev";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
5B4DE09C22F8D7B100B55A7B /* Build configuration list for PBXProject "SegmentationSampleApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5B4DE0B522F8D7B300B55A7B /* Debug */,
|
||||
5B4DE0B622F8D7B300B55A7B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
5B4DE0B722F8D7B300B55A7B /* Build configuration list for PBXNativeTarget "SegmentationSampleApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5B4DE0B822F8D7B300B55A7B /* Debug */,
|
||||
5B4DE0B922F8D7B300B55A7B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 5B4DE09922F8D7B100B55A7B /* Project object */;
|
||||
}
|
||||
@@ -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 <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property(strong, nonatomic) UIWindow *window;
|
||||
|
||||
@end
|
||||
@@ -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 "AppDelegate.h"
|
||||
#import <FirebaseCore/FirebaseCore.h>
|
||||
#import "FirebaseSegmentation.h"
|
||||
|
||||
@interface AppDelegate ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// Override point for customization after application launch.
|
||||
[FIRApp configure];
|
||||
FIRSegmentation *segmentation = [FIRSegmentation segmentation];
|
||||
[segmentation setCustomInstallationID:@"mandard-test-custom-installation-id3"
|
||||
completion:^(NSError *error) {
|
||||
if (error) {
|
||||
NSLog(@"Error! Could not set custom id");
|
||||
}
|
||||
}];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
+98
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key><true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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 <UIKit/UIKit.h>
|
||||
|
||||
@interface ViewController : UIViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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
|
||||
@@ -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.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#import "FirebaseSegmentation/Sources/SEGContentManager.h"
|
||||
#import "FirebaseSegmentation/Sources/SEGDatabaseManager.h"
|
||||
#import "FirebaseSegmentation/Sources/SEGNetworkManager.h"
|
||||
|
||||
#import <OCMock/OCMock.h>
|
||||
#import "FirebaseCore/Sources/Public/FirebaseCore/FirebaseCore.h"
|
||||
#import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.h"
|
||||
#import "FirebaseInstallations/Source/Library/Public/FirebaseInstallations/FirebaseInstallations.h"
|
||||
|
||||
@interface SEGContentManager (ForTest)
|
||||
- (instancetype)initWithDatabaseManager:databaseManager networkManager:networkManager;
|
||||
@end
|
||||
|
||||
@interface FIRInstallations (Tests)
|
||||
@property(nonatomic, readwrite, strong) FIROptions *appOptions;
|
||||
@property(nonatomic, readwrite, strong) NSString *appName;
|
||||
|
||||
- (instancetype)initWithAppOptions:(FIROptions *)appOptions
|
||||
appName:(NSString *)appName
|
||||
installationsIDController:(FIRInstallationsIDController *)installationsIDController
|
||||
prefetchAuthToken:(BOOL)prefetchAuthToken;
|
||||
@end
|
||||
|
||||
@interface FIRInstallationsAuthTokenResult (ForTest)
|
||||
- (instancetype)initWithToken:(NSString *)token expirationDate:(NSDate *)expirationTime;
|
||||
@end
|
||||
|
||||
@interface SEGContentManagerTests : XCTestCase
|
||||
@property(nonatomic) SEGContentManager *contentManager;
|
||||
@property(nonatomic) id networkManagerMock;
|
||||
@property(nonatomic) id mockIDController;
|
||||
@property(nonatomic) FIROptions *appOptions;
|
||||
@property(readonly) NSString *firebaseAppName;
|
||||
@property(strong, readonly, nonatomic) id mockInstallations;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SEGContentManagerTests
|
||||
|
||||
- (void)setUp {
|
||||
// Setup FIRApp.
|
||||
_firebaseAppName = @"my-firebase-app-id";
|
||||
XCTAssertNoThrow([FIRApp configureWithName:self.firebaseAppName options:[self FIRAppOptions]]);
|
||||
|
||||
// Installations Mock
|
||||
NSString *FID = @"fid-is-better-than-iid";
|
||||
_mockInstallations = OCMClassMock([FIRInstallations class]);
|
||||
OCMStub([_mockInstallations installationsWithApp:[FIRApp appNamed:self.firebaseAppName]])
|
||||
.andReturn(_mockInstallations);
|
||||
FIRInstallationsAuthTokenResult *FISToken =
|
||||
[[FIRInstallationsAuthTokenResult alloc] initWithToken:@"fake-fis-token" expirationDate:nil];
|
||||
OCMStub([_mockInstallations
|
||||
installationIDWithCompletion:([OCMArg invokeBlockWithArgs:FID, [NSNull null], nil])]);
|
||||
OCMStub([_mockInstallations
|
||||
authTokenWithCompletion:([OCMArg invokeBlockWithArgs:FISToken, [NSNull null], nil])]);
|
||||
|
||||
// Mock the network manager.
|
||||
FIROptions *options = [[FIROptions alloc] init];
|
||||
options.projectID = @"test-project-id";
|
||||
options.APIKey = @"test-api-key";
|
||||
self.networkManagerMock = OCMClassMock([SEGNetworkManager class]);
|
||||
OCMStub([self.networkManagerMock
|
||||
makeAssociationRequestToBackendWithData:[OCMArg any]
|
||||
token:[OCMArg any]
|
||||
completion:([OCMArg
|
||||
invokeBlockWithArgs:@YES, [NSNull null], nil])]);
|
||||
|
||||
// Initialize the content manager.
|
||||
self.contentManager =
|
||||
[[SEGContentManager alloc] initWithDatabaseManager:[SEGDatabaseManager sharedInstance]
|
||||
networkManager:self.networkManagerMock];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
[self.networkManagerMock stopMocking];
|
||||
self.networkManagerMock = nil;
|
||||
self.contentManager = nil;
|
||||
self.mockIDController = nil;
|
||||
}
|
||||
|
||||
// Associate a fake custom installation id and fake firebase installation id.
|
||||
// TODO(mandard): check for result and add more tests.
|
||||
- (void)testAssociateCustomInstallationIdentifierSuccessfully {
|
||||
XCTestExpectation *expectation =
|
||||
[self expectationWithDescription:@"associateCustomInstallation for contentmanager"];
|
||||
[_contentManager
|
||||
associateCustomInstallationIdentiferNamed:@"my-custom-id"
|
||||
firebaseApp:self.firebaseAppName
|
||||
completion:^(BOOL success, NSDictionary *result) {
|
||||
XCTAssertTrue(success,
|
||||
@"Could not associate custom installation ID");
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:10 handler:nil];
|
||||
}
|
||||
|
||||
#pragma mark private
|
||||
|
||||
- (FIROptions *)FIRAppOptions {
|
||||
FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
|
||||
GCMSenderID:@"correct_gcm_sender_id"];
|
||||
options.APIKey = @"AIzaSaaaaaaaaaaaaaaaaaaaaaaaaaaa1111111";
|
||||
options.projectID = @"abc-xyz-123";
|
||||
return options;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,118 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#import "FirebaseSegmentation/Sources/SEGDatabaseManager.h"
|
||||
|
||||
#import <OCMock/OCMock.h>
|
||||
|
||||
@interface SEGDatabaseManager (Test)
|
||||
- (NSString *)pathForSegmentationDatabase;
|
||||
@end
|
||||
|
||||
@interface SEGDatabaseManagerTests : XCTestCase {
|
||||
long _expectationTimeout;
|
||||
}
|
||||
@property(nonatomic) id databaseManagerMock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SEGDatabaseManagerTests
|
||||
|
||||
- (void)setUp {
|
||||
// Override the database path to create a test database.
|
||||
self.databaseManagerMock = OCMClassMock([SEGDatabaseManager class]);
|
||||
OCMStub([self.databaseManagerMock pathForSegmentationDatabase])
|
||||
.andReturn([self pathForSegmentationTestDatabase]);
|
||||
|
||||
// Expectation timeout for each test.
|
||||
_expectationTimeout = 2;
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
[self.databaseManagerMock stopMocking];
|
||||
self.databaseManagerMock = nil;
|
||||
}
|
||||
|
||||
- (void)testDatabaseCreateOrOpen {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"testDatabaseLoad"];
|
||||
// Initialize the database manager.
|
||||
SEGDatabaseManager *databaseManager = [[SEGDatabaseManager alloc] init];
|
||||
XCTAssertNotNil(databaseManager);
|
||||
// Load all data from the database.
|
||||
[databaseManager createOrOpenDatabaseWithCompletion:^(BOOL success, NSDictionary *result) {
|
||||
XCTAssertTrue(success);
|
||||
XCTAssertTrue(result.count == 0);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
|
||||
}
|
||||
|
||||
- (void)testDatabaseInsertAndRead {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"testDatabaseLoad"];
|
||||
// Initialize the database manager.
|
||||
SEGDatabaseManager *databaseManager = [[SEGDatabaseManager alloc] init];
|
||||
XCTAssertNotNil(databaseManager);
|
||||
// Load all data from the database.
|
||||
[databaseManager createOrOpenDatabaseWithCompletion:^(BOOL success, NSDictionary *result) {
|
||||
XCTAssertTrue(success);
|
||||
XCTAssertTrue(result.count == 0, "Result was %@", result);
|
||||
|
||||
// Insert data.
|
||||
[databaseManager
|
||||
insertMainTableApplicationNamed:@"firebase_test_app"
|
||||
customInstanceIdentifier:@"custom-123"
|
||||
firebaseInstanceIdentifier:@"firebase-123"
|
||||
associationStatus:kSEGAssociationStatusPending
|
||||
completionHandler:^(BOOL success, NSDictionary *result) {
|
||||
XCTAssertTrue(success);
|
||||
XCTAssertNil(result);
|
||||
|
||||
// Read data.
|
||||
[databaseManager loadMainTableWithCompletion:^(BOOL success,
|
||||
NSDictionary *result) {
|
||||
XCTAssertTrue(success);
|
||||
XCTAssertEqual(result.count, 1);
|
||||
NSDictionary *associations = [result objectForKey:@"firebase_test_app"];
|
||||
XCTAssertNotNil(associations);
|
||||
XCTAssertEqualObjects(
|
||||
[associations objectForKey:kSEGCustomInstallationIdentifierKey],
|
||||
@"custom-123");
|
||||
XCTAssertEqualObjects(
|
||||
[associations objectForKey:kSEGFirebaseInstallationIdentifierKey],
|
||||
@"firebase-123");
|
||||
XCTAssertEqualObjects(
|
||||
[associations objectForKey:kSEGAssociationStatusKey],
|
||||
kSEGAssociationStatusPending);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
|
||||
}
|
||||
|
||||
#pragma mark Helpers
|
||||
- (NSString *)pathForSegmentationTestDatabase {
|
||||
NSArray *dirPaths =
|
||||
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString *appSupportPath = dirPaths.firstObject;
|
||||
NSString *databaseName =
|
||||
[NSString stringWithFormat:@"FirebaseSegmentation-test-%d.sqlite3", (arc4random() % 100)];
|
||||
NSArray *components = @[ appSupportPath, @"Google/FirebaseSegmentation", databaseName ];
|
||||
NSString *dbPath = [NSString pathWithComponents:components];
|
||||
NSLog(@"Created test database at: %@", dbPath);
|
||||
return dbPath;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRApp.h"
|
||||
#import "FirebaseCore/Sources/Public/FirebaseCore/FIROptions.h"
|
||||
#import "FirebaseSegmentation/Sources/Public/FIRSegmentation.h"
|
||||
|
||||
@interface FIRSegmentation (ForTest)
|
||||
- (instancetype)initWithAppName:(NSString *)appName FIROptions:(FIROptions *)options;
|
||||
@end
|
||||
|
||||
@interface SEGInitializationTests : XCTestCase {
|
||||
FIRSegmentation *_segmentation;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation SEGInitializationTests
|
||||
|
||||
- (void)setUp {
|
||||
FIROptions *options = [[FIROptions alloc] init];
|
||||
options.APIKey = @"test-api-key";
|
||||
options.projectID = @"test-firebase-project-id";
|
||||
_segmentation = [[FIRSegmentation alloc] initWithAppName:@"test-firebase-app-name"
|
||||
FIROptions:options];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the
|
||||
// class.
|
||||
}
|
||||
|
||||
- (void)testExample {
|
||||
[_segmentation setCustomInstallationID:@"test-custom-id"
|
||||
completion:^(NSError *error){
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -74,7 +74,6 @@ EXAMPLES:
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
set -euo pipefail
|
||||
unset CDPATH
|
||||
|
||||
|
||||
@@ -55,8 +55,9 @@ else
|
||||
'InAppMessaging|Firebase/InAppMessaging|'\
|
||||
'FirebaseInAppMessaging.podspec|'\
|
||||
'Firebase/InstanceID|FirebaseInstanceID.podspec|'\
|
||||
'FirebaseInstallations'\
|
||||
'FirebaseCrashlytics.podspec)'\
|
||||
'FirebaseInstallations|'\
|
||||
'FirebaseCrashlytics.podspec|'\
|
||||
'FirebaseSegmentation.podspec)'\
|
||||
;;
|
||||
|
||||
FirebasePod-*)
|
||||
@@ -146,6 +147,10 @@ else
|
||||
check_changes '^(FirebaseCore|GoogleUtilities|FirebaseInstallations)'
|
||||
;;
|
||||
|
||||
Segmentation-*)
|
||||
check_changes '^(Firebase/Core|FirebaseSegmentation|FirebaseSegmentation.podspec)'
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown project-method combo" 1>&2
|
||||
echo " PROJECT=$PROJECT" 1>&2
|
||||
|
||||
Reference in new issue
Block a user