Skip to content

Commit

Permalink
Merge branch 'release/5.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mscwilson committed Jul 12, 2023
2 parents 581ef11 + 93a651d commit 3286419
Show file tree
Hide file tree
Showing 38 changed files with 2,243 additions and 14 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version 5.4.0 (2023-07-12)
--------------------------
Add Snowplow ecommerce events and entities (#800)
Increase interval for updating platform context properties from 0.1s to 1s (#798)
Expose property for retrieving payload in ConsentDocument that was removed in v5 (#804)

Version 5.3.1 (2023-07-06)
--------------------------
Fix incorrect date deserialization when reading the install timestamp from 1.7 version of the tracker (#801)
Expand Down
2 changes: 1 addition & 1 deletion SnowplowTracker.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SnowplowTracker"
s.version = "5.3.1"
s.version = "5.4.0"
s.summary = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games."
s.description = <<-DESC
Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events.
Expand Down
4 changes: 2 additions & 2 deletions Sources/Core/Subject/PlatformContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import UIKit
/// Manages a dictionary (Payload) with platform context. Some properties for mobile platforms are updated on fetch in set intervals.
class PlatformContext {
private var platformDict: Payload = Payload()
private var mobileDictUpdateFrequency: TimeInterval = 0.1
private var mobileDictUpdateFrequency: TimeInterval = 1.0
private var networkDictUpdateFrequency: TimeInterval = 10.0
private var lastUpdatedEphemeralMobileDict: TimeInterval = 0.0
private var lastUpdatedEphemeralNetworkDict: TimeInterval = 0.0
Expand All @@ -37,7 +37,7 @@ class PlatformContext {
/// - deviceInfoMonitor: Device monitor for fetching platform information
/// - Returns: a PlatformContext object
init(platformContextProperties: [PlatformContextProperty]? = nil,
mobileDictUpdateFrequency: TimeInterval = 0.1,
mobileDictUpdateFrequency: TimeInterval = 1.0,
networkDictUpdateFrequency: TimeInterval = 10.0,
deviceInfoMonitor: DeviceInfoMonitor = DeviceInfoMonitor()) {
self.platformContextProperties = platformContextProperties
Expand Down
37 changes: 37 additions & 0 deletions Sources/Core/Tracker/EcommerceControllerImpl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
//
// This program is licensed to you under the Apache License Version 2.0,
// and you may not use this file except in compliance with the Apache License
// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the Apache License Version 2.0 is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the Apache License Version 2.0 for the specific
// language governing permissions and limitations there under.

import Foundation

class EcommerceControllerImpl: Controller, EcommerceController {

func setEcommerceScreen(_ screen: EcommerceScreenEntity) {
let plugin = PluginConfiguration(identifier: "ecommercePageTypePluginInternal")
_ = plugin.entities { _ in [screen.entity] }
serviceProvider.addPlugin(plugin: plugin)
}

func setEcommerceUser(_ user: EcommerceUserEntity) {
let plugin = PluginConfiguration(identifier: "ecommerceUserPluginInternal")
_ = plugin.entities { _ in [user.entity] }
serviceProvider.addPlugin(plugin: plugin)
}

func removeEcommerceScreen() {
serviceProvider.removePlugin(identifier: "ecommercePageTypePluginInternal")
}

func removeEcommerceUser() {
serviceProvider.removePlugin(identifier: "ecommerceUserPluginInternal")
}
}
9 changes: 8 additions & 1 deletion Sources/Core/Tracker/ServiceProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,14 @@ class ServiceProvider: NSObject, ServiceProviderProtocol {
return mediaController
}

// Configurations
private var _ecommerceController: EcommerceController?
var ecommerceController: EcommerceController {
if let controller = _ecommerceController { return controller }
let ecommerceController = EcommerceControllerImpl(serviceProvider: self)
_ecommerceController = ecommerceController
return ecommerceController
}

private(set) var networkConfiguration = NetworkConfiguration()
private(set) var trackerConfiguration = TrackerConfiguration()
private(set) var emitterConfiguration = EmitterConfiguration()
Expand Down
1 change: 1 addition & 0 deletions Sources/Core/Tracker/ServiceProviderProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ protocol ServiceProviderProtocol: AnyObject {
var subjectConfiguration: SubjectConfiguration { get }
var sessionConfiguration: SessionConfiguration { get }
var gdprConfiguration: GDPRConfiguration { get }
var ecommerceController: EcommerceController { get }
var pluginConfigurations: [PluginIdentifiable] { get }
func addPlugin(plugin: PluginIdentifiable)
func removePlugin(identifier: String)
Expand Down
5 changes: 3 additions & 2 deletions Sources/Core/Tracker/Tracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -471,11 +471,12 @@ class Tracker: NSObject {
setApplicationInstallEventTimestamp(event)
addBasicProperties(to: payload, event: event)
addStateMachinePayloadValues(event: event)
event.wrapProperties(to: payload, base64Encoded: base64Encoded)


// Context entities
addBasicContexts(event: event)
addStateMachineEntities(event: event)

event.wrapProperties(to: payload, base64Encoded: base64Encoded)
event.wrapContexts(to: payload, base64Encoded: base64Encoded)

// Decide whether to track the event or not
Expand Down
4 changes: 4 additions & 0 deletions Sources/Core/Tracker/TrackerControllerImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class TrackerControllerImpl: Controller, TrackerController {
var media: MediaController {
return serviceProvider.mediaController
}

var ecommerce: EcommerceController {
return serviceProvider.ecommerceController
}

// MARK: - Control methods

Expand Down
12 changes: 11 additions & 1 deletion Sources/Core/TrackerConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import Foundation

// --- Version
let kSPRawVersion = "5.3.1"
let kSPRawVersion = "5.4.0"
#if os(iOS)
let kSPVersion = "ios-\(kSPRawVersion)"
#elseif os(tvOS)
Expand Down Expand Up @@ -60,6 +60,16 @@ let kSPErrorSchema = "iglu:com.snowplowanalytics.snowplow/application_error/json
let kSPApplicationInstallSchema = "iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0"
let kSPGdprContextSchema = "iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0"
let kSPDiagnosticErrorSchema = "iglu:com.snowplowanalytics.snowplow/diagnostic_error/jsonschema/1-0-0"
let ecommerceActionSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/snowplow_ecommerce_action/jsonschema/1-0-2"
let ecommerceProductSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/product/jsonschema/1-0-0"
let ecommerceCartSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/cart/jsonschema/1-0-0"
let ecommerceTransactionSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/transaction/jsonschema/1-0-0"
let ecommerceTransactionErrorSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/transaction_error/jsonschema/1-0-0"
let ecommerceCheckoutStepSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/checkout_step/jsonschema/1-0-0"
let ecommercePromotionSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/promotion/jsonschema/1-0-0"
let ecommerceRefundSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/refund/jsonschema/1-0-0"
let ecommerceUserSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/user/jsonschema/1-0-0"
let ecommercePageSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/page/jsonschema/1-0-0"

// --- Event Keys
let kSPEventPageView = "pv"
Expand Down
3 changes: 3 additions & 0 deletions Sources/Snowplow/Controllers/TrackerController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public protocol TrackerController: TrackerConfigurationProtocol {
/// Media controller for managing media tracking instances and tracking media events.
@objc
var media: MediaController { get }
/// Ecommerce controller for managing ecommerce entity addition.
@objc
var ecommerce: EcommerceController { get }
/// Track the event.
/// The tracker will take care to process and send the event assigning `event_id` and `device_timestamp`.
/// - Parameter event: The event to track.
Expand Down
39 changes: 39 additions & 0 deletions Sources/Snowplow/Ecommerce/EcommerceController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
//
// This program is licensed to you under the Apache License Version 2.0,
// and you may not use this file except in compliance with the Apache License
// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the Apache License Version 2.0 is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the Apache License Version 2.0 for the specific
// language governing permissions and limitations there under.

import Foundation

/**
Controller for managing Ecommerce entities.
*/
@objc(SPEcommController)
public protocol EcommerceController {

/// Add an ecommerce Screen/Page entity to all subsequent events.
/// - Parameter screen: A EcommScreenEntity.
@objc
func setEcommerceScreen(_ screen: EcommerceScreenEntity)

/// Add an ecommerce User entity to all subsequent events.
/// - Parameter user: A EcommUserEntity.
@objc
func setEcommerceUser(_ user: EcommerceUserEntity)

/// Stop adding a Screen/Page entity to events.
@objc
func removeEcommerceScreen()

/// Stop adding a User entity to events.
@objc
func removeEcommerceUser()
}
56 changes: 56 additions & 0 deletions Sources/Snowplow/Ecommerce/Entities/CartEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
//
// This program is licensed to you under the Apache License Version 2.0,
// and you may not use this file except in compliance with the Apache License
// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the Apache License Version 2.0 is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the Apache License Version 2.0 for the specific
// language governing permissions and limitations there under.

import Foundation

/**
Provided to certain Ecommerce events. The Cart properties will be sent with the event as a Cart entity.
Entity schema: `iglu:com.snowplowanalytics.snowplow.ecommerce/cart/jsonschema/1-0-0`
*/
@objc(SPCartEntity)
public class CartEntity: NSObject {
/// The total value of the cart after this interaction.
@objc
public var totalValue: Decimal

/// The currency used for this cart (ISO 4217).
@objc
public var currency: String

/// The unique ID representing this cart.
@objc
public var cartId: String?

internal var entity: SelfDescribingJson {
var data: [String : Any] = [
"total_value": totalValue,
"currency": currency
]
if let cartId = cartId { data["cart_id"] = cartId }

return SelfDescribingJson(schema: ecommerceCartSchema, andData: data)
}

/// - Parameter totalValue: The total value of the cart after this interaction.
/// - Parameter currency: The currency used for this cart (ISO 4217).
/// - Parameter cartId: The unique ID representing this cart.
@objc
public init(
totalValue: Decimal,
currency: String,
cartId: String? = nil) {
self.totalValue = totalValue
self.currency = currency
self.cartId = cartId
}
}
56 changes: 56 additions & 0 deletions Sources/Snowplow/Ecommerce/Entities/EcommerceScreenEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
//
// This program is licensed to you under the Apache License Version 2.0,
// and you may not use this file except in compliance with the Apache License
// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the Apache License Version 2.0 is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the Apache License Version 2.0 for the specific
// language governing permissions and limitations there under.

import Foundation

/**
Attach Ecommerce Screen (Page) details to events. It is designed to help with grouping insights by
screen/page type, e.g. Product description, Product list, Home.
Entity schema: `iglu:com.snowplowanalytics.snowplow.ecommerce/page/jsonschema/1-0-0`
*/
@objc(SPEcommerceScreenEntity)
public class EcommerceScreenEntity: NSObject {
/// The type of screen that was visited, e.g. homepage, product details, cart, checkout, etc.
@objc
public var type: String

/// The language that the screen is based in.
@objc
public var language: String?

/// The locale version of the app that is running.
@objc
public var locale: String?

internal var entity: SelfDescribingJson {
var data: [String : Any] = ["type": type]
if let language = language { data["language"] = language }
if let locale = locale { data["locale"] = locale }

return SelfDescribingJson(schema: ecommercePageSchema, andData: data)
}

/// - Parameter type: The type of screen that was visited, e.g. homepage, product details, cart, checkout, etc.
/// - Parameter language: The language that the screen is based in.
/// - Parameter locale: The locale version of the app that is running.
@objc
public init(
type: String,
language: String? = nil,
locale: String? = nil
) {
self.type = type
self.language = language
self.locale = locale
}
}
71 changes: 71 additions & 0 deletions Sources/Snowplow/Ecommerce/Entities/EcommerceUserEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
//
// This program is licensed to you under the Apache License Version 2.0,
// and you may not use this file except in compliance with the Apache License
// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the Apache License Version 2.0 is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the Apache License Version 2.0 for the specific
// language governing permissions and limitations there under.

import Foundation

/**
Attach Ecommerce User details to events. It is designed to help in modeling guest/non-guest account activity.
Entity schema: `iglu:com.snowplowanalytics.snowplow.ecommerce/user/jsonschema/1-0-0`
*/
@objc(SPEcommerceUserEntity)
public class EcommerceUserEntity: NSObject {
/// The user ID.
@objc
public var id: String

/// Whether or not the user is a guest.
public var isGuest: Bool?

/// The user's email address.
@objc
public var email: String?

internal var entity: SelfDescribingJson {
var data: [String : Any] = ["id": id]
if let isGuest = isGuest { data["is_guest"] = isGuest }
if let email = email { data["email"] = email }

return SelfDescribingJson(schema: ecommerceUserSchema, andData: data)
}

/// - Parameter id: The user ID.
/// - Parameter isGuest: Whether or not the user is a guest.
/// - Parameter email: The user's email address.
public init(
id: String,
isGuest: Bool? = nil,
email: String? = nil
) {
self.id = id
self.isGuest = isGuest
self.email = email
}

/// - Parameter id: The user ID.
/// - Parameter email: The user's email address.
@objc
public init(
id: String,
email: String? = nil
) {
self.id = id
self.email = email
}

/// Whether or not the user is a guest.
@objc
public func isGuest(_ isGuest: Bool) -> Self {
self.isGuest = isGuest
return self
}
}
Loading

0 comments on commit 3286419

Please sign in to comment.