Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Working drop of Segmentation SDK along with test app and unit tests. #4574

Merged
merged 19 commits into from
Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from 12 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 {
dmandar marked this conversation as resolved.
Show resolved Hide resolved
self = [super init];
if (self) {
_appName = appName;
_firebaseAppName = appName;

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

@end
6 changes: 5 additions & 1 deletion 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 Down Expand Up @@ -70,7 +71,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
15 changes: 13 additions & 2 deletions FirebaseSegmentation/Sources/Public/FIRSegmentation.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ 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)

/// Unknown or no error.
dmandar marked this conversation as resolved.
Show resolved Hide resolved
FIRSegmentationErrorCodeInternal = 8001,
/// Error indicating that backend reports an existing association for this custom installation
/// identifier.
FIRSegmentationErrorCodeConflict = 8002,
/// Error indicating that a network error occurred during association.
FIRSegmentationErrorCodeNetwork = 8003,
} 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 +67,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)sharedInstanceWithFIROptions:(FIROptions*)options;
dmandar marked this conversation as resolved.
Show resolved Hide resolved

- (void)associateCustomInstallationIdentiferNamed:(nonnull 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.

Nit: nonnull is not needed since it's wrapped in NS_ASSUME_NONNULL_BEGIN.

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.

firebaseApp:(nonnull NSString*)firebaseApp
Copy link
Member

Choose a reason for hiding this comment

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

This should likely be appName since firebaseApp made me think it was a FIRApp type.

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.

completion:(SEGRequestCompletion)completionHandler;

@end

NS_ASSUME_NONNULL_END
119 changes: 119 additions & 0 deletions FirebaseSegmentation/Sources/SEGContentManager.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// 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"

@interface SEGContentManager () {
NSMutableDictionary<NSString *, id> *_associationData;
NSString *_instanceIdentifier;
NSString *_instanceIdentifierToken;
SEGDatabaseManager *_databaseManager;
SEGNetworkManager *_networkManager;
}
@end

@implementation SEGContentManager

+ (instancetype)sharedInstanceWithFIROptions:(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;

NSAssert(_databaseManager != nil, @"Segmentation database could not be initialized");
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't be using NSAssert here - we can rely on the NONNULL entry point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sg. Done.


// 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, @{@"ErrorDescription" : @"InstanceID SDK not available"});
Copy link
Member

Choose a reason for hiding this comment

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

Nit: can you please define a constant for ErrorDescription?

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.

}
__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, @{@"ErrorDescription" : @"Internal Error getting instance ID."});
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.
[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
51 changes: 51 additions & 0 deletions FirebaseSegmentation/Sources/SEGDatabaseManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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"

/// Persist config data in sqlite database on device. Managing data read/write from/to database.
@interface SEGDatabaseManager : NSObject
/// Shared Singleton Instance
+ (_Nonnull instancetype)sharedInstance;
Copy link
Member

Choose a reason for hiding this comment

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

Please use NS_ASSUME_NONNULL_BEGIN/END instead of explicit nonnull/_Nonnull calls.

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.


/// Open the database.
- (void)createOrOpenDatabaseWithCompletion:(nullable SEGRequestCompletion)completionHandler;
Copy link
Member

Choose a reason for hiding this comment

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

Is there ever a situation where you wouldn't want to know when the database was opened? (nullable completion handler doesn't seem obvious 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.

Probably not. Done.


/// Read all contents of main table.
- (void)loadMainTableWithCompletion:(nullable SEGRequestCompletion)completionHandler;
Copy link
Member

Choose a reason for hiding this comment

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

Same re: nullable completion.

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.


/// 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:(nonnull NSString *)firebaseApplication
customInstanceIdentifier:(nonnull NSString *)customInstanceIdentifier
firebaseInstanceIdentifier:(nonnull NSString *)firebaseInstanceIdentifier
associationStatus:(nonnull NSString *)associationStatus
completionHandler:(nullable SEGRequestCompletion)handler;

/// Clear the record of given namespace and package name
/// before updating the table.//TODO: Add delete.
- (void)deleteRecordFromMainTableWithCustomInstanceIdentifier:
(nonnull NSString *)customInstanceIdentifier;

/// Remove all the records from a config content table.
- (void)deleteAllRecordsFromTable;

@end
Loading