From 86914e919c0e847b868ffb848ba5909a90bf2d0a Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 6 Jul 2022 23:05:47 -0400 Subject: [PATCH 01/28] add httpclient that uses shared session for simple requests --- PostHog/Internal/PHGHTTPClient.h | 1 + PostHog/Internal/PHGHTTPClient.m | 44 +++++++++++++++++++++++++++++++ PostHogTests/HTTPClientTest.swift | 30 +++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/PostHog/Internal/PHGHTTPClient.h b/PostHog/Internal/PHGHTTPClient.h index 6b3b60f158..7b57a02a17 100644 --- a/PostHog/Internal/PHGHTTPClient.h +++ b/PostHog/Internal/PHGHTTPClient.h @@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSURLSessionUploadTask *)upload:(JSON_DICT)batch host:(NSURL *_Nonnull)host completionHandler:(void (^)(BOOL retry))completionHandler; +- (NSURLSessionDataTask *)sharedSessionUpload:(NSDictionary *)payload host:(NSURL *)host success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure; @end NS_ASSUME_NONNULL_END diff --git a/PostHog/Internal/PHGHTTPClient.m b/PostHog/Internal/PHGHTTPClient.m index 78bcc073e6..7db1eda5e9 100644 --- a/PostHog/Internal/PHGHTTPClient.m +++ b/PostHog/Internal/PHGHTTPClient.m @@ -104,4 +104,48 @@ - (NSURLSessionUploadTask *)upload:(NSDictionary *)batch host:(NSURL *)host comp [task resume]; return task; } + +- (NSURLSessionDataTask *)sharedSessionUpload:(NSDictionary *)payload host:(NSURL *)host success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure +{ + NSMutableURLRequest *request = self.requestFactory(host); + [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [request setHTTPMethod:@"POST"]; + + NSError *error = nil; + NSException *exception = nil; + NSData *body = nil; + @try { + body = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&error]; + } + @catch (NSException *exc) { + exception = exc; + } + if (error || exception) { + PHGLog(@"Error serializing JSON for batch upload %@", error); + failure(error); // Don't retry this batch. + return nil; + } + + [request setHTTPBody:body]; + + + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *task = [session dataTaskWithRequest:request + completionHandler: + ^(NSData *data, NSURLResponse *response, NSError *error) { + if (error) { + // Network error. Retry. + PHGLog(@"Error uploading request %@.", error); + failure(error); + } else { + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + NSLog(@"%@",json); + success(json); + } + }]; + + [task resume]; + return task; +} @end diff --git a/PostHogTests/HTTPClientTest.swift b/PostHogTests/HTTPClientTest.swift index a7844933e7..dc51952bbe 100644 --- a/PostHogTests/HTTPClientTest.swift +++ b/PostHogTests/HTTPClientTest.swift @@ -26,6 +26,36 @@ class HTTPClientTest: QuickSpec { expect(request.url) == url } } + + describe("sharedSessionUpload") { + it("works") { + let payload: [String: Any] = [ + "token": "some-token" + ] + + _ = stubRequest("POST", "https://app.posthog.test/decide" as LSMatcheable) + .withHeaders( + [ + "Content-Length": "22", + "Content-Type": "application/json" + ])! + .withBody( + "{\"token\":\"some-token\"}" as LSMatcheable) + + var done = false; + let task = client.sharedSessionUpload(payload, host: URL(string:"https://app.posthog.test/decide")!){ responseDict in + expect(responseDict).toNot(beNil()) + done = true + } failure: { error in + NSLog("FAILURE") + expect(error).to(beNil()) + done = true + } + NSLog("nEW") + expect(task).toNot(beNil()) + expect(done).toEventually(beTrue()) + } + } describe("upload") { it("does not ask to retry for json error") { From 9682db71b217f5c7c600600de8aabf9934e36c18 Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 6 Jul 2022 23:13:35 -0400 Subject: [PATCH 02/28] add featureflag files --- PostHog.xcodeproj/project.pbxproj | 8 ++++++++ PostHog/Classes/PHGFeatureFlags.h | 14 ++++++++++++++ PostHog/Classes/PHGFeatureFlags.m | 9 +++++++++ 3 files changed, 31 insertions(+) create mode 100644 PostHog/Classes/PHGFeatureFlags.h create mode 100644 PostHog/Classes/PHGFeatureFlags.m diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index 95d58b74e4..69b28f47c6 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -90,6 +90,8 @@ EADEB8F31DECD335005322DA /* PostHogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E71DECD335005322DA /* PostHogTests.swift */; }; EADEB8F41DECD335005322DA /* ContextTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E81DECD335005322DA /* ContextTest.swift */; }; EADEB8F51DECD335005322DA /* CryptoTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E91DECD335005322DA /* CryptoTest.swift */; }; + F2A54FAA2876856A006C53FF /* PHGFeatureFlags.m in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FA92876856A006C53FF /* PHGFeatureFlags.m */; }; + F2A54FAE2876858E006C53FF /* PHGFeatureFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = F2A54FAD2876858E006C53FF /* PHGFeatureFlags.h */; }; F61EB4AF1F996DEF0038C8C0 /* PostHogUtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61EB4AE1F996DEF0038C8C0 /* PostHogUtilTests.swift */; }; /* End PBXBuildFile section */ @@ -193,6 +195,8 @@ EADEB8FF1DED3711005322DA /* circle.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = circle.yml; sourceTree = ""; }; EADEB9001DED3711005322DA /* PostHog.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = PostHog.podspec; sourceTree = ""; }; EADEB9011DED3711005322DA /* Podfile.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile.lock; sourceTree = ""; }; + F2A54FA92876856A006C53FF /* PHGFeatureFlags.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PHGFeatureFlags.m; sourceTree = ""; }; + F2A54FAD2876858E006C53FF /* PHGFeatureFlags.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PHGFeatureFlags.h; sourceTree = ""; }; F61EB4AE1F996DEF0038C8C0 /* PostHogUtilTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostHogUtilTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -366,6 +370,8 @@ BF8C8BD43F67239FA1902206 /* PHGContext.h */, BF8C81A4093856C696EF943D /* PHGMiddleware.h */, BF8C89BCDF12D1B64E9475F1 /* PHGCrypto.h */, + F2A54FA92876856A006C53FF /* PHGFeatureFlags.m */, + F2A54FAD2876858E006C53FF /* PHGFeatureFlags.h */, ); path = Classes; sourceTree = ""; @@ -412,6 +418,7 @@ BF8C8AD1FBFBB6941DE29D3B /* PHGFileStorage.h in Headers */, BF8C845A65AB5AD06E1EE26B /* PHGStorage.h in Headers */, BF8C890FADEC88022E7A3588 /* NSData+PHGGZIP.h in Headers */, + F2A54FAE2876858E006C53FF /* PHGFeatureFlags.h in Headers */, BF8C8181F96E1C8CB86D783D /* PHGPostHogUtils.h in Headers */, BF8C8FCEC80B47BF2236236E /* PHGUtils.h in Headers */, BF8C8DCC18E3AA39CCB330BC /* PHGStoreKitCapturer.h in Headers */, @@ -687,6 +694,7 @@ EADEB8DF1DECD12B005322DA /* PHGPostHogConfiguration.m in Sources */, EAA5427E1EB42B8C00945DA7 /* PHGReachability.m in Sources */, BF8C8C9C0FD46725EA63890A /* ObjC.m in Sources */, + F2A54FAA2876856A006C53FF /* PHGFeatureFlags.m in Sources */, BF8C8C32C210AC2B093F1042 /* CHANGELOG.md in Sources */, BF8C8AF845410CCF3744EEA1 /* PHGAES256Crypto.m in Sources */, BF8C8E57BD722DB6AEFF6835 /* PHGFileStorage.m in Sources */, diff --git a/PostHog/Classes/PHGFeatureFlags.h b/PostHog/Classes/PHGFeatureFlags.h new file mode 100644 index 0000000000..f9e7063749 --- /dev/null +++ b/PostHog/Classes/PHGFeatureFlags.h @@ -0,0 +1,14 @@ +#import + +@class PHGPostHog; + + +@interface PHGPayloadManager : NSObject + +- (instancetype _Nonnull)initWithPostHog:(PHGPostHog *_Nonnull)posthog; + +- (NSArray *)isFeatureEnabled:(NSString *)flagKey; + +- (void)reloadFeatureFlags; + +@end diff --git a/PostHog/Classes/PHGFeatureFlags.m b/PostHog/Classes/PHGFeatureFlags.m new file mode 100644 index 0000000000..03ef886a8b --- /dev/null +++ b/PostHog/Classes/PHGFeatureFlags.m @@ -0,0 +1,9 @@ +// +// PHGFeatureFlags.m +// PostHog +// +// Created by Eric Duong on 7/6/22. +// Copyright © 2022 PostHog. All rights reserved. +// + +#import From cd6a2643350434d53ee6835e46a17cb0389cf746 Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 6 Jul 2022 23:14:13 -0400 Subject: [PATCH 03/28] change class name --- PostHog/Classes/PHGFeatureFlags.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PostHog/Classes/PHGFeatureFlags.h b/PostHog/Classes/PHGFeatureFlags.h index f9e7063749..13d7ab8541 100644 --- a/PostHog/Classes/PHGFeatureFlags.h +++ b/PostHog/Classes/PHGFeatureFlags.h @@ -3,7 +3,7 @@ @class PHGPostHog; -@interface PHGPayloadManager : NSObject +@interface PHGFeatureFlags : NSObject - (instancetype _Nonnull)initWithPostHog:(PHGPostHog *_Nonnull)posthog; From 3dd97fb8908650f5733bb5df6775eb09c7285660 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 11:00:00 -0400 Subject: [PATCH 04/28] implement funcs --- PostHog.xcodeproj/project.pbxproj | 8 ---- PostHog/Classes/PHGContext.h | 3 ++ PostHog/Classes/PHGFeatureFlags.h | 14 ------ PostHog/Classes/PHGFeatureFlags.m | 9 ---- PostHog/Classes/PHGPayloadManager.h | 2 + PostHog/Classes/PHGPayloadManager.m | 55 ++++++++++++++++++++++++ PostHog/Classes/PHGPostHog.h | 3 ++ PostHog/Classes/PHGPostHog.m | 14 ++++++ PostHog/Internal/PHGPostHogIntegration.h | 2 + PostHog/Internal/PHGPostHogIntegration.m | 1 - 10 files changed, 79 insertions(+), 32 deletions(-) delete mode 100644 PostHog/Classes/PHGFeatureFlags.h delete mode 100644 PostHog/Classes/PHGFeatureFlags.m diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index 69b28f47c6..95d58b74e4 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -90,8 +90,6 @@ EADEB8F31DECD335005322DA /* PostHogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E71DECD335005322DA /* PostHogTests.swift */; }; EADEB8F41DECD335005322DA /* ContextTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E81DECD335005322DA /* ContextTest.swift */; }; EADEB8F51DECD335005322DA /* CryptoTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E91DECD335005322DA /* CryptoTest.swift */; }; - F2A54FAA2876856A006C53FF /* PHGFeatureFlags.m in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FA92876856A006C53FF /* PHGFeatureFlags.m */; }; - F2A54FAE2876858E006C53FF /* PHGFeatureFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = F2A54FAD2876858E006C53FF /* PHGFeatureFlags.h */; }; F61EB4AF1F996DEF0038C8C0 /* PostHogUtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61EB4AE1F996DEF0038C8C0 /* PostHogUtilTests.swift */; }; /* End PBXBuildFile section */ @@ -195,8 +193,6 @@ EADEB8FF1DED3711005322DA /* circle.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = circle.yml; sourceTree = ""; }; EADEB9001DED3711005322DA /* PostHog.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = PostHog.podspec; sourceTree = ""; }; EADEB9011DED3711005322DA /* Podfile.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile.lock; sourceTree = ""; }; - F2A54FA92876856A006C53FF /* PHGFeatureFlags.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PHGFeatureFlags.m; sourceTree = ""; }; - F2A54FAD2876858E006C53FF /* PHGFeatureFlags.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PHGFeatureFlags.h; sourceTree = ""; }; F61EB4AE1F996DEF0038C8C0 /* PostHogUtilTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostHogUtilTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -370,8 +366,6 @@ BF8C8BD43F67239FA1902206 /* PHGContext.h */, BF8C81A4093856C696EF943D /* PHGMiddleware.h */, BF8C89BCDF12D1B64E9475F1 /* PHGCrypto.h */, - F2A54FA92876856A006C53FF /* PHGFeatureFlags.m */, - F2A54FAD2876858E006C53FF /* PHGFeatureFlags.h */, ); path = Classes; sourceTree = ""; @@ -418,7 +412,6 @@ BF8C8AD1FBFBB6941DE29D3B /* PHGFileStorage.h in Headers */, BF8C845A65AB5AD06E1EE26B /* PHGStorage.h in Headers */, BF8C890FADEC88022E7A3588 /* NSData+PHGGZIP.h in Headers */, - F2A54FAE2876858E006C53FF /* PHGFeatureFlags.h in Headers */, BF8C8181F96E1C8CB86D783D /* PHGPostHogUtils.h in Headers */, BF8C8FCEC80B47BF2236236E /* PHGUtils.h in Headers */, BF8C8DCC18E3AA39CCB330BC /* PHGStoreKitCapturer.h in Headers */, @@ -694,7 +687,6 @@ EADEB8DF1DECD12B005322DA /* PHGPostHogConfiguration.m in Sources */, EAA5427E1EB42B8C00945DA7 /* PHGReachability.m in Sources */, BF8C8C9C0FD46725EA63890A /* ObjC.m in Sources */, - F2A54FAA2876856A006C53FF /* PHGFeatureFlags.m in Sources */, BF8C8C32C210AC2B093F1042 /* CHANGELOG.md in Sources */, BF8C8AF845410CCF3744EEA1 /* PHGAES256Crypto.m in Sources */, BF8C8E57BD722DB6AEFF6835 /* PHGFileStorage.m in Sources */, diff --git a/PostHog/Classes/PHGContext.h b/PostHog/Classes/PHGContext.h index d6bb135bba..7ae0d7985a 100644 --- a/PostHog/Classes/PHGContext.h +++ b/PostHog/Classes/PHGContext.h @@ -12,6 +12,9 @@ typedef NS_ENUM(NSInteger, PHGEventType) { // General utility PHGEventTypeReset, PHGEventTypeFlush, + + // FeatureFlags Methods + PHGReloadFeatureFlags, // Remote Notification PHGEventTypeReceivedRemoteNotification, diff --git a/PostHog/Classes/PHGFeatureFlags.h b/PostHog/Classes/PHGFeatureFlags.h deleted file mode 100644 index 13d7ab8541..0000000000 --- a/PostHog/Classes/PHGFeatureFlags.h +++ /dev/null @@ -1,14 +0,0 @@ -#import - -@class PHGPostHog; - - -@interface PHGFeatureFlags : NSObject - -- (instancetype _Nonnull)initWithPostHog:(PHGPostHog *_Nonnull)posthog; - -- (NSArray *)isFeatureEnabled:(NSString *)flagKey; - -- (void)reloadFeatureFlags; - -@end diff --git a/PostHog/Classes/PHGFeatureFlags.m b/PostHog/Classes/PHGFeatureFlags.m deleted file mode 100644 index 03ef886a8b..0000000000 --- a/PostHog/Classes/PHGFeatureFlags.m +++ /dev/null @@ -1,9 +0,0 @@ -// -// PHGFeatureFlags.m -// PostHog -// -// Created by Eric Duong on 7/6/22. -// Copyright © 2022 PostHog. All rights reserved. -// - -#import diff --git a/PostHog/Classes/PHGPayloadManager.h b/PostHog/Classes/PHGPayloadManager.h index fab80fe164..df77ea0d0c 100644 --- a/PostHog/Classes/PHGPayloadManager.h +++ b/PostHog/Classes/PHGPayloadManager.h @@ -13,6 +13,8 @@ extern NSString *_Nonnull PHGPostHogIntegrationDidStart; - (instancetype _Nonnull)initWithPostHog:(PHGPostHog *_Nonnull)posthog; +- (NSArray *_Nonnull)getFeatureFlags; + // @Deprecated - Exposing for backward API compat reasons only - (NSString *_Nonnull)getAnonymousId; diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index eb9f39b9b8..93f89548c0 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -13,6 +13,13 @@ static NSString *const PHGAnonymousIdKey = @"PHGAnonymousId"; static NSString *const kPHGAnonymousIdFilename = @"posthog.anonymousId"; +static NSString *const PHGActiveFeatureFlags = @"PHGActiveFeatureFlags"; +static NSString *const kPHGActiveFeatureFlags = @"posthog.activeFeatureFlags"; + +static NSString *const PHGEnabledFeatureFlags = @"PHGEnabledFeatureFlags"; +static NSString *const kPHGEnabledFeatureFlags = @"posthog.enabledFeatureFlags"; + + @interface PHGPayloadManager () @@ -157,6 +164,51 @@ - (void)alias:(NSString *)alias options:(NSDictionary *)options options:options]; } +#pragma mark - Feature Flags + +- (void)receivedFeatureFlags:(NSDictionary *)flags +{ + NSArray* keys = [flags allKeys]; + +#if TARGET_OS_TV + [self.userDefaultsStorage setArray:keys forKey:PHGActiveFeatureFlags]; +#else + [self.fileStorage setArray:keys forKey:kPHGActiveFeatureFlags]; +#endif + +#if TARGET_OS_TV + [self.userDefaultsStorage setString:anonymousId forKey:PHGEnabledFeatureFlags]; +#else + [self.fileStorage setDictionary:flags forKey:kPHGEnabledFeatureFlags]; +#endif +} + +- (void)reloadFeatureFlags +{ + NSMutableDictionary *payload = [[NSMutableDictionary alloc] init]; + [payload setObject:[self.posthog getAnonymousId] forKey:@"$anon_distinct_id"]; + [payload setObject:self.integration.distinctId forKey:@"distinct_id"]; + [payload setObject:self.posthog.configuration.apiKey forKey:@"api_key"]; + + NSURL *url = [self.posthog.configuration.host URLByAppendingPathComponent:@"decide/?v=2"]; + + [self.httpClient sharedSessionUpload:payload host:url success:^(NSDictionary * _Nonnull responseDict) { + [self receivedFeatureFlags:responseDict]; + } failure:^(NSError * _Nonnull error) { + + }]; +} + +- (NSArray *)getFeatureFlags +{ +#if TARGET_OS_TV + NSArray *keys = [self.userDefaultsStorage arrayForKey:PHGActiveFeatureFlags]; +#else + NSArray *keys = [self.fileStorage arrayForKey:kPHGActiveFeatureFlags]; +#endif + return keys; +} + - (void)receivedRemoteNotification:(NSDictionary *)userInfo { [self callWithSelector:_cmd arguments:@[ userInfo ] options:nil]; @@ -387,6 +439,9 @@ - (void)context:(PHGContext *)context next:(void (^_Nonnull)(PHGContext *_Nullab [self openURL:payload.url options:payload.options]; break; } + case PHGReloadFeatureFlags: + [self reloadFeatureFlags]; + break; case PHGEventTypeUndefined: NSAssert(NO, @"Received context with undefined event type %@", context); NSLog(@"[ERROR]: Received context with undefined event type %@", context); diff --git a/PostHog/Classes/PHGPostHog.h b/PostHog/Classes/PHGPostHog.h index dc9d12b5a8..12e3ef9242 100644 --- a/PostHog/Classes/PHGPostHog.h +++ b/PostHog/Classes/PHGPostHog.h @@ -127,6 +127,9 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)alias:(NSString *)alias; +- (bool)isFeatureEnabled:(NSString *)flagKey; +- (void)reloadFeatureFlags; + // todo: docs - (void)receivedRemoteNotification:(NSDictionary *)userInfo; - (void)failedToRegisterForRemoteNotificationsWithError:(NSError *)error; diff --git a/PostHog/Classes/PHGPostHog.m b/PostHog/Classes/PHGPostHog.m index 3dc5137dee..a85e9e1091 100644 --- a/PostHog/Classes/PHGPostHog.m +++ b/PostHog/Classes/PHGPostHog.m @@ -84,6 +84,8 @@ - (instancetype)initWithConfiguration:(PHGPostHogConfiguration *)configuration } } #endif + + [self reloadFeatureFlags]; } return self; } @@ -254,6 +256,18 @@ - (void)alias:(NSString *)alias [[PHGAliasPayload alloc] initWithAlias:alias]]; } +- (bool)isFeatureEnabled:(NSString *)flagKey +{ + NSArray *keys = [self.payloadManager getFeatureFlags]; + BOOL isFlagEnabled = [keys containsObject: flagKey]; + return isFlagEnabled; +} + +- (void)reloadFeatureFlags +{ + [self run:PHGReloadFeatureFlags payload:nil]; +} + - (void)capturePushNotification:(NSDictionary *)properties fromLaunch:(BOOL)launch { if (launch) { diff --git a/PostHog/Internal/PHGPostHogIntegration.h b/PostHog/Internal/PHGPostHogIntegration.h index 2a1364f710..bbd2a33aeb 100644 --- a/PostHog/Internal/PHGPostHogIntegration.h +++ b/PostHog/Internal/PHGPostHogIntegration.h @@ -17,6 +17,8 @@ extern NSString *const PHGPostHogRequestDidFailNotification; @interface PHGPostHogIntegration : NSObject +@property (nonatomic, copy) NSString *distinctId; + - (id)initWithPostHog:(PHGPostHog *)posthog httpClient:(PHGHTTPClient *)httpClient fileStorage:(id)fileStorage userDefaultsStorage:(id)userDefaultsStorage; - (NSDictionary *)staticContext; - (NSDictionary *)liveContext; diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index 059dd489aa..aaeb5da80d 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -53,7 +53,6 @@ @interface PHGPostHogIntegration () @property (nonatomic, assign) PHGPostHog *posthog; @property (nonatomic, assign) PHGPostHogConfiguration *configuration; @property (nonatomic, copy) NSString *referrer; -@property (nonatomic, copy) NSString *distinctId; @property (nonatomic, strong) PHGHTTPClient *httpClient; @property (nonatomic, strong) id fileStorage; @property (nonatomic, strong) id userDefaultsStorage; From f4314eaa185b77d196b95b764adf2fe520d60d95 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 11:12:35 -0400 Subject: [PATCH 05/28] correct type --- PostHog/Classes/PHGContext.h | 2 +- PostHog/Classes/PHGPayloadManager.m | 2 +- PostHog/Classes/PHGPostHog.m | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/PostHog/Classes/PHGContext.h b/PostHog/Classes/PHGContext.h index 7ae0d7985a..9891d1ee36 100644 --- a/PostHog/Classes/PHGContext.h +++ b/PostHog/Classes/PHGContext.h @@ -14,7 +14,7 @@ typedef NS_ENUM(NSInteger, PHGEventType) { PHGEventTypeFlush, // FeatureFlags Methods - PHGReloadFeatureFlags, + PHGEventTypeReloadFeatureFlags, // Remote Notification PHGEventTypeReceivedRemoteNotification, diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index 93f89548c0..d2fc052ba6 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -439,7 +439,7 @@ - (void)context:(PHGContext *)context next:(void (^_Nonnull)(PHGContext *_Nullab [self openURL:payload.url options:payload.options]; break; } - case PHGReloadFeatureFlags: + case PHGEventTypeReloadFeatureFlags: [self reloadFeatureFlags]; break; case PHGEventTypeUndefined: diff --git a/PostHog/Classes/PHGPostHog.m b/PostHog/Classes/PHGPostHog.m index a85e9e1091..206d6fff49 100644 --- a/PostHog/Classes/PHGPostHog.m +++ b/PostHog/Classes/PHGPostHog.m @@ -84,8 +84,7 @@ - (instancetype)initWithConfiguration:(PHGPostHogConfiguration *)configuration } } #endif - - [self reloadFeatureFlags]; + } return self; } @@ -265,7 +264,7 @@ - (bool)isFeatureEnabled:(NSString *)flagKey - (void)reloadFeatureFlags { - [self run:PHGReloadFeatureFlags payload:nil]; + [self run:PHGEventTypeReloadFeatureFlags payload:nil]; } - (void)capturePushNotification:(NSDictionary *)properties fromLaunch:(BOOL)launch From 6eeb678679614f243a14120493b4d76e7775db98 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 12:31:25 -0400 Subject: [PATCH 06/28] working reloadfeatureflags and isfeatureenabled with tests --- PostHog.xcodeproj/project.pbxproj | 6 +++++ PostHog/Classes/PHGPayloadManager.m | 7 +++--- PostHog/Internal/PHGHTTPClient.m | 2 +- PostHogTests/FeatureFlagTests.swift | 38 +++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 PostHogTests/FeatureFlagTests.swift diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index 95d58b74e4..e21fbcd5d5 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -90,6 +90,8 @@ EADEB8F31DECD335005322DA /* PostHogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E71DECD335005322DA /* PostHogTests.swift */; }; EADEB8F41DECD335005322DA /* ContextTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E81DECD335005322DA /* ContextTest.swift */; }; EADEB8F51DECD335005322DA /* CryptoTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E91DECD335005322DA /* CryptoTest.swift */; }; + F2A54FB028772F97006C53FF /* FeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */; }; + F2A54FB128772F97006C53FF /* FeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */; }; F61EB4AF1F996DEF0038C8C0 /* PostHogUtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61EB4AE1F996DEF0038C8C0 /* PostHogUtilTests.swift */; }; /* End PBXBuildFile section */ @@ -193,6 +195,7 @@ EADEB8FF1DED3711005322DA /* circle.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = circle.yml; sourceTree = ""; }; EADEB9001DED3711005322DA /* PostHog.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = PostHog.podspec; sourceTree = ""; }; EADEB9011DED3711005322DA /* Podfile.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile.lock; sourceTree = ""; }; + F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagTests.swift; sourceTree = ""; }; F61EB4AE1F996DEF0038C8C0 /* PostHogUtilTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostHogUtilTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -333,6 +336,7 @@ EADEB8EC1DECD335005322DA /* PostHogTests-Bridging-Header.h */, 6EEC1C702017EA370089C478 /* EndToEndTests.swift */, A31958EE2385AC3A00A47EFA /* SerializationTests.m */, + F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */, ); indentWidth = 2; path = PostHogTests; @@ -667,6 +671,7 @@ 9D8CE59223EE014E00197D0C /* CapturingTests.swift in Sources */, 9D8CE59323EE014E00197D0C /* ContextTest.swift in Sources */, 9D8CE59423EE014E00197D0C /* SerializationTests.m in Sources */, + F2A54FB128772F97006C53FF /* FeatureFlagTests.swift in Sources */, 9D8CE59523EE014E00197D0C /* HTTPClientTest.swift in Sources */, 9D8CE59623EE014E00197D0C /* PostHogTests.swift in Sources */, 9D8CE59723EE014E00197D0C /* FileStorageTest.swift in Sources */, @@ -718,6 +723,7 @@ EAA542771EB4035400945DA7 /* CapturingTests.swift in Sources */, EADEB8F41DECD335005322DA /* ContextTest.swift in Sources */, A31958EF2385AC3A00A47EFA /* SerializationTests.m in Sources */, + F2A54FB028772F97006C53FF /* FeatureFlagTests.swift in Sources */, EADEB8EE1DECD335005322DA /* HTTPClientTest.swift in Sources */, EADEB8F31DECD335005322DA /* PostHogTests.swift in Sources */, EADEB8F11DECD335005322DA /* FileStorageTest.swift in Sources */, diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index d2fc052ba6..adeaa87839 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -186,11 +186,12 @@ - (void)receivedFeatureFlags:(NSDictionary *)flags - (void)reloadFeatureFlags { NSMutableDictionary *payload = [[NSMutableDictionary alloc] init]; - [payload setObject:[self.posthog getAnonymousId] forKey:@"$anon_distinct_id"]; - [payload setObject:self.integration.distinctId forKey:@"distinct_id"]; +// [payload setObject:[self.posthog getAnonymousId] forKey:@"$anon_distinct_id"]; +// TODO: add groups and determine how distinct_id can be populated +// [payload setObject:self.integration.distinctId forKey:@"distinct_id"]; [payload setObject:self.posthog.configuration.apiKey forKey:@"api_key"]; - NSURL *url = [self.posthog.configuration.host URLByAppendingPathComponent:@"decide/?v=2"]; + NSURL *url = [self.posthog.configuration.host URLByAppendingPathComponent:@"decide/\?v=2"]; [self.httpClient sharedSessionUpload:payload host:url success:^(NSDictionary * _Nonnull responseDict) { [self receivedFeatureFlags:responseDict]; diff --git a/PostHog/Internal/PHGHTTPClient.m b/PostHog/Internal/PHGHTTPClient.m index 7db1eda5e9..a11d37ede5 100644 --- a/PostHog/Internal/PHGHTTPClient.m +++ b/PostHog/Internal/PHGHTTPClient.m @@ -133,7 +133,7 @@ - (NSURLSessionDataTask *)sharedSessionUpload:(NSDictionary *)payload host:(NSUR NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler: - ^(NSData *data, NSURLResponse *response, NSError *error) { + ^(NSData *data, NSURLResponse *response, NSError *error) {g if (error) { // Network error. Retry. PHGLog(@"Error uploading request %@.", error); diff --git a/PostHogTests/FeatureFlagTests.swift b/PostHogTests/FeatureFlagTests.swift new file mode 100644 index 0000000000..9d0bd823a4 --- /dev/null +++ b/PostHogTests/FeatureFlagTests.swift @@ -0,0 +1,38 @@ +import Quick +import Nimble +import Nocilla +import PostHog + +class FeatureFlagTests: QuickSpec { + override func spec() { + var posthog: PHGPostHog! + + beforeEach { + LSNocilla.sharedInstance().start() + let config = PHGPostHogConfiguration(apiKey: "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE", host: "https://app.posthog.test") + posthog = PHGPostHog(configuration: config) + } + + afterEach { + posthog.reset() + LSNocilla.sharedInstance().clearStubs() + LSNocilla.sharedInstance().stop() + } + + it("checks flag is enabled") { + _ = stubRequest("POST", "https://app.posthog.test/decide/%3Fv=2" as LSMatcheable) + .withHeaders([ "Content-Length": "46", "Content-Type": "application/json" ])? + .withBody("{\"api_key\":\"QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE\"}" as LSMatcheable)? + .andReturn(200)? + .withHeaders(["Content-Type": "application/json"])? + .withBody("{\"some-flag\":\"true\"}" as LSHTTPBody); + posthog.reloadFeatureFlags() + // Hacky: Need to buffer for async request to happen without stub being cleaned up + sleep(1) + let isEnabled = posthog.isFeatureEnabled("some-flag") + expect(isEnabled).to(beTrue()) + } + + } + +} From d2b7e3a7eb5e9352fcef556543f5a31fa5491a1f Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 12:42:03 -0400 Subject: [PATCH 07/28] remove bad typo --- PostHog/Internal/PHGHTTPClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PostHog/Internal/PHGHTTPClient.m b/PostHog/Internal/PHGHTTPClient.m index a11d37ede5..7db1eda5e9 100644 --- a/PostHog/Internal/PHGHTTPClient.m +++ b/PostHog/Internal/PHGHTTPClient.m @@ -133,7 +133,7 @@ - (NSURLSessionDataTask *)sharedSessionUpload:(NSDictionary *)payload host:(NSUR NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler: - ^(NSData *data, NSURLResponse *response, NSError *error) {g + ^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { // Network error. Retry. PHGLog(@"Error uploading request %@.", error); From 889c8417b727d697c257924db1cf7217146a4ff5 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 14:01:31 -0400 Subject: [PATCH 08/28] fix url --- PostHog/Classes/PHGPayloadManager.m | 12 ++++++++---- PostHogTests/FeatureFlagTests.swift | 5 +---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index adeaa87839..63f366bdb8 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -186,12 +186,16 @@ - (void)receivedFeatureFlags:(NSDictionary *)flags - (void)reloadFeatureFlags { NSMutableDictionary *payload = [[NSMutableDictionary alloc] init]; -// [payload setObject:[self.posthog getAnonymousId] forKey:@"$anon_distinct_id"]; -// TODO: add groups and determine how distinct_id can be populated -// [payload setObject:self.integration.distinctId forKey:@"distinct_id"]; + [payload setObject:[self getAnonymousId] forKey:@"$anon_distinct_id"]; +// TODO: add groups + [payload setObject:[self getAnonymousId] forKey:@"distinct_id"]; [payload setObject:self.posthog.configuration.apiKey forKey:@"api_key"]; - NSURL *url = [self.posthog.configuration.host URLByAppendingPathComponent:@"decide/\?v=2"]; + NSURL *url = [self.posthog.configuration.host URLByAppendingPathComponent:@"decide"]; + NSString *absoluteUrl = [url absoluteString]; + absoluteUrl = [absoluteUrl stringByAppendingString:@"/?v=2"]; + url = [NSURL URLWithString:absoluteUrl]; + [self.httpClient sharedSessionUpload:payload host:url success:^(NSDictionary * _Nonnull responseDict) { [self receivedFeatureFlags:responseDict]; diff --git a/PostHogTests/FeatureFlagTests.swift b/PostHogTests/FeatureFlagTests.swift index 9d0bd823a4..f1dce11144 100644 --- a/PostHogTests/FeatureFlagTests.swift +++ b/PostHogTests/FeatureFlagTests.swift @@ -20,11 +20,8 @@ class FeatureFlagTests: QuickSpec { } it("checks flag is enabled") { - _ = stubRequest("POST", "https://app.posthog.test/decide/%3Fv=2" as LSMatcheable) - .withHeaders([ "Content-Length": "46", "Content-Type": "application/json" ])? - .withBody("{\"api_key\":\"QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE\"}" as LSMatcheable)? + _ = stubRequest("POST", "https://app.posthog.test/decide/?v=2" as LSMatcheable) .andReturn(200)? - .withHeaders(["Content-Type": "application/json"])? .withBody("{\"some-flag\":\"true\"}" as LSHTTPBody); posthog.reloadFeatureFlags() // Hacky: Need to buffer for async request to happen without stub being cleaned up From fa1a71faa74441b39fde595a9507e2798947ddd4 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 14:25:07 -0400 Subject: [PATCH 09/28] add basic group api --- PostHog.xcodeproj/project.pbxproj | 8 ++++++++ PostHog/Classes/PHGContext.h | 1 + PostHog/Classes/PHGGroupPayload.h | 21 +++++++++++++++++++++ PostHog/Classes/PHGGroupPayload.m | 18 ++++++++++++++++++ PostHog/Classes/PHGIntegration.h | 4 ++++ PostHog/Classes/PHGPayloadManager.m | 17 +++++++++++++++++ PostHog/Classes/PHGPostHog.h | 2 ++ PostHog/Classes/PHGPostHog.m | 8 ++++++++ PostHog/Internal/PHGPostHogIntegration.h | 1 + PostHog/Internal/PHGPostHogIntegration.m | 14 ++++++++++++++ PostHogTests/CapturingTests.swift | 10 ++++++++++ 11 files changed, 104 insertions(+) create mode 100644 PostHog/Classes/PHGGroupPayload.h create mode 100644 PostHog/Classes/PHGGroupPayload.m diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index e21fbcd5d5..17ddcc8b89 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -92,6 +92,8 @@ EADEB8F51DECD335005322DA /* CryptoTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E91DECD335005322DA /* CryptoTest.swift */; }; F2A54FB028772F97006C53FF /* FeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */; }; F2A54FB128772F97006C53FF /* FeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */; }; + F2A54FB32877582E006C53FF /* PHGGroupPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FB22877582E006C53FF /* PHGGroupPayload.m */; }; + F2A54FB52877583C006C53FF /* PHGGroupPayload.h in Headers */ = {isa = PBXBuildFile; fileRef = F2A54FB42877583C006C53FF /* PHGGroupPayload.h */; settings = {ATTRIBUTES = (Public, ); }; }; F61EB4AF1F996DEF0038C8C0 /* PostHogUtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61EB4AE1F996DEF0038C8C0 /* PostHogUtilTests.swift */; }; /* End PBXBuildFile section */ @@ -196,6 +198,8 @@ EADEB9001DED3711005322DA /* PostHog.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = PostHog.podspec; sourceTree = ""; }; EADEB9011DED3711005322DA /* Podfile.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile.lock; sourceTree = ""; }; F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagTests.swift; sourceTree = ""; }; + F2A54FB22877582E006C53FF /* PHGGroupPayload.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PHGGroupPayload.m; sourceTree = ""; }; + F2A54FB42877583C006C53FF /* PHGGroupPayload.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PHGGroupPayload.h; sourceTree = ""; }; F61EB4AE1F996DEF0038C8C0 /* PostHogUtilTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostHogUtilTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -370,6 +374,8 @@ BF8C8BD43F67239FA1902206 /* PHGContext.h */, BF8C81A4093856C696EF943D /* PHGMiddleware.h */, BF8C89BCDF12D1B64E9475F1 /* PHGCrypto.h */, + F2A54FB22877582E006C53FF /* PHGGroupPayload.m */, + F2A54FB42877583C006C53FF /* PHGGroupPayload.h */, ); path = Classes; sourceTree = ""; @@ -413,6 +419,7 @@ BF8C8DC55C0552CCAFE7BEBF /* PHGMacros.h in Headers */, BF8C84098D3C47C6F7B7FF9A /* PHGHTTPClient.h in Headers */, BF8C817D68B4E9B441E21D64 /* UIViewController+PHGScreen.h in Headers */, + F2A54FB52877583C006C53FF /* PHGGroupPayload.h in Headers */, BF8C8AD1FBFBB6941DE29D3B /* PHGFileStorage.h in Headers */, BF8C845A65AB5AD06E1EE26B /* PHGStorage.h in Headers */, BF8C890FADEC88022E7A3588 /* NSData+PHGGZIP.h in Headers */, @@ -698,6 +705,7 @@ BF8C856B25377945554598DF /* UIViewController+PHGScreen.m in Sources */, BF8C8EF6349359A33E8C6DFD /* PHGUtils.m in Sources */, BF8C84CDF20A0959FC24E076 /* PHGPostHogUtils.m in Sources */, + F2A54FB32877582E006C53FF /* PHGGroupPayload.m in Sources */, BF8C8E28834B124F2E9835A5 /* PHGUserDefaultsStorage.m in Sources */, BF8C8FC2CB4FAE70F5879308 /* PHGStoreKitCapturer.m in Sources */, BF8C8D75F1E684EA93578DF5 /* NSData+PHGGZIP.m in Sources */, diff --git a/PostHog/Classes/PHGContext.h b/PostHog/Classes/PHGContext.h index 9891d1ee36..2c2c2b1603 100644 --- a/PostHog/Classes/PHGContext.h +++ b/PostHog/Classes/PHGContext.h @@ -8,6 +8,7 @@ typedef NS_ENUM(NSInteger, PHGEventType) { PHGEventTypeCapture, PHGEventTypeScreen, PHGEventTypeAlias, + PHGEventTypeGroup, // General utility PHGEventTypeReset, diff --git a/PostHog/Classes/PHGGroupPayload.h b/PostHog/Classes/PHGGroupPayload.h new file mode 100644 index 0000000000..96177c2c2e --- /dev/null +++ b/PostHog/Classes/PHGGroupPayload.h @@ -0,0 +1,21 @@ +#import +#import "PHGPayload.h" + +NS_ASSUME_NONNULL_BEGIN + + +@interface PHGGroupPayload : PHGPayload + +@property (nonatomic, readonly) NSString *groupType; + +@property (nonatomic, readonly) NSString *groupKey; + +@property (nonatomic, readonly, nullable) NSDictionary *properties; + +- (instancetype)initWithType:(NSString *_Nonnull)groupType + groupKey:(NSString *_Nonnull)groupKey + properties:(NSDictionary *_Nullable)properties; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PostHog/Classes/PHGGroupPayload.m b/PostHog/Classes/PHGGroupPayload.m new file mode 100644 index 0000000000..a7c9e34885 --- /dev/null +++ b/PostHog/Classes/PHGGroupPayload.m @@ -0,0 +1,18 @@ +#import "PHGGroupPayload.h" + + +@implementation PHGGroupPayload + +- (instancetype)initWithType:(NSString *_Nonnull)groupType + groupKey:(NSString *_Nonnull)groupKey + properties:(NSDictionary *_Nullable)properties; +{ + if (self = [super init]) { + _groupType = [groupType copy]; + _groupKey = [groupKey copy]; + _properties = [properties copy]; + } + return self; +} + +@end diff --git a/PostHog/Classes/PHGIntegration.h b/PostHog/Classes/PHGIntegration.h index 8bfdacf720..ff7aec49a9 100644 --- a/PostHog/Classes/PHGIntegration.h +++ b/PostHog/Classes/PHGIntegration.h @@ -3,6 +3,8 @@ #import "PHGCapturePayload.h" #import "PHGScreenPayload.h" #import "PHGAliasPayload.h" +#import "PHGGroupPayload.h" + #import "PHGIdentifyPayload.h" #import "PHGContext.h" @@ -34,6 +36,8 @@ NS_ASSUME_NONNULL_BEGIN // 2. [[PHGPostHog sharedInstance] alias:someNewAlias options:someOptions]; - (void)alias:(PHGAliasPayload *)payload; +- (void)group:(PHGGroupPayload *)payload; + // Reset is invoked when the user logs out, and any data saved about the user should be cleared. - (void)reset; diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index 63f366bdb8..458e8d379d 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -164,6 +164,18 @@ - (void)alias:(NSString *)alias options:(NSDictionary *)options options:options]; } +#pragma mark - Group + +- (void)group:(NSString *)groupType groupKey:(NSString *)groupKey groupProperties:(NSDictionary *)properties options:(NSDictionary *)options +{ + PHGGroupPayload *payload = [[PHGGroupPayload alloc] initWithType:groupType groupKey:groupKey + properties:PHGCoerceDictionary(properties)]; + + [self callWithSelector:NSSelectorFromString(@"group:") + arguments:@[ payload ] + options:options]; +} + #pragma mark - Feature Flags - (void)receivedFeatureFlags:(NSDictionary *)flags @@ -407,6 +419,11 @@ - (void)context:(PHGContext *)context next:(void (^_Nonnull)(PHGContext *_Nullab [self alias:p.alias options:p.options]; break; } + case PHGEventTypeGroup: { + PHGGroupPayload *p = (PHGGroupPayload *)context.payload; + [self group:p.groupType groupKey:p.groupKey groupProperties:p.properties options:p.options]; + break; + } case PHGEventTypeReset: [self reset]; break; diff --git a/PostHog/Classes/PHGPostHog.h b/PostHog/Classes/PHGPostHog.h index 12e3ef9242..3c8a633bd7 100644 --- a/PostHog/Classes/PHGPostHog.h +++ b/PostHog/Classes/PHGPostHog.h @@ -127,6 +127,8 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)alias:(NSString *)alias; +- (void)group:(NSString *)groupType groupKey:(NSString *)groupKey properties:(SERIALIZABLE_DICT _Nullable)properties; + - (bool)isFeatureEnabled:(NSString *)flagKey; - (void)reloadFeatureFlags; diff --git a/PostHog/Classes/PHGPostHog.m b/PostHog/Classes/PHGPostHog.m index 206d6fff49..da44602909 100644 --- a/PostHog/Classes/PHGPostHog.m +++ b/PostHog/Classes/PHGPostHog.m @@ -13,6 +13,7 @@ #import "PHGCapturePayload.h" #import "PHGScreenPayload.h" #import "PHGAliasPayload.h" +#import "PHGGroupPayload.h" static PHGPostHog *__sharedInstance = nil; @@ -255,6 +256,13 @@ - (void)alias:(NSString *)alias [[PHGAliasPayload alloc] initWithAlias:alias]]; } +#pragma mark - Group + +- (void)group:(NSString *_Nonnull)groupType groupKey:(NSString *_Nonnull)groupKey properties:(NSDictionary *)properties +{ + [self run:PHGEventTypeGroup payload: [[PHGGroupPayload alloc] initWithType:groupType groupKey:groupKey properties:PHGCoerceDictionary(properties)]]; +} + - (bool)isFeatureEnabled:(NSString *)flagKey { NSArray *keys = [self.payloadManager getFeatureFlags]; diff --git a/PostHog/Internal/PHGPostHogIntegration.h b/PostHog/Internal/PHGPostHogIntegration.h index bbd2a33aeb..3abc433fde 100644 --- a/PostHog/Internal/PHGPostHogIntegration.h +++ b/PostHog/Internal/PHGPostHogIntegration.h @@ -7,6 +7,7 @@ @class PHGCapturePayload; @class PHGScreenPayload; @class PHGAliasPayload; +@class PHGGroupPayload; NS_ASSUME_NONNULL_BEGIN diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index aaeb5da80d..cb3831a016 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -12,6 +12,7 @@ #import "PHGCapturePayload.h" #import "PHGScreenPayload.h" #import "PHGAliasPayload.h" +#import "PHGGroupPayload.h" #if TARGET_OS_IOS #import @@ -357,6 +358,19 @@ - (void)alias:(PHGAliasPayload *)payload [self enqueueAction:dictionary]; } +- (void)group:(PHGGroupPayload *)payload +{ + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setValue:@"$groupidentify" forKey:@"event"]; + + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + [properties setValue:payload.groupType forKey:@"$group_type"]; + [properties setValue:payload.groupKey forKey:@"group_key"]; + [dictionary setValue:properties forKey:@"group_set"]; + + [self enqueueAction:dictionary]; +} + - (void)registeredForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSCParameterAssert(deviceToken != nil); diff --git a/PostHogTests/CapturingTests.swift b/PostHogTests/CapturingTests.swift index 21100aa0ea..549292e6b6 100644 --- a/PostHogTests/CapturingTests.swift +++ b/PostHogTests/CapturingTests.swift @@ -70,6 +70,16 @@ class CapturingTests: QuickSpec { expect(screen?.name) == "Home" expect(screen?.properties?["referrer"] as? String) == "Google" } + + it("handles group:") { + posthog.group("some-type", groupKey: "some-key", properties: [ + "name": "some-company-name" + ]) + expect(passthrough.lastContext?.eventType) == PHGEventType.group + let payload = passthrough.lastContext?.payload as? PHGGroupPayload + expect(payload?.groupType) == "some-type" + expect(payload?.properties?["name"] as? String) == "some-company-name" + } it("handles null values") { posthog.capture("null test", properties: [ From c43e4b8794f9ddfec8c9d7795def2e41a75dca6a Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 15:33:30 -0400 Subject: [PATCH 10/28] working groups --- PostHog/Classes/PHGPayloadManager.h | 2 ++ PostHog/Classes/PHGPayloadManager.m | 31 +++++++++++++++++++++++++++++ PostHog/Classes/PHGPostHog.m | 10 ++++++++++ PostHogTests/CapturingTests.swift | 15 ++++++++++++-- PostHogTests/Utils/TestUtils.swift | 2 ++ 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/PostHog/Classes/PHGPayloadManager.h b/PostHog/Classes/PHGPayloadManager.h index df77ea0d0c..cb525b015f 100644 --- a/PostHog/Classes/PHGPayloadManager.h +++ b/PostHog/Classes/PHGPayloadManager.h @@ -14,6 +14,8 @@ extern NSString *_Nonnull PHGPostHogIntegrationDidStart; - (instancetype _Nonnull)initWithPostHog:(PHGPostHog *_Nonnull)posthog; - (NSArray *_Nonnull)getFeatureFlags; +- (NSDictionary *_Nonnull)getGroups; +- (void)saveGroup:(NSString *_Nonnull)groupType groupKey:(NSString *_Nonnull)groupKey; // @Deprecated - Exposing for backward API compat reasons only - (NSString *_Nonnull)getAnonymousId; diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index 458e8d379d..69a8d9ec0d 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -19,6 +19,9 @@ static NSString *const PHGEnabledFeatureFlags = @"PHGEnabledFeatureFlags"; static NSString *const kPHGEnabledFeatureFlags = @"posthog.enabledFeatureFlags"; +static NSString *const PHGGroups = @"PHGGroups"; +static NSString *const kPHGGroups = @"posthog.groups"; + @interface PHGPayloadManager () @@ -307,6 +310,34 @@ - (void)saveAnonymousId:(NSString *)anonymousId #endif } +- (void)saveGroup:(NSString *)groupType groupKey:(NSString *)groupKey +{ + NSDictionary *currentGroups = [self getGroups]; + NSMutableDictionary *newGroups = [currentGroups mutableCopy]; + [newGroups setObject:groupKey forKey:groupType]; + +#if TARGET_OS_TV + [self.userDefaultsStorage setDictionary:newGroups forKey:PHGGroups]; +#else + [self.fileStorage setDictionary:newGroups forKey:kPHGGroups]; +#endif +} + +- (NSDictionary *)getGroups +{ +#if TARGET_OS_TV + NSDictionary *groups = [self.userDefaultsStorage dictionaryForKey:PHGGroups]; +#else + NSDictionary *groups = [self.fileStorage dictionaryForKey:kPHGGroups]; +#endif + +// if groups doesn't exist, return a new empty dict + if (!groups){ + return [[NSDictionary alloc] init]; + } + return groups; +} + - (void)flush { [self callWithSelector:_cmd arguments:nil options:nil]; diff --git a/PostHog/Classes/PHGPostHog.m b/PostHog/Classes/PHGPostHog.m index da44602909..235f35c469 100644 --- a/PostHog/Classes/PHGPostHog.m +++ b/PostHog/Classes/PHGPostHog.m @@ -260,7 +260,17 @@ - (void)alias:(NSString *)alias - (void)group:(NSString *_Nonnull)groupType groupKey:(NSString *_Nonnull)groupKey properties:(NSDictionary *)properties { + NSDictionary *currentGroups = [self.payloadManager getGroups]; + NSLog(@"%@", currentGroups); + [self.payloadManager saveGroup:groupType groupKey:groupKey]; + [self run:PHGEventTypeGroup payload: [[PHGGroupPayload alloc] initWithType:groupType groupKey:groupKey properties:PHGCoerceDictionary(properties)]]; + + NSString *possibleGroupKey = [currentGroups objectForKey:groupType]; + + if (![possibleGroupKey isEqualToString:groupKey]){ + [self reloadFeatureFlags]; + } } - (bool)isFeatureEnabled:(NSString *)flagKey diff --git a/PostHogTests/CapturingTests.swift b/PostHogTests/CapturingTests.swift index 549292e6b6..c3e17df426 100644 --- a/PostHogTests/CapturingTests.swift +++ b/PostHogTests/CapturingTests.swift @@ -75,10 +75,21 @@ class CapturingTests: QuickSpec { posthog.group("some-type", groupKey: "some-key", properties: [ "name": "some-company-name" ]) - expect(passthrough.lastContext?.eventType) == PHGEventType.group - let payload = passthrough.lastContext?.payload as? PHGGroupPayload + let firstContext = passthrough.allContexts[0] + + expect(firstContext.eventType) == PHGEventType.group + let payload = firstContext.payload as? PHGGroupPayload expect(payload?.groupType) == "some-type" expect(payload?.properties?["name"] as? String) == "some-company-name" + + let secondContext = passthrough.allContexts[1] + expect(secondContext.eventType) == PHGEventType.reloadFeatureFlags + + posthog.group("some-type", groupKey: "some-key", properties: [ + "name": "some-company-name" + ]) + expect(passthrough.allContexts.count) == 3 + } it("handles null values") { diff --git a/PostHogTests/Utils/TestUtils.swift b/PostHogTests/Utils/TestUtils.swift index 7b28e0b718..26393ba7a8 100644 --- a/PostHogTests/Utils/TestUtils.swift +++ b/PostHogTests/Utils/TestUtils.swift @@ -4,8 +4,10 @@ import PostHog class PHGPassthroughMiddleware: PHGMiddleware { var lastContext: PHGContext? + var allContexts = [PHGContext]() func context(_ context: PHGContext, next: @escaping PHGMiddlewareNext) { + allContexts.append(context) lastContext = context; next(context) } From 20acc529a80a2315b1211cc56f770a3569036866 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 15:37:30 -0400 Subject: [PATCH 11/28] add groups to ff --- PostHog/Classes/PHGPayloadManager.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index 69a8d9ec0d..40bbe125ab 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -201,9 +201,10 @@ - (void)receivedFeatureFlags:(NSDictionary *)flags - (void)reloadFeatureFlags { NSMutableDictionary *payload = [[NSMutableDictionary alloc] init]; +// TODO: handle IDs [payload setObject:[self getAnonymousId] forKey:@"$anon_distinct_id"]; -// TODO: add groups [payload setObject:[self getAnonymousId] forKey:@"distinct_id"]; + [payload setObject:[self getGroups] forKey:@"$groups"]; [payload setObject:self.posthog.configuration.apiKey forKey:@"api_key"]; NSURL *url = [self.posthog.configuration.host URLByAppendingPathComponent:@"decide"]; @@ -215,7 +216,7 @@ - (void)reloadFeatureFlags [self.httpClient sharedSessionUpload:payload host:url success:^(NSDictionary * _Nonnull responseDict) { [self receivedFeatureFlags:responseDict]; } failure:^(NSError * _Nonnull error) { - +// TODO: handle error }]; } From 74ebe724efea074c429a6d6115dd916a0ed99c33 Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 18:08:00 -0400 Subject: [PATCH 12/28] adjust id handling --- PostHog/Classes/PHGPayloadManager.m | 19 ++++++++++--------- PostHog/Internal/PHGFileStorage.h | 1 + PostHog/Internal/PHGPostHogIntegration.h | 1 + PostHogTests/CapturingTests.swift | 8 -------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index 40bbe125ab..d52e274902 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -62,8 +62,12 @@ - (instancetype _Nonnull)initWithPostHog:(PHGPostHog *_Nonnull)posthog #endif self.cachedAnonymousId = [self loadOrGenerateAnonymousID:NO]; - + self.integration = [[PHGPostHogIntegration alloc] initWithPostHog:self.posthog httpClient:self.self.httpClient fileStorage:self.fileStorage userDefaultsStorage:self.userDefaultsStorage]; + + if (self.integration.distinctId.length == 0) { + [self.integration saveDistinctId:self.cachedAnonymousId]; + } [[NSNotificationCenter defaultCenter] postNotificationName:PHGPostHogIntegrationDidStart object:@"PostHog" userInfo:nil]; [self flushMessageQueue]; @@ -112,15 +116,11 @@ - (void)identify:(NSString *)distinctId properties:(NSDictionary *)properties op { NSCAssert1(distinctId.length > 0, @"distinctId (%@) must not be empty.", distinctId); - NSString *anonymousId = [options objectForKey:@"$anon_distinct_id"]; - if (anonymousId) { - [self saveAnonymousId:anonymousId]; - } else { - anonymousId = self.cachedAnonymousId; - } + [self saveAnonymousId:self.integration.distinctId]; + [self.integration saveDistinctId:distinctId]; PHGIdentifyPayload *payload = [[PHGIdentifyPayload alloc] initWithDistinctId:distinctId - anonymousId:anonymousId + anonymousId:self.cachedAnonymousId properties:PHGCoerceDictionary(properties)]; [self callWithSelector:NSSelectorFromString(@"identify:") @@ -203,7 +203,7 @@ - (void)reloadFeatureFlags NSMutableDictionary *payload = [[NSMutableDictionary alloc] init]; // TODO: handle IDs [payload setObject:[self getAnonymousId] forKey:@"$anon_distinct_id"]; - [payload setObject:[self getAnonymousId] forKey:@"distinct_id"]; + [payload setObject:self.integration.distinctId forKey:@"distinct_id"]; [payload setObject:[self getGroups] forKey:@"$groups"]; [payload setObject:self.posthog.configuration.apiKey forKey:@"api_key"]; @@ -266,6 +266,7 @@ - (void)reset { [self resetAnonymousId]; [self callWithSelector:_cmd arguments:nil options:nil]; + [self.fileStorage resetAll]; } - (void)resetAnonymousId diff --git a/PostHog/Internal/PHGFileStorage.h b/PostHog/Internal/PHGFileStorage.h index 07c39ead4b..26f01b2b32 100644 --- a/PostHog/Internal/PHGFileStorage.h +++ b/PostHog/Internal/PHGFileStorage.h @@ -9,6 +9,7 @@ - (instancetype _Nonnull)initWithFolder:(NSURL *_Nonnull)folderURL crypto:(id _Nullable)crypto; - (NSURL *_Nonnull)urlForKey:(NSString *_Nonnull)key; +- (void)resetAll; + (NSURL *_Nullable)applicationSupportDirectoryURL; + (NSURL *_Nullable)cachesDirectoryURL; diff --git a/PostHog/Internal/PHGPostHogIntegration.h b/PostHog/Internal/PHGPostHogIntegration.h index 3abc433fde..4f27644a67 100644 --- a/PostHog/Internal/PHGPostHogIntegration.h +++ b/PostHog/Internal/PHGPostHogIntegration.h @@ -23,6 +23,7 @@ extern NSString *const PHGPostHogRequestDidFailNotification; - (id)initWithPostHog:(PHGPostHog *)posthog httpClient:(PHGHTTPClient *)httpClient fileStorage:(id)fileStorage userDefaultsStorage:(id)userDefaultsStorage; - (NSDictionary *)staticContext; - (NSDictionary *)liveContext; +- (void)saveDistinctId:(NSString *)distinctId; @end diff --git a/PostHogTests/CapturingTests.swift b/PostHogTests/CapturingTests.swift index c3e17df426..8eb5039a85 100644 --- a/PostHogTests/CapturingTests.swift +++ b/PostHogTests/CapturingTests.swift @@ -81,14 +81,6 @@ class CapturingTests: QuickSpec { let payload = firstContext.payload as? PHGGroupPayload expect(payload?.groupType) == "some-type" expect(payload?.properties?["name"] as? String) == "some-company-name" - - let secondContext = passthrough.allContexts[1] - expect(secondContext.eventType) == PHGEventType.reloadFeatureFlags - - posthog.group("some-type", groupKey: "some-key", properties: [ - "name": "some-company-name" - ]) - expect(passthrough.allContexts.count) == 3 } From 3a8f064090762e2b830e52eac3b6d17fafadf32d Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 18:09:24 -0400 Subject: [PATCH 13/28] remove logs --- PostHogTests/HTTPClientTest.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PostHogTests/HTTPClientTest.swift b/PostHogTests/HTTPClientTest.swift index dc51952bbe..191095b465 100644 --- a/PostHogTests/HTTPClientTest.swift +++ b/PostHogTests/HTTPClientTest.swift @@ -47,11 +47,11 @@ class HTTPClientTest: QuickSpec { expect(responseDict).toNot(beNil()) done = true } failure: { error in - NSLog("FAILURE") + expect(error).to(beNil()) done = true } - NSLog("nEW") + expect(task).toNot(beNil()) expect(done).toEventually(beTrue()) } From 6bc1f52be71daa3cd5b05a33c37da37ffe1f706c Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 7 Jul 2022 18:11:38 -0400 Subject: [PATCH 14/28] add comments --- PostHog/Internal/PHGHTTPClient.m | 1 + PostHogTests/Utils/TestUtils.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/PostHog/Internal/PHGHTTPClient.m b/PostHog/Internal/PHGHTTPClient.m index 7db1eda5e9..26d0dfa9b6 100644 --- a/PostHog/Internal/PHGHTTPClient.m +++ b/PostHog/Internal/PHGHTTPClient.m @@ -105,6 +105,7 @@ - (NSURLSessionUploadTask *)upload:(NSDictionary *)batch host:(NSURL *)host comp return task; } +// Use shared session handler for basic network requests - (NSURLSessionDataTask *)sharedSessionUpload:(NSDictionary *)payload host:(NSURL *)host success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure { NSMutableURLRequest *request = self.requestFactory(host); diff --git a/PostHogTests/Utils/TestUtils.swift b/PostHogTests/Utils/TestUtils.swift index 26393ba7a8..7343aaf44e 100644 --- a/PostHogTests/Utils/TestUtils.swift +++ b/PostHogTests/Utils/TestUtils.swift @@ -4,6 +4,7 @@ import PostHog class PHGPassthroughMiddleware: PHGMiddleware { var lastContext: PHGContext? +// Catch more than just the latest context var allContexts = [PHGContext]() func context(_ context: PHGContext, next: @escaping PHGMiddlewareNext) { From 1fc7f7e26c0827ab963ecb140183092f661ba988 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 18 Jul 2022 15:31:56 -0400 Subject: [PATCH 15/28] flags working --- PostHog/Classes/PHGPayloadManager.m | 6 ++++-- PostHog/Classes/PHGPostHog.m | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index d52e274902..0e7bf4f3cb 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -192,7 +192,7 @@ - (void)receivedFeatureFlags:(NSDictionary *)flags #endif #if TARGET_OS_TV - [self.userDefaultsStorage setString:anonymousId forKey:PHGEnabledFeatureFlags]; + [self.userDefaultsStorage setDictinoary:flags forKey:PHGEnabledFeatureFlags]; #else [self.fileStorage setDictionary:flags forKey:kPHGEnabledFeatureFlags]; #endif @@ -214,7 +214,9 @@ - (void)reloadFeatureFlags [self.httpClient sharedSessionUpload:payload host:url success:^(NSDictionary * _Nonnull responseDict) { - [self receivedFeatureFlags:responseDict]; + NSDictionary *flags = [responseDict objectForKey:@"featureFlags"]; + [self receivedFeatureFlags:flags]; + NSArray *keys = [self getFeatureFlags]; } failure:^(NSError * _Nonnull error) { // TODO: handle error }]; diff --git a/PostHog/Classes/PHGPostHog.m b/PostHog/Classes/PHGPostHog.m index 235f35c469..810caf69a5 100644 --- a/PostHog/Classes/PHGPostHog.m +++ b/PostHog/Classes/PHGPostHog.m @@ -85,7 +85,8 @@ - (instancetype)initWithConfiguration:(PHGPostHogConfiguration *)configuration } } #endif - + + [self reloadFeatureFlags]; } return self; } From a8963de71f2f489239b94b9db4892497e75ab8df Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 18 Jul 2022 18:20:35 -0400 Subject: [PATCH 16/28] adjust group handling --- PostHog/Classes/PHGPayloadManager.m | 2 +- PostHog/Internal/PHGPostHogIntegration.m | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index 0e7bf4f3cb..f51fe0ea89 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -192,7 +192,7 @@ - (void)receivedFeatureFlags:(NSDictionary *)flags #endif #if TARGET_OS_TV - [self.userDefaultsStorage setDictinoary:flags forKey:PHGEnabledFeatureFlags]; + [self.userDefaultsStorage setDictionary:flags forKey:PHGEnabledFeatureFlags]; #else [self.fileStorage setDictionary:flags forKey:kPHGEnabledFeatureFlags]; #endif diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index cb3831a016..9e7eb15bb4 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -365,9 +365,10 @@ - (void)group:(PHGGroupPayload *)payload NSMutableDictionary *properties = [NSMutableDictionary dictionary]; [properties setValue:payload.groupType forKey:@"$group_type"]; - [properties setValue:payload.groupKey forKey:@"group_key"]; - [dictionary setValue:properties forKey:@"group_set"]; - + [properties setValue:payload.groupKey forKey:@"$group_key"]; + [properties setValue:payload.properties forKey:@"$group_set"]; + + [dictionary setValue:properties forKey:@"properties"]; [self enqueueAction:dictionary]; } From 0d0fd07367abd2ad1706fe6839fdec0051211a88 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 18 Jul 2022 20:32:24 -0400 Subject: [PATCH 17/28] working groups --- PostHog/Classes/PHGPostHog.h | 1 + PostHog/Classes/PHGPostHog.m | 8 +++++++- PostHog/Internal/PHGPostHogIntegration.m | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/PostHog/Classes/PHGPostHog.h b/PostHog/Classes/PHGPostHog.h index 3c8a633bd7..118f6288cb 100644 --- a/PostHog/Classes/PHGPostHog.h +++ b/PostHog/Classes/PHGPostHog.h @@ -127,6 +127,7 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)alias:(NSString *)alias; +- (void)group:(NSString *)groupType groupKey:(NSString *)groupKey; - (void)group:(NSString *)groupType groupKey:(NSString *)groupKey properties:(SERIALIZABLE_DICT _Nullable)properties; - (bool)isFeatureEnabled:(NSString *)flagKey; diff --git a/PostHog/Classes/PHGPostHog.m b/PostHog/Classes/PHGPostHog.m index 810caf69a5..c451a70941 100644 --- a/PostHog/Classes/PHGPostHog.m +++ b/PostHog/Classes/PHGPostHog.m @@ -259,10 +259,16 @@ - (void)alias:(NSString *)alias #pragma mark - Group +- (void)group:(NSString *_Nonnull)groupType groupKey:(NSString *_Nonnull)groupKey +{ + [self group:groupType groupKey:groupKey properties:nil]; +} + - (void)group:(NSString *_Nonnull)groupType groupKey:(NSString *_Nonnull)groupKey properties:(NSDictionary *)properties { NSDictionary *currentGroups = [self.payloadManager getGroups]; - NSLog(@"%@", currentGroups); + +// TODO: set groups as super property [self.payloadManager saveGroup:groupType groupKey:groupKey]; [self run:PHGEventTypeGroup payload: [[PHGGroupPayload alloc] initWithType:groupType groupKey:groupKey properties:PHGCoerceDictionary(properties)]]; diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index 9e7eb15bb4..bc5025a5cd 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -369,6 +369,7 @@ - (void)group:(PHGGroupPayload *)payload [properties setValue:payload.properties forKey:@"$group_set"]; [dictionary setValue:properties forKey:@"properties"]; + [dictionary setValue:self.distinctId ?: [self.posthog getAnonymousId] forKey:@"distinct_id"]; [self enqueueAction:dictionary]; } From 1fb7ea660ac5b0ef03fb13244f9c9f75a087273f Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 19 Jul 2022 11:13:01 -0400 Subject: [PATCH 18/28] update stub --- PostHogTests/FeatureFlagTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PostHogTests/FeatureFlagTests.swift b/PostHogTests/FeatureFlagTests.swift index f1dce11144..e81865b9ed 100644 --- a/PostHogTests/FeatureFlagTests.swift +++ b/PostHogTests/FeatureFlagTests.swift @@ -22,7 +22,7 @@ class FeatureFlagTests: QuickSpec { it("checks flag is enabled") { _ = stubRequest("POST", "https://app.posthog.test/decide/?v=2" as LSMatcheable) .andReturn(200)? - .withBody("{\"some-flag\":\"true\"}" as LSHTTPBody); + .withBody("{\"featureFlags\":{\"some-flag\":\"true\"}}" as LSHTTPBody); posthog.reloadFeatureFlags() // Hacky: Need to buffer for async request to happen without stub being cleaned up sleep(1) From 7dc6af07664b650f65854ce2ec96197e644ee760 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 19 Jul 2022 11:34:42 -0400 Subject: [PATCH 19/28] fix tests --- PostHog/Classes/PHGPayloadManager.m | 3 +-- PostHog/Internal/PHGPostHogIntegration.m | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index f51fe0ea89..0ccd1e3826 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -203,7 +203,7 @@ - (void)reloadFeatureFlags NSMutableDictionary *payload = [[NSMutableDictionary alloc] init]; // TODO: handle IDs [payload setObject:[self getAnonymousId] forKey:@"$anon_distinct_id"]; - [payload setObject:self.integration.distinctId forKey:@"distinct_id"]; + [payload setObject:self.integration.distinctId ? self.integration.distinctId : [self getAnonymousId] forKey:@"distinct_id"]; [payload setObject:[self getGroups] forKey:@"$groups"]; [payload setObject:self.posthog.configuration.apiKey forKey:@"api_key"]; @@ -216,7 +216,6 @@ - (void)reloadFeatureFlags [self.httpClient sharedSessionUpload:payload host:url success:^(NSDictionary * _Nonnull responseDict) { NSDictionary *flags = [responseDict objectForKey:@"featureFlags"]; [self receivedFeatureFlags:flags]; - NSArray *keys = [self getFeatureFlags]; } failure:^(NSError * _Nonnull error) { // TODO: handle error }]; diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index bc5025a5cd..e5117070f5 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -286,15 +286,14 @@ - (NSString *)description - (void)saveDistinctId:(NSString *)distinctId { - [self dispatchBackground:^{ - self.distinctId = distinctId; + self.distinctId = distinctId; #if TARGET_OS_TV - [self.userDefaultsStorage setString:distinctId forKey:PHGDistinctIdKey]; + [self.userDefaultsStorage setString:distinctId forKey:PHGDistinctIdKey]; #else - [self.fileStorage setString:distinctId forKey:kPHGDistinctIdFilename]; + [self.fileStorage setString:distinctId forKey:kPHGDistinctIdFilename]; #endif - }]; + } #pragma mark - PostHog API From 75e5497050e2d56e47ef452a12f4e56f4855540c Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 19 Jul 2022 11:43:28 -0400 Subject: [PATCH 20/28] fix group test --- PostHogTests/CapturingTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PostHogTests/CapturingTests.swift b/PostHogTests/CapturingTests.swift index 8eb5039a85..c58e559e55 100644 --- a/PostHogTests/CapturingTests.swift +++ b/PostHogTests/CapturingTests.swift @@ -75,7 +75,7 @@ class CapturingTests: QuickSpec { posthog.group("some-type", groupKey: "some-key", properties: [ "name": "some-company-name" ]) - let firstContext = passthrough.allContexts[0] + let firstContext = passthrough.allContexts[1] expect(firstContext.eventType) == PHGEventType.group let payload = firstContext.payload as? PHGGroupPayload From c44c85c4b2e435df1f51a5eebf32bf0b93d50ca1 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 19 Jul 2022 12:30:24 -0400 Subject: [PATCH 21/28] disable workflow --- .github/{workflows => disabled_workflows}/tests.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{workflows => disabled_workflows}/tests.yml (100%) diff --git a/.github/workflows/tests.yml b/.github/disabled_workflows/tests.yml similarity index 100% rename from .github/workflows/tests.yml rename to .github/disabled_workflows/tests.yml From 8d575ba9e320e7871372d5185cc40a8bcf9fdc05 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 19 Jul 2022 13:49:32 -0400 Subject: [PATCH 22/28] add example calls to examples directory --- Examples/CarthageExample/CarthageExample/AppDelegate.swift | 1 + Examples/CarthageExample/CarthageExample/ViewController.swift | 3 +++ Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m | 2 ++ Examples/CocoapodsExample/CocoapodsExample/ViewController.m | 2 ++ Examples/ManualExample/ManualExample/AppDelegate.m | 2 ++ Examples/ManualExample/ManualExample/ViewController.m | 2 ++ PostHog/Internal/PHGPostHogIntegration.m | 1 - PostHogTests/CapturingTests.swift | 2 +- 8 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Examples/CarthageExample/CarthageExample/AppDelegate.swift b/Examples/CarthageExample/CarthageExample/AppDelegate.swift index 22ef91870e..d8faa8996b 100644 --- a/Examples/CarthageExample/CarthageExample/AppDelegate.swift +++ b/Examples/CarthageExample/CarthageExample/AppDelegate.swift @@ -17,6 +17,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { PostHog.capture("Carthage Example Launched") PostHog.flush() + PostHog.isFeatureEnabled("test-flag") return true } diff --git a/Examples/CarthageExample/CarthageExample/ViewController.swift b/Examples/CarthageExample/CarthageExample/ViewController.swift index 3e4f99882a..fc68fff76b 100644 --- a/Examples/CarthageExample/CarthageExample/ViewController.swift +++ b/Examples/CarthageExample/CarthageExample/ViewController.swift @@ -6,6 +6,9 @@ class ViewController: UIViewController { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. PostHog.capture("Carthage Main View Did Load") + PostHog.group("some-group", groupKey: "id:4", properties: [ + "company_name": "Awesome Inc" + ]); PostHog.flush() } diff --git a/Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m b/Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m index a172ef04f8..22297cb033 100644 --- a/Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m +++ b/Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m @@ -23,6 +23,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [[PHGPostHog sharedPostHog] identify:@"Prateek" properties:nil options: @{@"$anon_distinct_id":@"test_anonymousId"}]; [[PHGPostHog sharedPostHog] capture:@"Cocoapods Example Launched"]; + [[PHGPostHog sharedPostHog] group:@"some-group" groupKey:@"id:4" properties: @{@"company_name":@"Awesome Inc"}]; + [[PHGPostHog sharedPostHog] flush]; NSLog(@"application:didFinishLaunchingWithOptions: %@", launchOptions); return YES; diff --git a/Examples/CocoapodsExample/CocoapodsExample/ViewController.m b/Examples/CocoapodsExample/CocoapodsExample/ViewController.m index cca3a901ea..bc2b813591 100644 --- a/Examples/CocoapodsExample/CocoapodsExample/ViewController.m +++ b/Examples/CocoapodsExample/CocoapodsExample/ViewController.m @@ -20,6 +20,8 @@ - (void)viewDidLoad [[PHGPostHog sharedPostHog] continueUserActivity:userActivity]; [[PHGPostHog sharedPostHog] capture:@"test"]; [[PHGPostHog sharedPostHog] flush]; + + [[PHGPostHog sharedPostHog] isFeatureEnabled:@"test-flag"]; } - (IBAction)fireEvent:(id)sender diff --git a/Examples/ManualExample/ManualExample/AppDelegate.m b/Examples/ManualExample/ManualExample/AppDelegate.m index d531e0acb8..c956c38c6b 100644 --- a/Examples/ManualExample/ManualExample/AppDelegate.m +++ b/Examples/ManualExample/ManualExample/AppDelegate.m @@ -15,6 +15,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [PHGPostHog setupWithConfiguration:[PHGPostHogConfiguration configurationWithApiKey:@"St5WbSrxW5bzxn2NGwPDVzS9PBPYctOl" host:@"https://app.posthog.com"]]; [[PHGPostHog sharedPostHog] capture:@"Manual Example Launched"]; [[PHGPostHog sharedPostHog] flush]; + + [[PHGPostHog sharedPostHog] group:@"some-group" groupKey:@"id:4" properties: @{@"company_name":@"Awesome Inc"}]; return YES; } diff --git a/Examples/ManualExample/ManualExample/ViewController.m b/Examples/ManualExample/ManualExample/ViewController.m index 3e8b823147..38bec586f0 100644 --- a/Examples/ManualExample/ManualExample/ViewController.m +++ b/Examples/ManualExample/ManualExample/ViewController.m @@ -14,6 +14,8 @@ - (void)viewDidLoad [super viewDidLoad]; [[PHGPostHog sharedPostHog] capture:@"Manual Example Main View Loaded"]; [[PHGPostHog sharedPostHog] flush]; + + [[PHGPostHog sharedPostHog] isFeatureEnabled:@"test-flag"]; } - (void)didReceiveMemoryWarning diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index e5117070f5..039715aa64 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -293,7 +293,6 @@ - (void)saveDistinctId:(NSString *)distinctId #else [self.fileStorage setString:distinctId forKey:kPHGDistinctIdFilename]; #endif - } #pragma mark - PostHog API diff --git a/PostHogTests/CapturingTests.swift b/PostHogTests/CapturingTests.swift index c58e559e55..05b61b8c1c 100644 --- a/PostHogTests/CapturingTests.swift +++ b/PostHogTests/CapturingTests.swift @@ -72,7 +72,7 @@ class CapturingTests: QuickSpec { } it("handles group:") { - posthog.group("some-type", groupKey: "some-key", properties: [ + posthog.group( "some-type", groupKey: "some-key", properties: [ "name": "some-company-name" ]) let firstContext = passthrough.allContexts[1] From 364ee9e14bc28577a6e950de147f4f5409033966 Mon Sep 17 00:00:00 2001 From: Eric Duong Date: Thu, 21 Jul 2022 13:46:11 -0400 Subject: [PATCH 23/28] Feat: Multivariate feature flags (#30) * feat: support multivariate flag checking * send capture call * Persistence of feature flags and groups (#31) * feat: make sure groups and feature flags and persisted and sent * remove deprecated key --- PostHog/Classes/PHGPayloadManager.h | 1 + PostHog/Classes/PHGPayloadManager.m | 58 +++---------------- PostHog/Classes/PHGPostHog.h | 1 + PostHog/Classes/PHGPostHog.m | 25 ++++++++ PostHog/Internal/PHGPostHogIntegration.h | 7 +++ PostHog/Internal/PHGPostHogIntegration.m | 74 ++++++++++++++++++++++++ PostHogTests/FeatureFlagTests.swift | 22 +++++++ 7 files changed, 139 insertions(+), 49 deletions(-) diff --git a/PostHog/Classes/PHGPayloadManager.h b/PostHog/Classes/PHGPayloadManager.h index cb525b015f..d9f3d81f71 100644 --- a/PostHog/Classes/PHGPayloadManager.h +++ b/PostHog/Classes/PHGPayloadManager.h @@ -14,6 +14,7 @@ extern NSString *_Nonnull PHGPostHogIntegrationDidStart; - (instancetype _Nonnull)initWithPostHog:(PHGPostHog *_Nonnull)posthog; - (NSArray *_Nonnull)getFeatureFlags; +- (NSDictionary *_Nonnull)getFlagVariants; - (NSDictionary *_Nonnull)getGroups; - (void)saveGroup:(NSString *_Nonnull)groupType groupKey:(NSString *_Nonnull)groupKey; diff --git a/PostHog/Classes/PHGPayloadManager.m b/PostHog/Classes/PHGPayloadManager.m index 0ccd1e3826..bdb45bd589 100644 --- a/PostHog/Classes/PHGPayloadManager.m +++ b/PostHog/Classes/PHGPayloadManager.m @@ -13,16 +13,6 @@ static NSString *const PHGAnonymousIdKey = @"PHGAnonymousId"; static NSString *const kPHGAnonymousIdFilename = @"posthog.anonymousId"; -static NSString *const PHGActiveFeatureFlags = @"PHGActiveFeatureFlags"; -static NSString *const kPHGActiveFeatureFlags = @"posthog.activeFeatureFlags"; - -static NSString *const PHGEnabledFeatureFlags = @"PHGEnabledFeatureFlags"; -static NSString *const kPHGEnabledFeatureFlags = @"posthog.enabledFeatureFlags"; - -static NSString *const PHGGroups = @"PHGGroups"; -static NSString *const kPHGGroups = @"posthog.groups"; - - @interface PHGPayloadManager () @@ -183,19 +173,7 @@ - (void)group:(NSString *)groupType groupKey:(NSString *)groupKey groupPropertie - (void)receivedFeatureFlags:(NSDictionary *)flags { - NSArray* keys = [flags allKeys]; - -#if TARGET_OS_TV - [self.userDefaultsStorage setArray:keys forKey:PHGActiveFeatureFlags]; -#else - [self.fileStorage setArray:keys forKey:kPHGActiveFeatureFlags]; -#endif - -#if TARGET_OS_TV - [self.userDefaultsStorage setDictionary:flags forKey:PHGEnabledFeatureFlags]; -#else - [self.fileStorage setDictionary:flags forKey:kPHGEnabledFeatureFlags]; -#endif + [self.integration receivedFeatureFlags:flags]; } - (void)reloadFeatureFlags @@ -223,12 +201,12 @@ - (void)reloadFeatureFlags - (NSArray *)getFeatureFlags { -#if TARGET_OS_TV - NSArray *keys = [self.userDefaultsStorage arrayForKey:PHGActiveFeatureFlags]; -#else - NSArray *keys = [self.fileStorage arrayForKey:kPHGActiveFeatureFlags]; -#endif - return keys; + return [self.integration getFeatureFlags]; +} + +- (NSDictionary *)getFlagVariants +{ + return [self.integration getFeatureFlagsAndValues]; } - (void)receivedRemoteNotification:(NSDictionary *)userInfo @@ -315,30 +293,12 @@ - (void)saveAnonymousId:(NSString *)anonymousId - (void)saveGroup:(NSString *)groupType groupKey:(NSString *)groupKey { - NSDictionary *currentGroups = [self getGroups]; - NSMutableDictionary *newGroups = [currentGroups mutableCopy]; - [newGroups setObject:groupKey forKey:groupType]; - -#if TARGET_OS_TV - [self.userDefaultsStorage setDictionary:newGroups forKey:PHGGroups]; -#else - [self.fileStorage setDictionary:newGroups forKey:kPHGGroups]; -#endif + [self.integration saveGroup:groupType groupKey:groupKey]; } - (NSDictionary *)getGroups { -#if TARGET_OS_TV - NSDictionary *groups = [self.userDefaultsStorage dictionaryForKey:PHGGroups]; -#else - NSDictionary *groups = [self.fileStorage dictionaryForKey:kPHGGroups]; -#endif - -// if groups doesn't exist, return a new empty dict - if (!groups){ - return [[NSDictionary alloc] init]; - } - return groups; + return [self.integration getGroups]; } - (void)flush diff --git a/PostHog/Classes/PHGPostHog.h b/PostHog/Classes/PHGPostHog.h index 118f6288cb..c7ce759424 100644 --- a/PostHog/Classes/PHGPostHog.h +++ b/PostHog/Classes/PHGPostHog.h @@ -130,6 +130,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)group:(NSString *)groupType groupKey:(NSString *)groupKey; - (void)group:(NSString *)groupType groupKey:(NSString *)groupKey properties:(SERIALIZABLE_DICT _Nullable)properties; +- (id)getFeatureFlag:(NSString *)flagKey; - (bool)isFeatureEnabled:(NSString *)flagKey; - (void)reloadFeatureFlags; diff --git a/PostHog/Classes/PHGPostHog.m b/PostHog/Classes/PHGPostHog.m index c451a70941..ae3fbc60d9 100644 --- a/PostHog/Classes/PHGPostHog.m +++ b/PostHog/Classes/PHGPostHog.m @@ -280,10 +280,35 @@ - (void)group:(NSString *_Nonnull)groupType groupKey:(NSString *_Nonnull)groupKe } } +- (id)getFeatureFlag:(NSString *)flagKey +{ + NSDictionary *variants = [self.payloadManager getFlagVariants]; + id variantValue = [variants valueForKey:flagKey]; + + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + [properties setValue:flagKey forKey:@"$feature_flag"]; + [properties setValue:variantValue forKey:@"$feature_flag_response"]; + + [self run:PHGEventTypeCapture payload: + [[PHGCapturePayload alloc] initWithEvent:@"$feature_flag_called" + properties:PHGCoerceDictionary(properties)]]; + return variantValue; +} + - (bool)isFeatureEnabled:(NSString *)flagKey { NSArray *keys = [self.payloadManager getFeatureFlags]; BOOL isFlagEnabled = [keys containsObject: flagKey]; + + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + + [properties setValue:flagKey forKey:@"$feature_flag"]; + [properties setValue:@(isFlagEnabled) forKey:@"$feature_flag_response"]; + + [self run:PHGEventTypeCapture payload: + [[PHGCapturePayload alloc] initWithEvent:@"$feature_flag_called" + properties:PHGCoerceDictionary(properties)]]; + return isFlagEnabled; } diff --git a/PostHog/Internal/PHGPostHogIntegration.h b/PostHog/Internal/PHGPostHogIntegration.h index 4f27644a67..8ff4f3273e 100644 --- a/PostHog/Internal/PHGPostHogIntegration.h +++ b/PostHog/Internal/PHGPostHogIntegration.h @@ -25,6 +25,13 @@ extern NSString *const PHGPostHogRequestDidFailNotification; - (NSDictionary *)liveContext; - (void)saveDistinctId:(NSString *)distinctId; +- (NSDictionary *_Nonnull)getGroups; +- (void)saveGroup:(NSString *_Nonnull)groupType groupKey:(NSString *_Nonnull)groupKey; + +- (NSArray *_Nonnull)getFeatureFlags; +- (NSDictionary *)getFeatureFlagsAndValues; +- (void)receivedFeatureFlags:(NSDictionary *)flags; + @end NS_ASSUME_NONNULL_END diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index 039715aa64..c4e9217b93 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -31,6 +31,12 @@ NSString *const kPHGDistinctIdFilename = @"posthog.distinctId"; NSString *const kPHGQueueFilename = @"posthog.queue.plist"; +static NSString *const PHGEnabledFeatureFlags = @"PHGEnabledFeatureFlags"; +static NSString *const kPHGEnabledFeatureFlags = @"posthog.enabledFeatureFlags"; + +static NSString *const PHGGroups = @"PHGGroups"; +static NSString *const kPHGGroups = @"posthog.groups"; + static NSString *GetDeviceModel() { size_t size; @@ -217,6 +223,20 @@ - (NSDictionary *)liveContext context[@"$network_wifi"] = @NO; context[@"$network_cellular"] = @NO; } + + context[@"$groups"] = [self getGroups]; + context[@"$active_feature_flags"] = [self getFeatureFlags]; + + NSDictionary *flagsAndValues = [self getFeatureFlagsAndValues]; + + int n = 0; + for(id flag in flagsAndValues){ + NSString *key = [NSString stringWithFormat:@"$feature/%@", flag]; + NSString *enumeratedKey = [NSString stringWithFormat:@"$active_feature_flags__%d", n]; + context[key] = [flagsAndValues objectForKey:flag]; + context[enumeratedKey] = flag; + n++; + } #if TARGET_OS_IOS static dispatch_once_t networkInfoOnceToken; @@ -569,6 +589,60 @@ - (NSString *)getDistinctId #endif } +- (void)saveGroup:(NSString *)groupType groupKey:(NSString *)groupKey +{ + NSDictionary *currentGroups = [self getGroups]; + NSMutableDictionary *newGroups = [currentGroups mutableCopy]; + [newGroups setObject:groupKey forKey:groupType]; + +#if TARGET_OS_TV + [self.userDefaultsStorage setDictionary:newGroups forKey:PHGGroups]; +#else + [self.fileStorage setDictionary:newGroups forKey:kPHGGroups]; +#endif +} + +- (NSDictionary *)getGroups +{ +#if TARGET_OS_TV + NSDictionary *groups = [self.userDefaultsStorage dictionaryForKey:PHGGroups]; +#else + NSDictionary *groups = [self.fileStorage dictionaryForKey:kPHGGroups]; +#endif + +// if groups doesn't exist, return a new empty dict + if (!groups){ + return [[NSDictionary alloc] init]; + } + return groups; +} + +- (void)receivedFeatureFlags:(NSDictionary *)flags +{ +#if TARGET_OS_TV + [self.userDefaultsStorage setDictionary:flags forKey:PHGEnabledFeatureFlags]; +#else + [self.fileStorage setDictionary:flags forKey:kPHGEnabledFeatureFlags]; +#endif +} + +- (NSArray *)getFeatureFlags +{ + NSDictionary *dict = [self getFeatureFlagsAndValues]; + NSArray *keys = [dict allKeys]; + return keys; +} + +- (NSDictionary *)getFeatureFlagsAndValues +{ +#if TARGET_OS_TV + NSDictionary *dict = [self.userDefaultsStorage dictionaryForKey:PHGEnabledFeatureFlags]; +#else + NSDictionary *dict = [self.fileStorage dictionaryForKey:kPHGEnabledFeatureFlags]; +#endif + return dict; +} + - (void)persistQueue { [self.fileStorage setArray:[self.queue copy] forKey:kPHGQueueFilename]; diff --git a/PostHogTests/FeatureFlagTests.swift b/PostHogTests/FeatureFlagTests.swift index e81865b9ed..b48440fde7 100644 --- a/PostHogTests/FeatureFlagTests.swift +++ b/PostHogTests/FeatureFlagTests.swift @@ -29,6 +29,28 @@ class FeatureFlagTests: QuickSpec { let isEnabled = posthog.isFeatureEnabled("some-flag") expect(isEnabled).to(beTrue()) } + + it("checks multivariate flag is enabled - integer") { + _ = stubRequest("POST", "https://app.posthog.test/decide/?v=2" as LSMatcheable) + .andReturn(200)? + .withBody("{\"featureFlags\":{\"some-flag\":1}}" as LSHTTPBody); + posthog.reloadFeatureFlags() + // Hacky: Need to buffer for async request to happen without stub being cleaned up + sleep(1) + let flagValue = posthog.getFeatureFlag("some-flag") + expect(flagValue).to(be(1)) + } + + it("checks multivariate flag is enabled - string") { + _ = stubRequest("POST", "https://app.posthog.test/decide/?v=2" as LSMatcheable) + .andReturn(200)? + .withBody("{\"featureFlags\":{\"some-flag\":\"variant-1\"}}" as LSHTTPBody); + posthog.reloadFeatureFlags() + // Hacky: Need to buffer for async request to happen without stub being cleaned up + sleep(1) + let flagValue = posthog.getFeatureFlag("some-flag") + expect(flagValue).to(be("variant-1")) + } } From 3f0a0214acf88669e221d89356899d94f08d63fc Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 21 Jul 2022 14:29:11 -0400 Subject: [PATCH 24/28] renable workflows --- .github/{disabled_workflows => workflows}/tests.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{disabled_workflows => workflows}/tests.yml (100%) diff --git a/.github/disabled_workflows/tests.yml b/.github/workflows/tests.yml similarity index 100% rename from .github/disabled_workflows/tests.yml rename to .github/workflows/tests.yml From 3f50488b6c1372406013307e10ee738e504e0efe Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 25 Jul 2022 10:22:52 -0400 Subject: [PATCH 25/28] feat: session id support for payloads --- PostHog.xcodeproj/project.pbxproj | 12 +++++ PostHog/Classes/PHGSession.h | 14 ++++++ PostHog/Classes/PHGSession.m | 56 ++++++++++++++++++++++++ PostHog/Internal/PHGPostHogIntegration.m | 4 ++ PostHog/PostHog.h | 1 + PostHogTests/SessionTest.swift | 37 ++++++++++++++++ 6 files changed, 124 insertions(+) create mode 100644 PostHog/Classes/PHGSession.h create mode 100644 PostHog/Classes/PHGSession.m create mode 100644 PostHogTests/SessionTest.swift diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index 17ddcc8b89..403aab5a7e 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -90,6 +90,9 @@ EADEB8F31DECD335005322DA /* PostHogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E71DECD335005322DA /* PostHogTests.swift */; }; EADEB8F41DECD335005322DA /* ContextTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E81DECD335005322DA /* ContextTest.swift */; }; EADEB8F51DECD335005322DA /* CryptoTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADEB8E91DECD335005322DA /* CryptoTest.swift */; }; + F250ADE0288B401B0045FA7E /* PHGSession.m in Sources */ = {isa = PBXBuildFile; fileRef = F250ADDF288B401B0045FA7E /* PHGSession.m */; }; + F250ADE2288ED2E20045FA7E /* SessionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F250ADE1288ED2E20045FA7E /* SessionTest.swift */; }; + F250ADE3288ED4910045FA7E /* PHGSession.h in Headers */ = {isa = PBXBuildFile; fileRef = F250ADDE288B3ED80045FA7E /* PHGSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; F2A54FB028772F97006C53FF /* FeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */; }; F2A54FB128772F97006C53FF /* FeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */; }; F2A54FB32877582E006C53FF /* PHGGroupPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = F2A54FB22877582E006C53FF /* PHGGroupPayload.m */; }; @@ -197,6 +200,9 @@ EADEB8FF1DED3711005322DA /* circle.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = circle.yml; sourceTree = ""; }; EADEB9001DED3711005322DA /* PostHog.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = PostHog.podspec; sourceTree = ""; }; EADEB9011DED3711005322DA /* Podfile.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile.lock; sourceTree = ""; }; + F250ADDE288B3ED80045FA7E /* PHGSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PHGSession.h; sourceTree = ""; }; + F250ADDF288B401B0045FA7E /* PHGSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PHGSession.m; sourceTree = ""; }; + F250ADE1288ED2E20045FA7E /* SessionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionTest.swift; sourceTree = ""; }; F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagTests.swift; sourceTree = ""; }; F2A54FB22877582E006C53FF /* PHGGroupPayload.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PHGGroupPayload.m; sourceTree = ""; }; F2A54FB42877583C006C53FF /* PHGGroupPayload.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PHGGroupPayload.h; sourceTree = ""; }; @@ -341,6 +347,7 @@ 6EEC1C702017EA370089C478 /* EndToEndTests.swift */, A31958EE2385AC3A00A47EFA /* SerializationTests.m */, F2A54FAF28772F97006C53FF /* FeatureFlagTests.swift */, + F250ADE1288ED2E20045FA7E /* SessionTest.swift */, ); indentWidth = 2; path = PostHogTests; @@ -376,6 +383,8 @@ BF8C89BCDF12D1B64E9475F1 /* PHGCrypto.h */, F2A54FB22877582E006C53FF /* PHGGroupPayload.m */, F2A54FB42877583C006C53FF /* PHGGroupPayload.h */, + F250ADDE288B3ED80045FA7E /* PHGSession.h */, + F250ADDF288B401B0045FA7E /* PHGSession.m */, ); path = Classes; sourceTree = ""; @@ -423,6 +432,7 @@ BF8C8AD1FBFBB6941DE29D3B /* PHGFileStorage.h in Headers */, BF8C845A65AB5AD06E1EE26B /* PHGStorage.h in Headers */, BF8C890FADEC88022E7A3588 /* NSData+PHGGZIP.h in Headers */, + F250ADE3288ED4910045FA7E /* PHGSession.h in Headers */, BF8C8181F96E1C8CB86D783D /* PHGPostHogUtils.h in Headers */, BF8C8FCEC80B47BF2236236E /* PHGUtils.h in Headers */, BF8C8DCC18E3AA39CCB330BC /* PHGStoreKitCapturer.h in Headers */, @@ -713,6 +723,7 @@ BF8C8A634BCECED539A18D57 /* PHGPostHogIntegration.m in Sources */, BF8C8735F5CF80C193F5AEDF /* PHGAliasPayload.m in Sources */, BF8C8374E0D8F97469715282 /* PHGPayload.m in Sources */, + F250ADE0288B401B0045FA7E /* PHGSession.m in Sources */, BF8C86ED787477703D8BCD6C /* PHGScreenPayload.m in Sources */, BF8C8334DB2564A7CFA7A87A /* PHGPayloadManager.m in Sources */, BF8C87E4B70CA478721EB11D /* PHGCapturePayload.m in Sources */, @@ -726,6 +737,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F250ADE2288ED2E20045FA7E /* SessionTest.swift in Sources */, EADEB8F51DECD335005322DA /* CryptoTest.swift in Sources */, EADEB8F21DECD335005322DA /* UserDefaultsStorageTest.swift in Sources */, EAA542771EB4035400945DA7 /* CapturingTests.swift in Sources */, diff --git a/PostHog/Classes/PHGSession.h b/PostHog/Classes/PHGSession.h new file mode 100644 index 0000000000..c14272ffb5 --- /dev/null +++ b/PostHog/Classes/PHGSession.h @@ -0,0 +1,14 @@ +#import + +@class PHGSession; + + +@interface PHGSession : NSObject + +- (instancetype _Nonnull)init; +- (NSString *)getId; + +- (void)checkAndSetSessionId:(NSTimeInterval)timestamp; +- (void)checkAndSetSessionId; + +@end diff --git a/PostHog/Classes/PHGSession.m b/PostHog/Classes/PHGSession.m new file mode 100644 index 0000000000..77814805bf --- /dev/null +++ b/PostHog/Classes/PHGSession.m @@ -0,0 +1,56 @@ +// +// PHGSession.m +// PostHog +// +// Created by Eric Duong on 7/22/22. +// Copyright © 2022 PostHog. All rights reserved. +// + +#import +#import "PHGSession.h" +#import "PHGPostHogUtils.h" + +static int const SESSION_CHANGE_THRESHOLD = 1800; + +@interface PHGSession () + +@property (nonatomic, copy) NSString *sessionId; +@property (nonatomic) NSTimeInterval sessionStartTimestamp; + +@end + +@implementation PHGSession + +- (instancetype _Nonnull)init +{ + if (self = [super init]) { + self.sessionId = nil; + self.sessionStartTimestamp = 0; + } + return self; +} + +- (void)checkAndSetSessionId +{ + NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; + [self checkAndSetSessionId:now]; +} + +- (void)checkAndSetSessionId:(NSTimeInterval)timestamp +{ + NSLog(@"%f", timestamp); + if (self.sessionId == nil || self.sessionStartTimestamp == 0 || fabs(timestamp - self.sessionStartTimestamp) > SESSION_CHANGE_THRESHOLD) { + NSLog(@"%f", fabs(timestamp - self.sessionStartTimestamp)); + NSString *newSessionId = createUUIDString(); + self.sessionId = newSessionId; + self.sessionStartTimestamp = timestamp; + } +} + +- (NSString *)getId +{ + [self checkAndSetSessionId]; + return self.sessionId; +} + +@end diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index c4e9217b93..705f13b2c1 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -13,6 +13,7 @@ #import "PHGScreenPayload.h" #import "PHGAliasPayload.h" #import "PHGGroupPayload.h" +#import "PHGSession.h" #if TARGET_OS_IOS #import @@ -54,6 +55,7 @@ @interface PHGPostHogIntegration () @property (nonatomic, strong) NSURLSessionUploadTask *batchRequest; @property (nonatomic, assign) UIBackgroundTaskIdentifier flushTaskID; @property (nonatomic, strong) PHGReachability *reachability; +@property (nonatomic, strong) PHGSession *sessionManager; @property (nonatomic, strong) NSTimer *flushTimer; @property (nonatomic, strong) dispatch_queue_t serialQueue; @property (nonatomic, strong) dispatch_queue_t backgroundTaskQueue; @@ -79,6 +81,7 @@ - (id)initWithPostHog:(PHGPostHog *)posthog httpClient:(PHGHTTPClient *)httpClie self.fileStorage = fileStorage; self.userDefaultsStorage = userDefaultsStorage; self.distinctId = [self getDistinctId]; + self.sessionManager = [PHGSession init]; self.reachability = [PHGReachability reachabilityWithHostname:@"google.com"]; [self.reachability startNotifier]; self.cachedStaticContext = [self staticContext]; @@ -226,6 +229,7 @@ - (NSDictionary *)liveContext context[@"$groups"] = [self getGroups]; context[@"$active_feature_flags"] = [self getFeatureFlags]; + context[@"$session_id"] = [self.sessionManager getId]; NSDictionary *flagsAndValues = [self getFeatureFlagsAndValues]; diff --git a/PostHog/PostHog.h b/PostHog/PostHog.h index 40f03dc8cf..29a48eb6de 100644 --- a/PostHog/PostHog.h +++ b/PostHog/PostHog.h @@ -10,3 +10,4 @@ FOUNDATION_EXPORT const unsigned char PostHogVersionString[]; #import "PHGPostHogIntegration.h" #import "PHGContext.h" #import "PHGMiddleware.h" +#import "PHGSession.h" diff --git a/PostHogTests/SessionTest.swift b/PostHogTests/SessionTest.swift new file mode 100644 index 0000000000..7914d63c05 --- /dev/null +++ b/PostHogTests/SessionTest.swift @@ -0,0 +1,37 @@ +import Quick +import Nimble +import PostHog + +class SessionTest: QuickSpec { + override func spec() { + + var sessionHandler: PHGSession! + + beforeEach { + sessionHandler = PHGSession() + } + + it("Generates a session id") { + let sessionId = sessionHandler.getId() + expect(sessionId).toNot(beNil()) + + let anotherSessionId = sessionHandler.getId() + expect(anotherSessionId).to(equal(sessionId)) + + } + + it("Generates a new session id after refresh") { + let sessionId = sessionHandler.getId() + expect(sessionId).toNot(beNil()) + + let timeInterval = Date().addingTimeInterval(TimeInterval(40.0 * 60.0)).timeIntervalSince1970 + sessionHandler.checkAndSetSessionId(timeInterval) + + let anotherSessionId = sessionHandler.getId() + expect(anotherSessionId).toNot(equal(sessionId)) + + } + + } + +} From 9ad2dbf352df803b6f47d09c7bcb2258bf232eb4 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 26 Jul 2022 17:08:33 -0400 Subject: [PATCH 26/28] make adjustments and add persistence --- PostHog/Classes/PHGSession.h | 5 +- PostHog/Classes/PHGSession.m | 66 +++++++++++++++++++---- PostHog/Internal/PHGFileStorage.m | 15 ++++++ PostHog/Internal/PHGPostHogIntegration.m | 2 +- PostHog/Internal/PHGStorage.h | 5 +- PostHog/Internal/PHGUserDefaultsStorage.m | 21 ++++++++ 6 files changed, 99 insertions(+), 15 deletions(-) diff --git a/PostHog/Classes/PHGSession.h b/PostHog/Classes/PHGSession.h index c14272ffb5..c00477f3f6 100644 --- a/PostHog/Classes/PHGSession.h +++ b/PostHog/Classes/PHGSession.h @@ -1,12 +1,13 @@ #import +#import "PHGStorage.h" @class PHGSession; @interface PHGSession : NSObject -- (instancetype _Nonnull)init; -- (NSString *)getId; +- (instancetype _Nonnull)initWithStorage:(id)fileStorage userDefaultsStorage:(id)userDefaultsStorage; +- (NSString *_Nullable)getId; - (void)checkAndSetSessionId:(NSTimeInterval)timestamp; - (void)checkAndSetSessionId; diff --git a/PostHog/Classes/PHGSession.m b/PostHog/Classes/PHGSession.m index 77814805bf..8be3752211 100644 --- a/PostHog/Classes/PHGSession.m +++ b/PostHog/Classes/PHGSession.m @@ -9,23 +9,31 @@ #import #import "PHGSession.h" #import "PHGPostHogUtils.h" +#import "PHGStorage.h" static int const SESSION_CHANGE_THRESHOLD = 1800; +static NSString *const PHGSessionId = @"PHGSessionId"; +static NSString *const kPHGSessionId = @"posthog.sessionId"; + +static NSString *const PHGSessionLastTimestamp = @"PHGSessionLastTimestamp"; +static NSString *const kPHGSessionLastTimestamp = @"posthog.sessionlastTimestamp"; + @interface PHGSession () -@property (nonatomic, copy) NSString *sessionId; -@property (nonatomic) NSTimeInterval sessionStartTimestamp; +@property (nonatomic, strong) id fileStorage; +@property (nonatomic, strong) id userDefaultsStorage; @end @implementation PHGSession -- (instancetype _Nonnull)init +- (instancetype _Nonnull)initWithStorage:(id)fileStorage userDefaultsStorage:(id)userDefaultsStorage; { if (self = [super init]) { - self.sessionId = nil; - self.sessionStartTimestamp = 0; + self.fileStorage = fileStorage; + self.userDefaultsStorage = userDefaultsStorage; + } return self; } @@ -38,19 +46,55 @@ - (void)checkAndSetSessionId - (void)checkAndSetSessionId:(NSTimeInterval)timestamp { - NSLog(@"%f", timestamp); - if (self.sessionId == nil || self.sessionStartTimestamp == 0 || fabs(timestamp - self.sessionStartTimestamp) > SESSION_CHANGE_THRESHOLD) { - NSLog(@"%f", fabs(timestamp - self.sessionStartTimestamp)); + NSString *sessionId = [self getSessionId]; + NSTimeInterval sessionLastTimestamp = [self getSessionLastTimestamp]; + if (sessionId == nil || sessionLastTimestamp == 0 || fabs(timestamp - sessionLastTimestamp) > SESSION_CHANGE_THRESHOLD) { NSString *newSessionId = createUUIDString(); - self.sessionId = newSessionId; - self.sessionStartTimestamp = timestamp; + [self saveSessionId:newSessionId sessionLastTimestamp:timestamp]; } } +- (void)saveSessionId:(NSString *)sessionId sessionLastTimestamp:(NSTimeInterval)sessionLastTimestamp +{ + +#if TARGET_OS_TV + [self.userDefaultsStorage setString:sessionId forKey:PHGSessionId]; +#else + [self.fileStorage setString:sessionId forKey:kPHGSessionId]; +#endif + + NSNumber* nsSessionLastTimestamp = [NSNumber numberWithDouble:sessionLastTimestamp]; +#if TARGET_OS_TV + [self.userDefaultsStorage setString:distinctId forKey:PHGSessionLastTimestamp]; +#else + [self.fileStorage setNumber:nsSessionLastTimestamp forKey:kPHGSessionLastTimestamp]; +#endif +} + +- (NSString *)getSessionId +{ +#if TARGET_OS_TV + return [[NSUserDefaults standardUserDefaults] valueForKey:PHGSessionId]; +#else + return [self.fileStorage stringForKey:kPHGSessionId]; +#endif +} + +- (NSTimeInterval)getSessionLastTimestamp +{ + NSNumber *timestamp; +#if TARGET_OS_TV + timestamp = [[NSUserDefaults standardUserDefaults] numberForKey:PHGSessionLastTimestamp]; +#else + timestamp = [self.fileStorage numberForKey:kPHGSessionLastTimestamp]; +#endif + return [timestamp doubleValue]; +} + - (NSString *)getId { [self checkAndSetSessionId]; - return self.sessionId; + return [self getSessionId]; } @end diff --git a/PostHog/Internal/PHGFileStorage.m b/PostHog/Internal/PHGFileStorage.m index 3e4cb91d7b..a99421b9fa 100644 --- a/PostHog/Internal/PHGFileStorage.m +++ b/PostHog/Internal/PHGFileStorage.m @@ -114,6 +114,21 @@ - (void)setString:(nullable NSString *)string forKey:(NSString *)key [self setJSON:string forKey:key]; } +- (nullable NSNumber *)numberForKey:(NSString *)key +{ + NSDictionary *data = [self jsonForKey:key]; + if (data) { + return data[key]; + } + return nil; +} + + +- (void)setNumber:(nullable NSNumber *)number forKey:(NSString *)key +{ + [self setJSON:number forKey:key]; +} + + (NSURL *)applicationSupportDirectoryURL { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index 705f13b2c1..df77f540d0 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -81,7 +81,7 @@ - (id)initWithPostHog:(PHGPostHog *)posthog httpClient:(PHGHTTPClient *)httpClie self.fileStorage = fileStorage; self.userDefaultsStorage = userDefaultsStorage; self.distinctId = [self getDistinctId]; - self.sessionManager = [PHGSession init]; + self.sessionManager = [PHGSession initWithStorage:fileStorage userDefaultsStorage:userDefaultsStorage]; self.reachability = [PHGReachability reachabilityWithHostname:@"google.com"]; [self.reachability startNotifier]; self.cachedStaticContext = [self staticContext]; diff --git a/PostHog/Internal/PHGStorage.h b/PostHog/Internal/PHGStorage.h index 7874db4e6d..d651b0362a 100644 --- a/PostHog/Internal/PHGStorage.h +++ b/PostHog/Internal/PHGStorage.h @@ -20,6 +20,9 @@ - (void)setString:(NSString *_Nullable)string forKey:(NSString *_Nonnull)key; - (NSString *_Nullable)stringForKey:(NSString *_Nonnull)key; -// Number and Booleans are intentionally omitted at the moment because they are not needed +- (void)setNumber:(NSNumber *_Nullable)number forKey:(NSString *_Nonnull)key; +- (nullable NSNumber *)numberForKey:(NSString *_Nonnull)key; + +// Booleans are intentionally omitted at the moment because they are not needed @end diff --git a/PostHog/Internal/PHGUserDefaultsStorage.m b/PostHog/Internal/PHGUserDefaultsStorage.m index f6afc4eb73..8f0be8b51c 100644 --- a/PostHog/Internal/PHGUserDefaultsStorage.m +++ b/PostHog/Internal/PHGUserDefaultsStorage.m @@ -130,6 +130,27 @@ - (void)setString:(NSString *)string forKey:(NSString *)key [self setPlist:string forKey:key]; } +- (NSNumber *)numberForKey:(NSString *)key +{ + if (!self.crypto) { + key = [self namespacedKey:key]; + + double value = [self.defaults doubleForKey:key]; + return [NSNumber numberWithDouble:value]; + } + return [self plistForKey:key]; +} + +- (void)setNumber:(NSNumber *)number forKey:(NSString *)key +{ + if (!self.crypto) { + key = [self namespacedKey:key]; + [self setObject:number forKey:key]; + return; + } + [self setPlist:number forKey:key]; +} + #pragma mark - Helpers - (id _Nullable)plistForKey:(NSString *)key From a3b56b255e78a7dd810eececfc51358561555825 Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 27 Jul 2022 12:41:52 -0400 Subject: [PATCH 27/28] fix test --- PostHog/Classes/PHGSession.h | 2 +- PostHog/Internal/PHGPostHogIntegration.m | 2 +- PostHogTests/SessionTest.swift | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/PostHog/Classes/PHGSession.h b/PostHog/Classes/PHGSession.h index c00477f3f6..521907490b 100644 --- a/PostHog/Classes/PHGSession.h +++ b/PostHog/Classes/PHGSession.h @@ -6,7 +6,7 @@ @interface PHGSession : NSObject -- (instancetype _Nonnull)initWithStorage:(id)fileStorage userDefaultsStorage:(id)userDefaultsStorage; +- (instancetype _Nonnull)initWithStorage:(id _Nonnull)fileStorage userDefaultsStorage:(id _Nonnull)userDefaultsStorage; - (NSString *_Nullable)getId; - (void)checkAndSetSessionId:(NSTimeInterval)timestamp; diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index df77f540d0..c792a88c8b 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -81,7 +81,7 @@ - (id)initWithPostHog:(PHGPostHog *)posthog httpClient:(PHGHTTPClient *)httpClie self.fileStorage = fileStorage; self.userDefaultsStorage = userDefaultsStorage; self.distinctId = [self getDistinctId]; - self.sessionManager = [PHGSession initWithStorage:fileStorage userDefaultsStorage:userDefaultsStorage]; + self.sessionManager = [[PHGSession alloc] initWithStorage:fileStorage userDefaultsStorage:userDefaultsStorage]; self.reachability = [PHGReachability reachabilityWithHostname:@"google.com"]; [self.reachability startNotifier]; self.cachedStaticContext = [self staticContext]; diff --git a/PostHogTests/SessionTest.swift b/PostHogTests/SessionTest.swift index 7914d63c05..c063d7d928 100644 --- a/PostHogTests/SessionTest.swift +++ b/PostHogTests/SessionTest.swift @@ -6,9 +6,17 @@ class SessionTest: QuickSpec { override func spec() { var sessionHandler: PHGSession! + var storage : PHGFileStorage! + var userDefaultsStorage : PHGUserDefaultsStorage! beforeEach { - sessionHandler = PHGSession() + let url = PHGFileStorage.applicationSupportDirectoryURL() + expect(url).toNot(beNil()) + expect(url?.pathComponents[url!.pathComponents.count - 2]) == "Application Support" + expect(url?.lastPathComponent) == Bundle.main.bundleIdentifier + storage = PHGFileStorage(folder: url!, crypto: nil) + userDefaultsStorage = PHGUserDefaultsStorage(defaults: UserDefaults.standard, namespacePrefix: nil, crypto: nil) + sessionHandler = PHGSession(storage: storage, userDefaultsStorage: userDefaultsStorage) } it("Generates a session id") { From 4d8c162b7674b7efce4055088bf598e371c85e75 Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 27 Jul 2022 20:56:24 -0400 Subject: [PATCH 28/28] reset session --- PostHog/Classes/PHGSession.h | 1 + PostHog/Classes/PHGSession.m | 11 +++++++++++ PostHog/Internal/PHGPostHogIntegration.m | 1 + 3 files changed, 13 insertions(+) diff --git a/PostHog/Classes/PHGSession.h b/PostHog/Classes/PHGSession.h index 521907490b..2e39356164 100644 --- a/PostHog/Classes/PHGSession.h +++ b/PostHog/Classes/PHGSession.h @@ -11,5 +11,6 @@ - (void)checkAndSetSessionId:(NSTimeInterval)timestamp; - (void)checkAndSetSessionId; +- (void)resetSession; @end diff --git a/PostHog/Classes/PHGSession.m b/PostHog/Classes/PHGSession.m index 8be3752211..588c584fe0 100644 --- a/PostHog/Classes/PHGSession.m +++ b/PostHog/Classes/PHGSession.m @@ -97,4 +97,15 @@ - (NSString *)getId return [self getSessionId]; } +- (void)resetSession +{ +#if TARGET_OS_TV + [self.userDefaultsStorage removeKey:PHGSessionId]; + [self.userDefaultsStorage removeKey:PHGSessionLastTimestamp]; +#else + [self.fileStorage removeKey:kPHGSessionId]; + [self.fileStorage removeKey:kPHGSessionLastTimestamp]; +#endif +} + @end diff --git a/PostHog/Internal/PHGPostHogIntegration.m b/PostHog/Internal/PHGPostHogIntegration.m index c792a88c8b..1d167a6282 100644 --- a/PostHog/Internal/PHGPostHogIntegration.m +++ b/PostHog/Internal/PHGPostHogIntegration.m @@ -510,6 +510,7 @@ - (void)reset #else [self.fileStorage removeKey:kPHGDistinctIdFilename]; #endif + [self.sessionManager resetSession]; self.distinctId = nil; }]; }