diff --git a/Glassfy.podspec b/Glassfy.podspec index b705f94..c0b2a45 100644 --- a/Glassfy.podspec +++ b/Glassfy.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Glassfy" - s.version = "1.3.4" + s.version = "1.3.5" s.summary = "Subscription and in-app-purchase service." s.license = { :type => 'MIT', :file => 'LICENSE' } s.source = { :git => "https://github.com/glassfy/ios-sdk.git", :tag => s.version.to_s } diff --git a/Glassfy.xcodeproj/project.pbxproj b/Glassfy.xcodeproj/project.pbxproj index 56004ca..68ef255 100644 --- a/Glassfy.xcodeproj/project.pbxproj +++ b/Glassfy.xcodeproj/project.pbxproj @@ -100,6 +100,9 @@ 81D8D49A258B7193006BFE51 /* Glassfy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8168309F258B6D9C00565968 /* Glassfy.framework */; }; 81E1DC3225933FC7003B5EBD /* SKPayment+GYEncode.h in Headers */ = {isa = PBXBuildFile; fileRef = 81E1DC3025933FC7003B5EBD /* SKPayment+GYEncode.h */; settings = {ATTRIBUTES = (Private, ); }; }; 81E1DC3325933FC7003B5EBD /* SKPayment+GYEncode.m in Sources */ = {isa = PBXBuildFile; fileRef = 81E1DC3125933FC7003B5EBD /* SKPayment+GYEncode.m */; }; + 81E5B8942912C87600D134E9 /* GYAttributionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 81E5B8932912C87600D134E9 /* GYAttributionItem.m */; }; + 81E5B8962912C88400D134E9 /* GYAttributionItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 81E5B8952912C88400D134E9 /* GYAttributionItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81E5B89A2912CEBC00D134E9 /* GYAttributionItem+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 81E5B8972912CE9700D134E9 /* GYAttributionItem+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 81EFB54425A772D900706FCB /* GYAPIPermissionsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 81EFB54225A772D900706FCB /* GYAPIPermissionsResponse.h */; settings = {ATTRIBUTES = (Private, ); }; }; 81EFB54525A772D900706FCB /* GYAPIPermissionsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 81EFB54325A772D900706FCB /* GYAPIPermissionsResponse.m */; }; 81EFB54B25A772F200706FCB /* GYPermission.m in Sources */ = {isa = PBXBuildFile; fileRef = 81EFB54925A772F200706FCB /* GYPermission.m */; }; @@ -213,6 +216,9 @@ 81D8D499258B7193006BFE51 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81E1DC3025933FC7003B5EBD /* SKPayment+GYEncode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SKPayment+GYEncode.h"; sourceTree = ""; }; 81E1DC3125933FC7003B5EBD /* SKPayment+GYEncode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SKPayment+GYEncode.m"; sourceTree = ""; }; + 81E5B8932912C87600D134E9 /* GYAttributionItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GYAttributionItem.m; sourceTree = ""; }; + 81E5B8952912C88400D134E9 /* GYAttributionItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GYAttributionItem.h; path = Public/GYAttributionItem.h; sourceTree = ""; }; + 81E5B8972912CE9700D134E9 /* GYAttributionItem+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GYAttributionItem+Private.h"; sourceTree = ""; }; 81EFB54225A772D900706FCB /* GYAPIPermissionsResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GYAPIPermissionsResponse.h; sourceTree = ""; }; 81EFB54325A772D900706FCB /* GYAPIPermissionsResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GYAPIPermissionsResponse.m; sourceTree = ""; }; 81EFB54925A772F200706FCB /* GYPermission.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GYPermission.m; sourceTree = ""; }; @@ -308,6 +314,8 @@ children = ( 81951CD026443F65003901F1 /* Glassfy.h */, 817A1C75258CE0A200D1BA10 /* Glassfy.m */, + 81E5B8952912C88400D134E9 /* GYAttributionItem.h */, + 81E5B8932912C87600D134E9 /* GYAttributionItem.m */, 81951CD526443F94003901F1 /* GYOfferings.h */, 810F6FB225E50CFF00D3009E /* GYOfferings.m */, 81951CD426443F94003901F1 /* GYOffering.h */, @@ -371,6 +379,7 @@ 81C9AF672805D2F3004A36A1 /* GYStoresInfo+Private.h */, 81C9AF542805BA8A004A36A1 /* GYStoreInfo+Private.h */, 8166503F28DB689200E5B985 /* GYAccountableSku+Private.h */, + 81E5B8972912CE9700D134E9 /* GYAttributionItem+Private.h */, ); name = Core; sourceTree = ""; @@ -417,6 +426,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 81E5B8962912C88400D134E9 /* GYAttributionItem.h in Headers */, 81C9AF5F2805BB82004A36A1 /* GYStoresInfo.h in Headers */, 811DB39F280975C900021880 /* GYSkuBase.h in Headers */, 811BCDDF2608DA90007742D9 /* GYSysInfo.h in Headers */, @@ -427,6 +437,7 @@ 81010F292590C1AB00B07DE4 /* GYAPIOfferingsResponse.h in Headers */, 81951CD726443F94003901F1 /* GYOfferings.h in Headers */, 8138BAB225924D0A005CB44E /* SKProductSubscriptionPeriod+GYEncode.h in Headers */, + 81E5B89A2912CEBC00D134E9 /* GYAttributionItem+Private.h in Headers */, 81D18D7D2812AFE100C68558 /* GYSkuPaddle+Private.h in Headers */, 81FCCE8627C3CE0A00DF3D1A /* GYPurchaseDelegate.h in Headers */, 817A1CF5258CF63600D1BA10 /* GYAPIManager.h in Headers */, @@ -591,6 +602,7 @@ 811DB39D280972C900021880 /* GYSkuBase.m in Sources */, 8179459126565E32000EAA47 /* GYUtils.m in Sources */, 81C9AF5C2805BB5B004A36A1 /* GYStoresInfo.m in Sources */, + 81E5B8942912C87600D134E9 /* GYAttributionItem.m in Sources */, 81C9AF582805BAB1004A36A1 /* GYStoreInfo.m in Sources */, 81C684662602633500223D3E /* GYAPISignatureResponse.m in Sources */, 817A1C3E258CC9EA00D1BA10 /* GYOffering.m in Sources */, @@ -777,7 +789,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.3.4; + MARKETING_VERSION = 1.3.5; PRODUCT_BUNDLE_IDENTIFIER = net.glassfy.sdk; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -805,7 +817,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.3.4; + MARKETING_VERSION = 1.3.5; PRODUCT_BUNDLE_IDENTIFIER = net.glassfy.sdk; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/README.md b/README.md index 85157c8..b3a4850 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Our SDK can be easly integrated through Cocoapods or Swift Package Manager #### Integrate using Cocoapods Add the pod to your Podfile: -```pod 'Glassfy', '~> 1.3.4'``` +```pod 'Glassfy', '~> 1.3.5'``` Then, run the following command: diff --git a/Source/GYAPIManager.h b/Source/GYAPIManager.h index 5cfd567..5c90612 100644 --- a/Source/GYAPIManager.h +++ b/Source/GYAPIManager.h @@ -19,6 +19,7 @@ @class SKProduct; @class SKPaymentTransaction; @class GYCacheManager; +@class GYAttributionItem; typedef void(^GYGetInitCompletion)(GYAPIInitResponse* _Nullable, NSError* _Nullable); typedef void(^GYGetPermissionsCompletion)(GYAPIPermissionsResponse* _Nullable, NSError* _Nullable); @@ -35,6 +36,7 @@ typedef GYBaseCompletion GYLogoutCompletion; typedef GYBaseCompletion GYLoginCompletion; typedef GYBaseCompletion GYPropertyCompletion; typedef GYBaseCompletion GYGetConnectCompletion; +typedef GYBaseCompletion GYPostAttributionsCompletion; NS_ASSUME_NONNULL_BEGIN @@ -75,6 +77,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)getPaywall:(NSString *)paywallId locale:(NSString *_Nullable)locale completion:(GYGetPaywallCompletion _Nullable)block; - (void)putLastSeen; + +- (void)postAttributions:(NSArray *)items withCompletion:(GYPostAttributionsCompletion _Nullable)block; + @end NS_ASSUME_NONNULL_END diff --git a/Source/GYAPIManager.m b/Source/GYAPIManager.m index 47f077b..deaa1c1 100644 --- a/Source/GYAPIManager.m +++ b/Source/GYAPIManager.m @@ -11,6 +11,7 @@ #import "GYSku+Private.h" #import "SKPaymentTransaction+GYEncode.h" #import "SKProduct+GYEncode.h" +#import "GYAttributionItem+Private.h" #import "GYSysInfo.h" #import "GYAPIManager.h" #import "GYError.h" @@ -449,6 +450,41 @@ - (void)getPaywall:(NSString *)paywallId locale:(NSString *)locale completion:(G [self callApiWithRequest:req response:GYAPIPaywallResponse.class completion:block]; } +- (void)postAttributions:(NSArray *)items withCompletion:(GYPostAttributionsCompletion _Nullable)block +{ + NSURLComponents *url = [self baseURLV0]; + url.path = [url.path stringByAppendingPathComponent:@"attribution"]; + + NSMutableDictionary *bodyEncoded = [NSMutableDictionary new]; + for (GYAttributionItem *item in items) { + [bodyEncoded addEntriesFromDictionary:[item encodedObject]]; + } + + NSError *err; + NSData *body; + if (![NSJSONSerialization isValidJSONObject:bodyEncoded]) { + err = GYError.encodeData; + } + else { + body = [NSJSONSerialization dataWithJSONObject:bodyEncoded options:kNilOptions error:&err]; + } + if (err) { + dispatch_async(Glassfy.shared.glqueue, ^{ + GYGetConnectCompletion completion = block; + if (completion) { + completion(nil, err); + } + }); + return; + } + + NSMutableURLRequest *req = [self authorizedRequestWithComponents:url]; + [req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [req setHTTPMethod:@"POST"]; + [req setHTTPBody:body]; + [self callApiWithRequest:req response:GYAPIBaseResponse.class completion:block]; +} + #pragma mark - private - (NSMutableURLRequest *_Nullable)authorizedRequestWithComponents:(NSURLComponents *)urlComponents diff --git a/Source/GYAttributionItem+Private.h b/Source/GYAttributionItem+Private.h new file mode 100644 index 0000000..88a8287 --- /dev/null +++ b/Source/GYAttributionItem+Private.h @@ -0,0 +1,17 @@ +// +// GYAttributionItem+Private.h +// Glassfy +// +// Created by Luca Garbolino on 02/11/22. +// + +#import "GYAttributionItem.h" +#import "GYCodableProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GYAttributionItem (Private) +@end + +NS_ASSUME_NONNULL_END + diff --git a/Source/GYAttributionItem.m b/Source/GYAttributionItem.m new file mode 100644 index 0000000..6ae212f --- /dev/null +++ b/Source/GYAttributionItem.m @@ -0,0 +1,53 @@ +// +// GYAttributionItem.h +// Glassfy +// +// Created by Luca Garbolino on 02/11/22. +// + +#import "GYAttributionItem+Private.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GYAttributionItem() +@property(nonatomic, strong, nullable) NSString *value; +@property(nonatomic, assign) GYAttributionType type; +@end + +@implementation GYAttributionItem (Private) + +- (id)encodedObject +{ + NSMutableDictionary *attributionInfo = [NSMutableDictionary dictionary]; + NSString *nameStr = [self.class attributionTypeToString:self.type]; + attributionInfo[nameStr] = self.value ?: [NSNull null]; + + return [NSDictionary dictionaryWithDictionary:attributionInfo]; +} + ++ (NSString *)attributionTypeToString:(GYAttributionType)type { + switch (type) { + case GYAttributionTypeAdjustID: return @"adjustid"; + case GYAttributionTypeAppsFlyerID: return @"appsflyerid"; + case GYAttributionTypeIDFA: return @"idfa"; + case GYAttributionTypeIDFV: return @"idfv"; + case GYAttributionTypeIP: return @"ip"; + } +} + +@end + +@implementation GYAttributionItem + ++ (instancetype)attributionItemWithType:(GYAttributionType)type value:(NSString *_Nullable)value +{ + GYAttributionItem *item = [GYAttributionItem new]; + item.type = type; + item.value = value; + + return item; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/GYManager.h b/Source/GYManager.h index c80067a..fcd629c 100644 --- a/Source/GYManager.h +++ b/Source/GYManager.h @@ -9,7 +9,7 @@ #import "GYTypes.h" #import "GYPurchaseDelegate.h" @class GYSku; - +@class GYAttributionItem; NS_ASSUME_NONNULL_BEGIN @@ -48,6 +48,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)storeInfo:(GYStoreCompletion)block; +- (void)setAttributionWithType:(GYAttributionType)type value:(NSString *_Nullable)value completion:(GYErrorCompletion)block; +- (void)setAttributions:(NSArray *)attributions completion:(GYErrorCompletion)block; + @end NS_ASSUME_NONNULL_END diff --git a/Source/GYManager.m b/Source/GYManager.m index c79c307..8c12843 100644 --- a/Source/GYManager.m +++ b/Source/GYManager.m @@ -413,6 +413,17 @@ - (void)storeInfo:(GYStoreCompletion)block }]; } +- (void)setAttributionWithType:(GYAttributionType)type value:(NSString *_Nullable)value completion:(GYErrorCompletion)block +{ + GYAttributionItem *item = [GYAttributionItem attributionItemWithType:type value:value]; + [self setAttributions:@[item] completion:block]; +} + +- (void)setAttributions:(NSArray*)attributions completion:(GYErrorCompletion)block +{ + [self setAttributions:attributions maxRetries:10 completion:block]; +} + #pragma mark - Notification @@ -818,5 +829,31 @@ - (void)setUserProperty:(GYUserPropertyType)property value:(id)obj completion:(G }]; } +- (void)setAttributions:(NSArray*)attributions maxRetries:(NSUInteger)times completion:(GYErrorCompletion)block +{ + typeof(self) __weak weakSelf = self; + GYPostAttributionsCompletion apiCompletion = ^(GYAPIBaseResponse *res, NSError *err) { + if (err && times > 0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8f * NSEC_PER_SEC)), Glassfy.shared.glqueue, ^{ + [weakSelf setAttributions:attributions maxRetries:(times-1) completion:block]; + }); + } + else { + typeof(block) __strong completion = block; + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(err); + }); + } + } + }; + + if (!self.initialized) { + apiCompletion(nil, GYError.sdkNotInitialized); + return; + } + + [self.api postAttributions:attributions withCompletion:apiCompletion]; +} @end diff --git a/Source/GYPaywallViewController+Private.h b/Source/GYPaywallViewController+Private.h index 5ad9052..a7ba087 100644 --- a/Source/GYPaywallViewController+Private.h +++ b/Source/GYPaywallViewController+Private.h @@ -1,5 +1,5 @@ // -// GYPaywallViewController.h +// GYPaywallViewController+Private.h // Glassfy // // Created by Luca Garbolino on 13/10/21. diff --git a/Source/GYStoreInfo+Private.h b/Source/GYStoreInfo+Private.h index 86e82bc..29f5391 100644 --- a/Source/GYStoreInfo+Private.h +++ b/Source/GYStoreInfo+Private.h @@ -1,5 +1,5 @@ // -// GYStoreInfo+Private.h.h +// GYStoreInfo+Private.h // Glassfy // // Created by Luca Garbolino on 12/04/22. diff --git a/Source/GYStoresInfo+Private.h b/Source/GYStoresInfo+Private.h index f5cc78c..8a06234 100644 --- a/Source/GYStoresInfo+Private.h +++ b/Source/GYStoresInfo+Private.h @@ -1,5 +1,5 @@ // -// GYStoreInfo+Private.h.h +// GYStoreInfo+Private.h // Glassfy // // Created by Luca Garbolino on 12/04/22. diff --git a/Source/Glassfy.m b/Source/Glassfy.m index 4517576..ace57d9 100644 --- a/Source/Glassfy.m +++ b/Source/Glassfy.m @@ -40,7 +40,7 @@ + (Glassfy *)shared + (NSString *)sdkVersion { - return @"1.3.4"; + return @"1.3.5"; } + (void)initializeWithAPIKey:(NSString *)apiKey @@ -200,6 +200,19 @@ + (void)storeInfo:(GYStoreCompletion)block }); } ++ (void)setAttributionWithType:(GYAttributionType)type value:(NSString *)value completion:(GYErrorCompletion)block +{ + dispatch_async(Glassfy.shared.glqueue, ^{ + [Glassfy.shared.manager setAttributionWithType:type value:value completion:block]; + }); +} + ++ (void)setAttributions:(NSArray *)attributions completion:(GYErrorCompletion)block +{ + dispatch_async(Glassfy.shared.glqueue, ^{ + [Glassfy.shared.manager setAttributions:attributions completion:block]; + }); +} #pragma mark - Deprecations diff --git a/Source/Public/GYAttributionItem.h b/Source/Public/GYAttributionItem.h new file mode 100644 index 0000000..d8503f8 --- /dev/null +++ b/Source/Public/GYAttributionItem.h @@ -0,0 +1,24 @@ +// +// GYAttributionItem.h +// Glassfy +// +// Created by Luca Garbolino on 02/11/22. +// + +#import +#if __has_include() +#import +#else +#import "GYTypes.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(Glassfy.AttributionItem) +@interface GYAttributionItem : NSObject + ++ (instancetype)attributionItemWithType:(GYAttributionType)name value:(NSString *_Nullable)value; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Public/GYTypes.h b/Source/Public/GYTypes.h index 25db11f..1bd7ec5 100644 --- a/Source/Public/GYTypes.h +++ b/Source/Public/GYTypes.h @@ -97,6 +97,19 @@ typedef NS_ENUM(NSUInteger, GYStore) { GYStorePaddle = 3 } NS_SWIFT_NAME(Glassfy.Store); +typedef NS_ENUM(NSUInteger, GYAttributionType) { + // Unique Adjust ID of the device. Available after the installation has been successfully tracked. + GYAttributionTypeAdjustID, + // App identifier generated by AppsFlyer at installation. A new ID is generated if an app is deleted, then reinstalled. + GYAttributionTypeAppsFlyerID, + // Identifier For Advertisers - Unique to each device. Only use for advertising. + GYAttributionTypeIDFA, + // Identifier For Vendor - Uniquely identifies a device to the app’s vendor. + GYAttributionTypeIDFV, + // User IP address + GYAttributionTypeIP, +} NS_SWIFT_NAME(Glassfy.GYAttributionType); + NS_ASSUME_NONNULL_END #endif /* Types_h */ diff --git a/Source/Public/Glassfy.h b/Source/Public/Glassfy.h index 6833ed7..e7f50e3 100644 --- a/Source/Public/Glassfy.h +++ b/Source/Public/Glassfy.h @@ -25,6 +25,7 @@ #import #import #import +#import #else #import "GYSku.h" #import "GYSkuPaddle.h" @@ -42,6 +43,7 @@ #import "GYPurchaseDelegate.h" #import "GYStoresInfo.h" #import "GYStoreInfoPaddle.h" +#import "GYAttributionItem.h" #endif @@ -242,6 +244,23 @@ Save extra user properties */ + (void)storeInfo:(GYStoreCompletion)block NS_SWIFT_NAME(storeInfo(completion:)); +/** +Set attribution values + +@param type Attribution identifier +@param value Attribution value +@param block Completion block +*/ ++ (void)setAttributionWithType:(GYAttributionType)type value:(NSString *_Nullable)value completion:(GYErrorCompletion)block NS_SWIFT_NAME(setAttribution(type:value:completion:)); + +/** +Set attribution values + +@param attributions Array of AttributionItem +@param block Completion block +*/ ++ (void)setAttributions:(NSArray *)attributions completion:(GYErrorCompletion)block NS_SWIFT_NAME(setAttributions(_:completion:)); + /// Deprecations