Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions FirebaseSegmentation.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Firebase Segmentation enables you to associate your custom application instance
s.static_framework = true
s.prefix_header_file = false

s.source_files = 'FirebaseSegmentation/Sources/**/*'
s.source_files = 'FirebaseSegmentation/Sources/**/*.[mh]'
s.public_header_files = 'FirebaseSegmentation/Sources/Public/*.h'

s.dependency 'FirebaseCore', '~> 6.1'
Expand All @@ -29,8 +29,6 @@ Firebase Segmentation enables you to associate your custom application instance
'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}"'
}

s.user_target_xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '$(PLATFORM_DIR)/Developer/Library/Frameworks' }

s.pod_target_xcconfig = {
'GCC_C_LANGUAGE_STANDARD' => 'c99',
'GCC_PREPROCESSOR_DEFINITIONS' => 'FIRSegmentation_VERSION=' + s.version.to_s
Expand All @@ -41,4 +39,4 @@ s.user_target_xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '$(PLATFORM_DIR)/Develope
unit_tests.dependency 'OCMock'
unit_tests.requires_app_host = true
end
end
end
29 changes: 24 additions & 5 deletions FirebaseSegmentation/Sources/FIRSegmentation.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
#import <FirebaseCore/FIRComponentContainer.h>
#import <FirebaseCore/FIRLogger.h>
#import <FirebaseCore/FIROptionsInternal.h>
#import <FirebaseSegmentation/Sources/Private/FIRSegmentationComponent.h>

FIRLoggerService kFIRLoggerSegmentation = @"[Firebase/Segmentation]";
#import "FirebaseSegmentation/Sources/Private/FIRSegmentationComponent.h"
#import "FirebaseSegmentation/Sources/SEGContentManager.h"

@implementation FIRSegmentation {
NSString *_appName;
NSString *_firebaseAppName;
SEGContentManager *_contentManager;
}

+ (nonnull FIRSegmentation *)segmentation {
Expand All @@ -45,14 +45,33 @@ + (nonnull FIRSegmentation *)segmentationWithApp:(nonnull FIRApp *)firebaseApp {

- (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) {
_appName = appName;
_firebaseAppName = appName;

// Initialize the content manager.
_contentManager = [SEGContentManager sharedInstanceWithOptions:options];
}
return self;
}

@end
8 changes: 5 additions & 3 deletions FirebaseSegmentation/Sources/FIRSegmentationComponent.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#import <FirebaseCore/FIRComponentContainer.h>
#import <FirebaseCore/FIROptionsInternal.h>
#import "FirebaseSegmentation/Sources/Private/FIRSegmentationInternal.h"
#import "FirebaseSegmentation/Sources/SEGSegmentationConstants.h"

#ifndef FIRSegmentation_VERSION
#error "FIRSegmentation_VERSION is not defined: \
Expand All @@ -29,8 +30,6 @@
#define STR(x) STR_EXPAND(x)
#define STR_EXPAND(x) #x

NSString *const kFirebaseSegmentationErrorDomain = @"com.firebase.segmentation";

@implementation FIRSegmentationComponent

/// Default method for retrieving a Segmentation instance, or creating one if it doesn't exist.
Expand Down Expand Up @@ -70,7 +69,10 @@ - (instancetype)initWithApp:(FIRApp *)app {
self = [super init];
if (self) {
_app = app;
_segmentationInstance = nil;
if (!_segmentationInstance) {
_segmentationInstance = [[FIRSegmentation alloc] initWithAppName:app.name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you describe a situation where _segmentationInstance would be non-nil when initWithApp: is called? I'm not sure I can think of a case when that's true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not typical. Unless initWithApp is called twice tomorrow from elsewhere.

FIROptions:app.options];
}
}
return self;
}
Expand Down
17 changes: 15 additions & 2 deletions FirebaseSegmentation/Sources/Public/FIRSegmentation.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Segmentation)
@interface FIRSegmentation : NSObject

/// Firebase Remote Config service fetch error.
typedef NS_ENUM(NSInteger, FIRSegmentationErrorCode) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has not gone through API review - please add it to the API doc or create a new one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. PTAL in the API review (commented)

/// 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
Expand Down Expand Up @@ -56,8 +69,8 @@ NS_SWIFT_NAME(Segmentation)
/// installation ID.
/// @param completionHandler Set custom installation ID completion. Returns nil if initialization
/// succeeded or an NSError object if initialization failed.
- (void)setCustomInstallationID:(NSString *)customInstallationID
completion:(void (^)(NSError *))completionHandler;
- (void)setCustomInstallationID:(nullable NSString *)customInstallationID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you share this amendment in the API review?

Also, can you add documentation as to what happens when the customInstallationID is nil?

completion:(nullable void (^)(NSError *))completionHandler;

@end

Expand Down
34 changes: 34 additions & 0 deletions FirebaseSegmentation/Sources/SEGContentManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2019 Google
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: 2020 now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below.

//
// 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 "SEGSegmentationConstants.h"

NS_ASSUME_NONNULL_BEGIN

@class FIROptions;

@interface SEGContentManager : NSObject
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some documentation in terms of what this class does? I realize it's internal but looking at the API surface I'm not sure sure what the responsibility is.


/// Shared Singleton Instance
+ (instancetype)sharedInstanceWithOptions:(FIROptions*)options;

- (void)associateCustomInstallationIdentiferNamed:(NSString*)customInstallationID
firebaseApp:(NSString*)appName
completion:(SEGRequestCompletion)completionHandler;

@end

NS_ASSUME_NONNULL_END
131 changes: 131 additions & 0 deletions FirebaseSegmentation/Sources/SEGContentManager.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2019 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#import "SEGContentManager.h"

#import <FirebaseCore/FIRAppInternal.h>
#import <FirebaseInstanceID/FIRInstanceID.h>
#import "FIRSegmentation.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be using fully qualified imports. @paulb777 to confirm

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - we should now use repo-relative imports. See the s.pod_target_xcconfig section at https://github.com/firebase/firebase-ios-sdk/blob/master/AddNewPod.md

#import "SEGDatabaseManager.h"
#import "SEGNetworkManager.h"
#import "SEGSegmentationConstants.h"

NSString *const kErrorDescription = @"ErrorDescription";

@interface SEGContentManager () {
NSMutableDictionary<NSString *, id> *_associationData;
NSString *_instanceIdentifier;
NSString *_instanceIdentifierToken;
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] initWithFIROptions: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;
}

// TODO(dmandar) IID only supports default instance. Modify for FIS.
- (FIRInstanceID *)instanceIDForApp:(NSString *)firebaseApp {
return [FIRInstanceID instanceID];
}

- (void)associateCustomInstallationIdentiferNamed:(NSString *)customInstallationID
firebaseApp:(NSString *)firebaseApp
completion:(SEGRequestCompletion)completionHandler {
// Get the latest instance identifier
if (![self instanceIDForApp:firebaseApp]) {
completionHandler(NO, @{kErrorDescription : @"InstanceID SDK not available"});
}
__weak SEGContentManager *weakSelf = self;
[[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult *_Nullable result,
NSError *_Nullable error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we don't actually check if there was an error here or not - this means it could wipe out all the existing information on a fetch failure. Is that expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. done.

SEGContentManager *strongSelf = weakSelf;
if (!strongSelf) {
completionHandler(NO, @{kErrorDescription : @"Internal Error getting instance ID."});
return;
}

if (!result || error) {
NSString *errorMessage = @"Error getting instance ID.";
if (error) {
errorMessage = [errorMessage stringByAppendingString:error.description];
}
NSDictionary *errorDictionary = @{kErrorDescription : errorMessage};
completionHandler(NO, errorDictionary);
return;
}

strongSelf->_instanceIdentifier = result.instanceID;
strongSelf->_instanceIdentifierToken = result.token;

NSMutableDictionary<NSString *, NSString *> *appAssociationData =
[[NSMutableDictionary alloc] init];
[appAssociationData setObject:customInstallationID forKey:kSEGCustomInstallationIdentifierKey];
[appAssociationData setObject:self->_instanceIdentifier
forKey:kSEGFirebaseInstallationIdentifierKey];
[appAssociationData setObject:kSEGAssociationStatusPending forKey:kSEGAssociationStatusKey];
[strongSelf->_associationData setObject:appAssociationData forKey:firebaseApp];

// 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.
[strongSelf->_databaseManager insertMainTableApplicationNamed:firebaseApp
customInstanceIdentifier:customInstallationID
firebaseInstanceIdentifier:strongSelf->_instanceIdentifier
associationStatus:kSEGAssociationStatusPending
completionHandler:nil];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a comment about why we don't care when the completionHandler is called here? It's unclear to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated comment.


// Send the change up to the backend. Also add the token.

[strongSelf->_networkManager
makeAssociationRequestToBackendWithData:appAssociationData
token:strongSelf->_instanceIdentifierToken
completion:^(BOOL status,
NSDictionary<NSString *, id> *result) {
// TODO...log, update database.

completionHandler(status, result);
}];
}];
}

@end
54 changes: 54 additions & 0 deletions FirebaseSegmentation/Sources/SEGDatabaseManager.h
Original file line number Diff line number Diff line change
@@ -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 "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
Loading