Skip to content

Commit

Permalink
Fixes issue with bool cast as a number
Browse files Browse the repository at this point in the history
- Bools in device attributes was getting serialised as a special NSNumber which was then cast as an int in the CEL. So audience filter such as `totalPaywallViews is 0` was failing. This update checks to see if it's an NSNumber which is actually a Boolean and casts it as a Boolean.
- Added funcs to the graveyard so that people updating from v3 to v4 get a renamed error message.
  • Loading branch information
yusuftor committed Jan 14, 2025
1 parent c8132f7 commit 492b396
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 22 deletions.
25 changes: 16 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

The changelog for `SuperwallKit`. Also see the [releases](/~https://github.com/superwall/Superwall-iOS/releases) on GitHub.

## 4.0.0-beta.2

### Fixes

- Fixes an issue to do with audience filters.
- Readds unavailable functions from v3 to make the upgrade path smoother.

## 4.0.0-beta.1

### Fixes
Expand Down Expand Up @@ -37,18 +44,18 @@ The changelog for `SuperwallKit`. Also see the [releases](/~https://github.com/sup
- Removes `subscriptionStatus_didChange`.
- Removes `subscriptionStatusDidChange(to:)` from the `SuperwallDelegate`.
- Renames `productItems` to `products` in `PaywallInfo`.
- Renames `register(event:)` to `register(placement:)`.
- Renames `preloadPaywalls(forEvents:)` to `preloadPaywalls(forPlacements:)`.
- Renames `getPaywall(forEvent:)` to `getPaywall(forPlacement:)`.
- Renames `getPresentationResult(forEvent:)` to `getPresentationResult(forPlacement:)`.
- Renames the `TriggerResult` `eventNotFound` case to `placementNotFound`.
- Renames the `PresentationResult` and `PaywallSkippedReason` `noRuleMatch` case to `noAudienceMatch`.
- Renames `register(event:)` to `register(placement:)`. DONE
- Renames `preloadPaywalls(forEvents:)` to `preloadPaywalls(forPlacements:)`. DONE
- Renames `getPaywall(forEvent:)` to `getPaywall(forPlacement:)`. DONE
- Renames `getPresentationResult(forEvent:)` to `getPresentationResult(forPlacement:)`. DONE
- Renames the `TriggerResult`, `PresentationResult` and `PaywallSkippedReason` `eventNotFound` case to `placementNotFound` and `noEventMatch` to `noAudienceMatch`. DONE
- Renames `handleSuperwallEvent(withInfo:)` to `handleSuperwallPlacement(withInfo:). DONE
- Moves `ComputedPropertyRequestType` to be a top-level type.
- Renames `Store` to `ProductStore`.
- Removes `Superwall.shared.isConfigured` in favor of `Superwall.shared.configurationStatus`.
- Defaults to StoreKit 2 for product purchasing for apps running on iOS 15+. You can change this back to StoreKit 1 by setting the `SuperwallOption` `storeKitVersion` to `.storeKit1`. Note that when using Objective-C and providing a PurchaseController or using observer mode, the SDK will default to `.storeKit1`. If you're using the purchase function, you must use `.storeKit1`.
- Changes the `PurchaseController` purchase function to `func purchase(product: StoreProduct) async -> PurchaseResult`. There will be an StoreKit 2 product accessible via `product.sk2Product` by default. However, if you're using the StoreKit 1 `SuperwallOption` or your app is running on an iOS version lower than iOS 15, this will be `nil` and you can access the StoreKit 1 product via `product.sk1Product`.
- Consumables no longer count as lifetime subscriptions when using StoreKit 2.
- Defaults to StoreKit 2 for product purchasing for apps running on iOS 15+. You can change this back to StoreKit 1 by setting the `SuperwallOption` `storeKitVersion` to `.storeKit1`. When using Objective-C and providing a PurchaseController or using observer mode, the SDK will default to `.storeKit1`. If you're using Objective-C and using `purchase(_:)`, you must use `.storeKit1`.
- Changes the `PurchaseController` purchase function to `func purchase(product: StoreProduct) async -> PurchaseResult`. There will be an StoreKit 2 product accessible via `product.sk2Product` by default. However, if you're using the StoreKit 1 `SuperwallOption` or your app is running on an iOS version lower than iOS 15, this will be `nil` and you can access the StoreKit 1 product via `product.sk1Product`. DONE
- Consumables no longer count as lifetime subscriptions when using StoreKit 2. REVISIT
- Renames the `PurchaseResult` case `purchased(productId: String)` to `purchased(Product)`.
- Changes the Swift `onDismiss` block of the `PaywallPresentationHandler` to accept both a `PaywallInfo` object and a `PaywallResult` object so you know which product was purchased after dismiss.
- Changes the `onRequestDismiss` block of the `PaywallView` to accept both a `PaywallInfo` object and a `PaywallResult` object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,7 @@ enum InternalSuperwallPlacement {
withComputedProperties: computedPropertyRequests
)

if let audienceFiltersDictionary = audienceFilters.dictionaryObject,
let jsonData = try? JSONSerialization.data(withJSONObject: audienceFiltersDictionary),
if let jsonData = try? JSONSerialization.data(withJSONObject: audienceFilters),
let decoded = String(data: jsonData, encoding: .utf8) {
params += [
"expression_params": decoded
Expand Down
6 changes: 3 additions & 3 deletions Sources/SuperwallKit/Dependencies/DependencyContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -387,19 +387,19 @@ extension DependencyContainer: AudienceFilterAttributesFactory {
func makeAudienceFilterAttributes(
forPlacement placement: PlacementData?,
withComputedProperties computedPropertyRequests: [ComputedPropertyRequest]
) async -> JSON {
) async -> [String: Any] {
var userAttributes = identityManager.userAttributes
userAttributes["isLoggedIn"] = identityManager.isLoggedIn

let deviceAttributes = await deviceHelper.getDeviceAttributes(
since: placement,
computedPropertyRequests: computedPropertyRequests
)
return JSON([
return [
"user": userAttributes,
"device": deviceAttributes,
"params": placement?.parameters.dictionaryObject ?? ""
] as [String: Any])
]
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/SuperwallKit/Dependencies/FactoryProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ protocol AudienceFilterAttributesFactory: AnyObject {
func makeAudienceFilterAttributes(
forPlacement placement: PlacementData?,
withComputedProperties computedPropertyRequests: [ComputedPropertyRequest]
) async -> JSON
) async -> [String: Any]
}

protocol FeatureFlagsFactory: AnyObject {
Expand Down
21 changes: 21 additions & 0 deletions Sources/SuperwallKit/Graveyard/SuperwallGraveyard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,24 @@ extension Superwall {
return .init(trackResult: .placementNotFound)
}
}

@available(*, unavailable, renamed: "SuperwallPlacementInfo")
@objc(SWKSuperwallEventInfo)
@objcMembers
public final class SuperwallEventInfo: NSObject {}

extension SuperwallDelegate {
@available(*, unavailable, renamed: "handleSuperwallPlacement(withInfo:)")
public func handleSuperwallEvent(withInfo placementInfo: SuperwallEventInfo) {}
}

@available(*, unavailable, renamed: "ProductStore")
@objc(SWKStore)
public enum Store: Int {
case appStore
}

@available(*, unavailable, renamed: "Product")
@objc(SWKProductInfo)
@objcMembers
public final class ProductInfo: NSObject, Codable, Sendable {}
2 changes: 1 addition & 1 deletion Sources/SuperwallKit/Misc/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ let sdkVersion = """
*/

let sdkVersion = """
4.0.0-beta.1
4.0.0-beta.2
"""
11 changes: 11 additions & 0 deletions Sources/SuperwallKit/Models/Triggers/TriggerResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ public enum TriggerResult: Sendable, Equatable {
///
/// In these instances, consider falling back to a native paywall.
case error(NSError)

/// This event was not found on the dashboard.
///
/// Please make sure you have added the placement to a campaign on the dashboard and
/// double check its spelling.
@available(*, unavailable, renamed: "placementNotFound")
case eventNotFound

/// No matching audience was found for this placement so no paywall will be shown.
@available(*, unavailable, renamed: "noAudienceMatch")
case noRuleMatch
}

/// The result of a paywall trigger. `noAudienceMatch` is an associated enum.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,15 @@ extension Dictionary where Key == String, Value == Any {
}

func toPassableValue(from anyValue: Any) -> PassableValue {
if let number = anyValue as? NSNumber {
// Check if it is a boolean
if CFGetTypeID(number) == CFBooleanGetTypeID() {
return .bool(number.boolValue)

Check warning on line 81 in Sources/SuperwallKit/Paywall/Presentation/Audience Logic/Expression Evaluator/EvaluationContext.swift

View workflow job for this annotation

GitHub Actions / Package-SwiftLint

Indentation Width Violation: Code should be indented using one tab or 2 spaces (indentation_width)

Check warning on line 81 in Sources/SuperwallKit/Paywall/Presentation/Audience Logic/Expression Evaluator/EvaluationContext.swift

View workflow job for this annotation

GitHub Actions / Package-SwiftLint

Indentation Width Violation: Code should be indented using one tab or 2 spaces (indentation_width)
}
// If not a boolean, let the switch handle the rest
}

switch anyValue {
case let value as Bool:
return .bool(value)
case let value as Int:
return .int(value)
case let value as UInt64:
Expand All @@ -86,19 +92,21 @@ func toPassableValue(from anyValue: Any) -> PassableValue {
return .float(value)
case let value as String:
return .string(value)
case let value as Bool:
return .bool(value)
case let value as Data:
return .bytes(value)
case let value as [Any]:
return .list(value.map { toPassableValue(from: $0) })
case let value as JSON:
if let bool = value.bool {
return .bool(bool)
} else if let int = value.int {
if let int = value.int {
return .int(int)
} else if let uint = value.uInt64 {
return .uint(uint)
} else if let float = value.double {
return .float(float)
} else if let bool = value.bool {
return .bool(bool)
} else if let string = value.string {
return .string(string)
} else if let array = value.array {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,16 @@ public enum PresentationResult: Sendable, Equatable {
/// The paywall is unavailable. This could be because there's no internet, no view controller to
/// present from, or the paywall is already presented.
case paywallNotAvailable

// MARK: - Graveyard
/// This event was not found on the dashboard.
///
/// Please make sure you have added the placement to a campaign on the dashboard and
/// double check its spelling.
@available(*, unavailable, renamed: "placementNotFound")
case eventNotFound

/// No matching audience was found for this placement so no paywall will be shown.
@available(*, unavailable, renamed: "noAudienceMatch")
case noRuleMatch
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ public enum PaywallSkippedReason: Error, Sendable, Equatable, CustomStringConver
return .placementNotFound
}
}

// MARK: - Graveyard

/// This event was not found on the dashboard.
///
/// Please make sure you have added the placement to a campaign on the dashboard and
/// double check its spelling.
@available(*, unavailable, renamed: "placementNotFound")
case eventNotFound

/// No matching rule was found for this event so no paywall will be shown.
@available(*, unavailable, renamed: "noAudienceMatch")
case noRuleMatch
}

/// Objective-C-only enum. Specifies the reason the paywall presentation was skipped.
Expand Down
2 changes: 1 addition & 1 deletion SuperwallKit.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "SuperwallKit"
s.version = "4.0.0-beta.1"
s.version = "4.0.0-beta.2"
s.summary = "Superwall: In-App Paywalls Made Easy"
s.description = "Paywall infrastructure for mobile apps :) we make things like editing your paywall and running price tests as easy as clicking a few buttons. superwall.com"

Expand Down

0 comments on commit 492b396

Please sign in to comment.