Skip to content

Commit

Permalink
Various Improvements (#108)
Browse files Browse the repository at this point in the history
* facebook aem

* improvements for Flutter and purchases processing

* merge with master

* remove visionOS

* bump version 3.2.8

* disable automatic attribution extraction feature

* add more fields to ApphudProduct
  • Loading branch information
ren6 authored Feb 6, 2024
1 parent 93d7cc6 commit fcc4cce
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 35 deletions.
5 changes: 2 additions & 3 deletions ApphudSDK.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = 'ApphudSDK'
s.version = '3.2.7'
s.summary = 'Track and control iOS auto-renewable subscriptions.'
s.version = '3.2.8'
s.summary = 'Track and control iOS auto-renewable subscriptions.'
s.description = 'Track, control and analyze iOS auto-renewable subscriptions with Apphud.'
s.homepage = 'https://github.com/apphud/ApphudSDK'
s.license = { :type => 'MIT', :file => 'LICENSE' }
Expand All @@ -12,7 +12,6 @@ Pod::Spec.new do |s|
s.osx.deployment_target = '11.0'
s.tvos.deployment_target = '13.0'
s.watchos.deployment_target = '6.2'
s.visionos.deployment_target = '1.0'
s.swift_version = '5.0'
s.source_files = 'ApphudSDK/**/*.{swift,h,m}'

Expand Down
5 changes: 3 additions & 2 deletions ApphudSDK/Internal/ApphudAsyncStoreKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ internal class ApphudAsyncStoreKit {
}
}

@MainActor
internal func purchaseResult(product: Product, _ scene:Any? = nil, apphudProduct: ApphudProduct?, isPurchasing: Binding<Bool>? = nil) async -> ApphudAsyncPurchaseResult {
self.isPurchasing = true
await productsStorage.append(product)
Expand Down Expand Up @@ -120,14 +121,14 @@ internal class ApphudAsyncStoreKit {
self.isPurchasing = false
isPurchasing?.wrappedValue = false

return await ApphudInternal.shared.asyncPurchaseResult(product: product, transaction: transaction, error: nil)
return ApphudInternal.shared.asyncPurchaseResult(product: product, transaction: transaction, error: nil)

} catch {
ApphudLoggerService.shared.paywallPaymentError(paywallId: apphudProduct?.paywallId, placementId: apphudProduct?.placementId, productId: product.id, error: error.apphudErrorMessage())

self.isPurchasing = false
isPurchasing?.wrappedValue = false
return await ApphudInternal.shared.asyncPurchaseResult(product: product, transaction: nil, error: error)
return ApphudInternal.shared.asyncPurchaseResult(product: product, transaction: nil, error: error)
}
}

Expand Down
16 changes: 16 additions & 0 deletions ApphudSDK/Internal/ApphudInternal+Attribution.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ extension ApphudInternal {
if let customAttribution = data as? [String: Any] {
params.merge(customAttribution, uniquingKeysWith: { f, _ in f})
}
case .facebook:
guard identifer != nil, self.submittedFacebookAnonId != identifer else {
apphudLog("Facebook Anon ID is nil or didn't change, exiting", forceDisplay: true)
callback?(false)
return
}
params["fb_anon_id"] = identifer
if let customAttribution = data as? [String: Any] {
params.merge(customAttribution, uniquingKeysWith: { f, _ in f})
}
case .firebase:
guard identifer != nil, self.submittedFirebaseId != identifer else {
callback?(false)
Expand Down Expand Up @@ -146,6 +156,10 @@ extension ApphudInternal {
if result {
self.submittedFirebaseId = identifer
}
case .facebook:
if result {
self.submittedFacebookAnonId = identifer
}
case .appleAdsAttribution:
if result {
self.didSubmitAppleAdsAttribution = true
Expand All @@ -167,9 +181,11 @@ extension ApphudInternal {

@MainActor
@objc internal func forceSendAttributionDataIfNeeded() {
/* This functionality has been removed since 3.2.8
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(forceSendAttributionDataIfNeeded), object: nil)
automaticallySubmitAppsFlyerAttributionIfNeeded()
automaticallySubmitAdjustAttributionIfNeeded()
*/
}

@objc internal func getAppleAttribution(_ appleAttibutionToken: String) async -> [AnyHashable: Any]? {
Expand Down
1 change: 0 additions & 1 deletion ApphudSDK/Internal/ApphudInternal+Eligibility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ extension ApphudInternal {
// not found subscriptions, try to restore and try again

let didSendReceiptForIntroEligibility = "ReceiptForIntroSent"

if self.currentUser?.subscriptions.count ?? 0 == 0 && !UserDefaults.standard.bool(forKey: didSendReceiptForIntroEligibility) {
if let receiptString = apphudReceiptDataString() {
apphudLog("Restoring subscriptions for intro eligibility check")
Expand Down
4 changes: 2 additions & 2 deletions ApphudSDK/Internal/ApphudInternal+Product.swift
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ extension ApphudInternal {
}

paywalls.forEach { paywall in
paywall.update()
paywall.update(placementId: nil, placementIdentifier: nil)
}

placements.forEach { placement in
placement.paywall?.update(placementId: placement.id)
placement.paywall?.update(placementId: placement.id, placementIdentifier: placement.identifier)
}
}

Expand Down
41 changes: 21 additions & 20 deletions ApphudSDK/Internal/ApphudInternal+Purchase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ extension ApphudInternal {
transactionProductIdentifier: productId,
transactionState: transaction?.transactionState,
receiptString: receiptString,
notifyDelegate: eligibilityCheck,
notifyDelegate: notifyDelegate,
eligibilityCheck: eligibilityCheck,
callback: callback)
}
}
Expand Down Expand Up @@ -360,9 +361,11 @@ extension ApphudInternal {
} else {
self.scheduleSubmitReceiptRetry(error: error, code: errorCode)
}

self.submitReceiptCallbacks.forEach { callback in callback?(error)}
self.submitReceiptCallbacks.removeAll()

while !self.submitReceiptCallbacks.isEmpty {
let callback = self.submitReceiptCallbacks.removeFirst()
callback?(error)
}
}
}
}
Expand Down Expand Up @@ -396,23 +399,21 @@ extension ApphudInternal {
}

@MainActor internal func purchase(productId: String, product: ApphudProduct?, validate: Bool, value: Double? = nil, callback: ((ApphudPurchaseResult) -> Void)?) {
if let apphudProduct = product, let skProduct = apphudProduct.skProduct {
purchase(product: skProduct, apphudProduct: apphudProduct, validate: validate, value: value, callback: callback)

let skProduct = product?.skProduct ?? ApphudStoreKitWrapper.shared.products.first(where: { $0.productIdentifier == productId })

if let skProduct = skProduct {
purchase(product: skProduct, apphudProduct: product, validate: validate, value: value, callback: callback)
} else {
if let apphudProduct = ApphudInternal.shared.allAvailableProducts.first(where: { $0.productId == productId }), let skProduct = apphudProduct.skProduct, product != nil {
purchase(product: skProduct, apphudProduct: product!, validate: validate, value: value, callback: callback)
} else {
apphudLog("Product with id \(productId) not found, re-fetching from App Store...")
ApphudStoreKitWrapper.shared.fetchProducts(productIds: [productId]) { prds in
let product = prds?.first(where: { $0.productIdentifier == productId })
if let product = product {
self.purchase(product: product, apphudProduct: nil, validate: validate, value: value, callback: callback)
} else {
let message = "Unable to start payment because product identifier is invalid: [\([productId])]"
apphudLog(message, forceDisplay: true)
let result = ApphudPurchaseResult(nil, nil, nil, ApphudError(message: message))
callback?(result)
}
apphudLog("Product with id \(productId) not found, re-fetching from App Store...")
ApphudStoreKitWrapper.shared.fetchProducts(productIds: [productId]) { prds in
if let sk = prds?.first(where: { $0.productIdentifier == productId }) {
self.purchase(product: sk, apphudProduct: product, validate: validate, value: value, callback: callback)
} else {
let message = "Unable to start payment because product identifier is invalid: [\([productId])]"
apphudLog(message, forceDisplay: true)
let result = ApphudPurchaseResult(nil, nil, nil, ApphudError(message: message))
callback?(result)
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion ApphudSDK/Internal/ApphudInternal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ final class ApphudInternal: NSObject {
internal let didSubmitAdjustAttributionKey = "didSubmitAdjustAttributionKey"
internal let didSubmitProductPricesKey = "didSubmitProductPricesKey"
internal let submittedFirebaseIdKey = "submittedFirebaseIdKey"
internal let submittedFacebookAnonIdKey = "submittedFacebookAnonIdKey"
internal var didSubmitAppleAdsAttributionKey = "didSubmitAppleAdsAttributionKey"
internal let submittedPushTokenKey = "submittedPushTokenKey"
internal let swizzlePaymentDisabledKey = "swizzlePaymentDisabledKey"
Expand Down Expand Up @@ -196,7 +197,14 @@ final class ApphudInternal: NSObject {
UserDefaults.standard.set(newValue, forKey: didSubmitAppleAdsAttributionKey)
}
}

internal var submittedFacebookAnonId: String? {
get {
UserDefaults.standard.string(forKey: submittedFacebookAnonIdKey)
}
set {
UserDefaults.standard.set(newValue, forKey: submittedFacebookAnonIdKey)
}
}
internal var submittedFirebaseId: String? {
get {
UserDefaults.standard.string(forKey: submittedFirebaseIdKey)
Expand Down
2 changes: 1 addition & 1 deletion ApphudSDK/Public/Apphud.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Foundation
import UserNotifications
import SwiftUI

internal let apphud_sdk_version = "3.2.7"
internal let apphud_sdk_version = "3.2.8"

// MARK: - Initialization

Expand Down
2 changes: 0 additions & 2 deletions ApphudSDK/Public/ApphudEnums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ public typealias ApphudBoolCallback = ((Bool) -> Void)

case appleAdsAttribution // For iOS 14.3+ devices only, Apple Search Ads attribution via AdServices.framework
case firebase

@available(*, deprecated, message: "Facebook integration is no longer needed from SDK and has been voided. You can safely remove this line of code.")
case facebook

/**
Expand Down
28 changes: 26 additions & 2 deletions ApphudSDK/Public/ApphudPaywall.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,37 @@ public class ApphudPaywall: NSObject, Codable, ObservableObject {
Array of products
*/
@Published @objc public internal(set) var products: [ApphudProduct]

/**
Your custom paywall identifier from Apphud Dashboard
*/
@objc public internal(set) var identifier: String

/**
It's possible to make a paywall default – it's a special alias name, that can be assigned to only ONE paywall at a time. There can be no default paywalls at all. It's up to you whether you want to have them or not.
*/
@objc public internal(set) var isDefault: Bool

/**
A/B test experiment name
*/
@objc public var experimentName: String?

/**
A/B Experiment Variation Name
*/
@objc public var variationName: String?

/**
Represents the identifier of a parent paywall from which an experiment variation was derived in A/B Experiments. This property is populated only if the 'Use existing paywall' option was selected during the setup of the experiment variation.
*/
@objc public var parentPaywallIdentifier: String?

/**
Current paywall's placement identifier, if available.
*/
@objc public internal(set) var placementIdentifier: String?

/**
Insert any parameters you need into custom JSON. It could be titles, descriptions, localisations, font, background and color parameters, URLs to media content, etc. Parameters count are not limited.
*/
Expand All @@ -82,13 +100,15 @@ public class ApphudPaywall: NSObject, Codable, ObservableObject {
internal var placementId: String?

@MainActor
internal func update(placementId: String? = nil) {
internal func update(placementId: String?, placementIdentifier: String?) {
objectWillChange.send()
self.placementId = placementId
self.placementIdentifier = placementIdentifier
products.forEach({ product in
product.paywallId = id
product.paywallIdentifier = identifier
product.placementId = placementId
product.placementIdentifier = placementIdentifier
product.skProduct = ApphudStoreKitWrapper.shared.products.first(where: { $0.productIdentifier == product.productId })
})
}
Expand All @@ -98,18 +118,20 @@ public class ApphudPaywall: NSObject, Codable, ObservableObject {
case identifier
case name
case experimentName
case fromPaywall
case variationName
case isDefault = "default"
case jsonString = "json"
case products = "items"
case parentPaywallIdentifier = "fromPaywall"
}

required public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
name = try values.decode(String.self, forKey: .name)
experimentName = try? values.decode(String.self, forKey: .experimentName)
variationName = try? values.decode(String.self, forKey: .variationName)
parentPaywallIdentifier = try? values.decode(String.self, forKey: .parentPaywallIdentifier)
identifier = try values.decode(String.self, forKey: .identifier)
jsonString = try? values.decode(String.self, forKey: .jsonString)
isDefault = try values.decode(Bool.self, forKey: .isDefault)
Expand All @@ -120,6 +142,8 @@ public class ApphudPaywall: NSObject, Codable, ObservableObject {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try? container.encode(experimentName, forKey: .experimentName)
try? container.encode(variationName, forKey: .variationName)
try? container.encode(parentPaywallIdentifier, forKey: .parentPaywallIdentifier)
try container.encode(name, forKey: .name)
try container.encode(identifier, forKey: .identifier)
try? container.encode(jsonString, forKey: .jsonString)
Expand Down
3 changes: 2 additions & 1 deletion ApphudSDK/Public/ApphudProduct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ public class ApphudProduct: NSObject, Codable, ObservableObject {
internal var id: String?

@objc public internal(set) var paywallId: String?
@objc internal var placementId: String?
@objc public internal(set) var placementId: String?
@objc public internal(set) var placementIdentifier: String?

private enum CodingKeys: String, CodingKey {
case id
Expand Down

0 comments on commit fcc4cce

Please sign in to comment.