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

[final] fix: subscriber attributes deleted when aliasing #222

Merged
merged 23 commits into from Apr 29, 2020

Conversation

aboedo
Copy link
Member

@aboedo aboedo commented Apr 9, 2020

Fixes a bug where subscriber attributes are deleted when an alias is created.
This was happening because we clear everything in user defaults when creating an alias.

This PR ensures that instead of deleting subscriber attributes, we transfer them over to the new user, if any exist.

I tested this locally against the production server by:

  • Creating subscriber attributes
  • calling create alias
  • checking the shared preferences .plist file to see its contents.
Before After
The key for subscriber attributes was deleted, along with its contents There's a new key for subscriber attributes with the alias, its contents are the same as the ones for the old user

This also means changing the format in which subscriber attributes are stored.

Before:

com.revenuecat.subscriberAttributes.<user_id>: {
    attribute_1:
        value: something,
        synced: true
        ...
}

After:

com.revenuecat.subscriberAttributes: {
    <user_id>:
        attribute_1:
            value: something,
            synced: true
            ...
}

To do:

  • Write unit tests that repro the bug and check for the fix
  • Update existing unit tests to reflect new behavior

@aboedo aboedo added the WIP label Apr 9, 2020
@aboedo aboedo requested review from vegaro and rkotzy April 9, 2020 18:34
@aboedo aboedo self-assigned this Apr 9, 2020
@vegaro
Copy link
Contributor

vegaro commented Apr 9, 2020

Two thoughts:

  • Don't we have the same issue with just an identify (no alias created). It is possible we identify the new user but the attributes for the previous user have not been sent and get deleted.
  • How is transfering different to not using appUserID as part of the user defaults key for the subscriber attributes? Isn't the outcome the same?

@aboedo aboedo force-pushed the fix/subscriber_attributes_deleted_after_aliasing branch from b6cee53 to 05aa509 Compare April 9, 2020 22:57
@aboedo aboedo marked this pull request as draft April 13, 2020 14:43
Comment on lines -155 to -156

// MARK: Subscriber Attributes
Copy link
Member Author

Choose a reason for hiding this comment

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

moved these to a new file

@aboedo aboedo added final and removed WIP labels Apr 20, 2020
@aboedo aboedo changed the title [WIP] fix: subscriber attributes deleted when aliasing [final] fix: subscriber attributes deleted when aliasing Apr 20, 2020
@aboedo
Copy link
Member Author

aboedo commented Apr 20, 2020

@vegaro this is ready for another pass

@aboedo aboedo marked this pull request as ready for review April 21, 2020 14:28
@@ -104,6 +115,10 @@ - (void)setPurchaserInfoCacheTimestampToNow {
self.purchaserInfoCachesLastUpdated = [NSDate date];
}

- (NSString *)purchaserInfoUserDefaultCacheKeyForAppUserID:(NSString *)appUserID {
Copy link
Member Author

Choose a reason for hiding this comment

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

this one and clearOfferingsCache were just moved for organizational purposes

@aboedo
Copy link
Member Author

aboedo commented Apr 21, 2020

found a bug, moving back to WIP

@aboedo aboedo added WIP and removed final labels Apr 21, 2020
@aboedo aboedo marked this pull request as draft April 21, 2020 20:17
[self.offeringsCachedObject clearCache];
}

#pragma mark - subscriber attributes

- (void)storeSubscriberAttribute:(RCSubscriberAttribute *)attribute appUserID:(NSString *)appUserID {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe put appUserID parameter in next line for consistency with the next function

subscriberAttributesForAppUserID[attribute.key] = attribute.asDictionary;
groupedSubscriberAttributes[appUserID] = subscriberAttributesForAppUserID;
[self.userDefaults setObject:groupedSubscriberAttributes
forKey:RCSubscriberAttributesKey];
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you could remove the logic in this fucntion and reuse the other one by doing something like:

Suggested change
forKey:RCSubscriberAttributesKey];
[self.storeSubscriberAttributes:@{attribute.key: attribute} appUserID: appUserID

Copy link
Member Author

Choose a reason for hiding this comment

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

good call

? allSubscriberAttributesByKey.mutableCopy
: [[NSMutableDictionary alloc] init];
NSMutableDictionary *groupedSubscriberAttributes = self.unsyncedAttributesForAllUsersAsDict.mutableCopy;
NSMutableDictionary *subscriberAttributesForAppUserID = ((NSDictionary *)groupedSubscriberAttributes[appUserID] ?: @{})
Copy link
Contributor

Choose a reason for hiding this comment

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

You can reuse subscriberAttributesForAppUserID. Although it would call self. unsyncedAttributesForAllUsersAsDict again

Suggested change
NSMutableDictionary *subscriberAttributesForAppUserID = ((NSDictionary *)groupedSubscriberAttributes[appUserID] ?: @{})
NSMutableDictionary *subscriberAttributesForAppUserID = [self subscriberAttributesForAppUserID].mutableCopy;

Copy link
Member Author

Choose a reason for hiding this comment

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

I kinda wanted to avoid an extra read, if possible

@aboedo aboedo added final and removed WIP labels Apr 21, 2020
@aboedo aboedo marked this pull request as ready for review April 21, 2020 20:46
#pragma MARK - Private methods

- (void)setAttributeWithKey:(NSString *)key value:(nullable NSString *)value appUserID:(NSString *)appUserID {
[self storeAttributeLocallyIfNeededWithKey:key value:value appUserID:appUserID];
}

- (void)syncAttributesWithCompletion:(void (^)(NSError *_Nullable error))completion appUserID:(NSString *)appUserID {

Copy link
Contributor

Choose a reason for hiding this comment

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

extra line

} else {
completion(nil);
}
}

- (void)syncAttributesForAllUsersWithCurrentAppUserID:(NSString *)currentAppUserID {
NSDictionary < NSString * , RCSubscriberAttributeDict > *unsyncedAttributesForAllUsers =
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
NSDictionary < NSString * , RCSubscriberAttributeDict > *unsyncedAttributesForAllUsers =
NSDictionary <NSString *, RCSubscriberAttributeDict> *unsyncedAttributesForAllUsers =

Copy link
Member Author

Choose a reason for hiding this comment

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

good catch, no clue what happened with formatting there

[self.deviceCache deleteAttributesIfSyncedForAppUserID:syncingAppUserID];
RCLog(@"Subscriber attributes synced successfully for appUserID: %@", syncingAppUserID);
} else {
RCErrorLog(@"error when syncing subscriber attributes. Details: %@\n UserInfo:%@",
Copy link
Contributor

Choose a reason for hiding this comment

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

If error is nil but syncingAppUserID == currentAppUserID it will also print this error right?

Copy link
Member Author

Choose a reason for hiding this comment

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

good catch. there was also a problem if where we wouldn't log success if the current app user id was the one being synced. fixed and extracted to a separate method

}
NSMutableDictionary <NSString *, NSDictionary *>
*groupedAttributes = self.storedAttributesForAllUsers.mutableCopy;
[groupedAttributes removeObjectForKey:appUserID];
Copy link
Contributor

Choose a reason for hiding this comment

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

We are removing all attributes for that user id. We can be sure they have been synced right?

Copy link
Member Author

Choose a reason for hiding this comment

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

they should be - we check that numberOfUnsyncedAttributesForAppUserID == 0 a couple of lines before doing this

Copy link
Contributor

Choose a reason for hiding this comment

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

oohhh got it

@aboedo
Copy link
Member Author

aboedo commented Apr 22, 2020

@vegaro ready for another pass

NSMutableDictionary *groupedSubscriberAttributes = self.storedAttributesForAllUsers.mutableCopy;
[groupedSubscriberAttributes removeObjectForKey:appUserID];
[self.userDefaults setObject:groupedSubscriberAttributes forKey:RCSubscriberAttributesKey];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably synchronize this right?

Copy link
Member Author

Choose a reason for hiding this comment

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

yep! great catch

Copy link
Member Author

Choose a reason for hiding this comment

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

}
NSMutableDictionary <NSString *, NSDictionary *>
*groupedAttributes = self.storedAttributesForAllUsers.mutableCopy;
[groupedAttributes removeObjectForKey:appUserID];
Copy link
Contributor

Choose a reason for hiding this comment

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

oohhh got it

Copy link
Contributor

@vegaro vegaro left a comment

Choose a reason for hiding this comment

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

Not sure if this was waiting for another review to get shipped :)

@aboedo aboedo merged commit 2f15db5 into master Apr 29, 2020
@aboedo aboedo deleted the fix/subscriber_attributes_deleted_after_aliasing branch April 29, 2020 23:06
@aboedo aboedo mentioned this pull request May 1, 2020
@aboedo aboedo mentioned this pull request May 13, 2020
@aboedo aboedo mentioned this pull request Jul 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants