Swift Core Installation

Requirements + Known Limitations

  • This SDK requires the following minimum OS versions:
    • macOS 10.14
    • iOS 12.0
    • watchOS 5.0
    • tvOS 12.0
  • The SDK requires Xcode 14.0 or later.
  • The SDK depends on SwiftProtobuf, a Swift package developed by Apple.
  • watchOS support is currently limited to Swift Package Manager implementations.
  • watchOS devices will have separate sessions and user IDs from the parent iOS device. Tracking a user across both will require using Heap.shared.identify to combine the users under a single identity.

📘

For notes on the latest SDK versions, see our iOS changelog.

Installation

Heap Swift Core can be installed using Swift Package Manager or CocoaPods.

Swift Package Manager Installation

  1. In Xcode, open the package manager by either navigating to File → Add Packages… in the menu, or, by opening the Package Dependencies tab of the project file and clicking the "+" button.
  1. In the top right corner of the control, paste https://github.com/heap/heap-swift-core-sdk.git into the Search or Enter Package URL text field.
  1. Select a Dependency Rule. We recommend "Up to Next Major Version".
  1. Click Add Package in the lower right corner of the dialog.
  2. Once the next dialog finishes loading, select the checkbox next to HeapSwiftCore, confirm you're adding to the app target, and click Add Package.
  1. Confirm that “HeapSwiftCore” and “SwiftProtobuf” now appear in the Package Dependencies section of the Project Navigator.
  1. Before continuing to Initializing Heap Swift Core, build the app to help Swift resolve the modules.

CocoaPods Installation

CocoaPods is a Ruby-based dependency manager for iOS projects. Heap requires CocoaPods 1.11.0 or later.

  1. To get started, follow the instructions for installing CocoaPods at https://cocoapods.org.
  2. Save the following line to your Podfile:
pod 'HeapSwiftCore', '~> 0.1'
  1. Run pod install within your project directory.
  2. If you weren't already using CocoaPods or a Workspace, close your project and open the new file with the .xcworkspace extension.
  3. Before continuing to Initializing Heap Swift Core, build the app to help Swift resolve the modules.

macOS Considerations

If you are developing a macOS app that doesn't currently allow outgoing network connections, you'll need to enable the feature in your app sandbox using the following steps.

  1. Select your project file in the Project Navigator.
  2. Select your app target under Targets.
  3. Select the Signing & Capabilities tab.
  4. Scroll down to the App Sandbox section.
  5. Check Outgoing Connections (Client).

Manual Installation

Because Heap depends on SwiftProtobuf, there's currently no manual installation option.

Initializing Heap Swift Core

The Heap Swift SDK is always either recording or not recording. When the SDK isn't recording, the APIs for tracking events, managing users, and setting user and event properties are no-ops. To avail yourself of these APIs, you'll need to do the following:

When you want to start recording, call Heap.shared.startRecording in the manner shown in the code snipped below. We typically recommend invoking this in application(_:,didFinishLaunchingWithOptions:) to capture the whole lifecycle of the app, but this can be called at a later point if you need to wait for conditions to be met.

import HeapSwiftCore  
...

func application(_ application: UIApplication,  
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

  // Replace YOUR_APP_ID with your Heap app ID of the environment you wish to send data to.  
  Heap.shared.startRecording("YOUR_APP_ID")  
  return true  
}
@import HeapSwiftCore  
...

- (BOOL)application:(UIApplication _)application didFinishLaunchingWithOptions:(NSDictionary _)launchOptions  
  {  
    // Replace YOUR_APP_ID with your Heap app ID of the environment you wish to send data to.  
    [Heap.sharedInstance startRecording:@"YOUR_APP_ID"];  
    return YES;  
  }

If you are developing a pure SwiftUI app, one place to start Heap would be in the init method of a state object:

import SwiftUI

final class StateStore: NSObject, ObservableObject {
    
  init() {
    ...
    Heap.shared.startRecording("YOUR_APP_ID")
  }
  
  ...
}

@main
struct ExampleApp: App {
    
    @StateObject var stateStore = StateStore()
    
    ...
}

Using logs to validate the install

By default, Heap logs a minimal amount of data to the console, with the aim of providing appropriate information for a production install. Once Heap is running, you should see a message with the text [Heap] Heap started recording with environment ID YOUR_APP_ID.. This will be followed by messages about uploads succeeding or failing.

It may be helpful to increase the log level during initial deployment to validate events are getting fired. To do this, add the following line in your app.

import HeapSwiftCore
...

HeapLogger.shared.logLevel = .debug
@import HeapSwiftCore
...

HeapLogger.sharedInstance.logLevel = HeapLogLevelDebug;

This will provide even more detailed messages about Heap usage, typically with about one or two messages per API call.

Another option, .trace / HeapLogLevelTrace is available for more complex debugging situations, but these messages are noisier and intended primarily for Heap developers to review.

API Methods

HeapSwiftCore.Heap

The publicly-facing API for the Heap Swift Core SDK.

Note: The Heap object follows the singleton convention and all methods are accessible through Heap.shared in Swift and Heap.sharedInstance in Objective-C.

startRecording(_ environmentId: String, with options: [Option: Any] = [:])

Description
Initializes the Heap SDK and enables Heap to start recording data.

If Heap is already recording and the options have changed, Heap will restart tracking with the new options, creating a new session. Otherwise, calling this method multiple times with the same value has no effect.

Parameters

  • environmentId: The environment ID to which data should be attributed.
  • options: (optional) A dictionary of configuration options for use starting the SDK.

Objective-C method signatures

- (void)startRecording:(NSString * _Nonnull)environmentId;
- (void)startRecording:(NSString * _Nonnull)environmentId
           withOptions:(NSDictionary<HeapOption *, id> * _Nonnull)options;

Examples

// Without options
Heap.shared.startRecording("YOUR_APP_ID")

// With options
Heap.shared.startRecording("YOUR_APP_ID", with: [
  .uploadInterval: 60, // Upload every 60 seconds instead of every 15.
])
// Without options
[Heap.sharedInstance startRecording:@"YOUR_APP_ID"];

// With options
[Heap.sharedInstance startRecording:@"YOUR_APP_ID" withOptions:@{
  HeapOption.uploadInterval: @60, // Upload every 60 seconds instead of every 15.
}];

stopRecording()

Description
Shuts down the Heap SDK and prevents any further data collection.

Examples

Heap.shared.stopRecording()
[Heap.sharedInstance stopRecording];

track(_ event: String, properties: [String: HeapPropertyValue] = [:], timestamp: Date = Date(), sourceInfo: SourceInfo? = nil, pageview: Pageview? = nil

Description
Creates an event message to be tracked and sent to the Heap API.

Parameters

  • event: The string name of the event.
  • properties (optional): The Dictionary of properties to associate with the event. Map keys are represented by strings and values can be any combination of any Int, Double, Bool, String, or type that implements the HeapPropertyValue protocol.
  • timestamp (optional): The non-null Date of when the event occurs. Defaults to the current time. Your implementation shouldn't assign values for this parameter.
  • sourceInfo (optional): The nullable SourceInfo object if this event was generated by an autocapture SDK. Defaults to null. Your implementation shouldn't assign values for this parameter.
  • pageview (optional): The Pageview to associate with the tracked event. This is most commonly the case if the event was generated by an autocapture SDK. Defaults to null. Your implementations shouldn't assign values for this parameter.

Objective-C method signatures

- (void)track:(NSString * _Nonnull)event;
- (void)track:(NSString * _Nonnull)event
   properties:(NSDictionary<NSString *, id <HeapObjcPropertyValue>> * _Nonnull)properties;

The Objective-C method accepts NSString and NSNumber property values, as well as any type that conforms to the HeapObjcPropertyValue protocol.

🚧

Due to runtime limitations, booleans of @YES and @NO are stored as “true” and “false”, while non-Objective-C boolean types are rendered as 1 and 0. If this affects analysis, the booleans should be converted to strings before calling the method. For example: @{ @"isDiscounted": isDiscounted ? @"true" : @"false" }

Examples

// Without properties
Heap.shared.track("Added to cart")

// With properties
Heap.shared.track("Added to cart", properties: [
  "product_category": self.product.category,
])
// Without properties
[Heap.sharedInstance track:@"Added to cart"];

// With properties
[Heap.sharedInstance track:@"Added to cart" properties: @{
  @"product_category": self.product.category,
}];

identify(_ identity: String)

Description
Assigns an identity to be associated with the current user ID, used for combining users across multiple implementations.

If the identity is already set and different from the past identity, this will reset the user ID and session while setting the identity.

Parameters

  • identity: The string value representing the identity to associate with events. This should uniquely identify the person using the app and never a hard-coded value.

Examples
In this example, the app has a method userAuthenticated which is called when the user logs into the app.

// Without properties
Heap.shared.track("Added to cart")

// With properties
Heap.shared.track("Added to cart", properties: [
  "product_category": self.product.category,
])
// Without properties
[Heap.sharedInstance track:@"Added to cart"];

// With properties
[Heap.sharedInstance track:@"Added to cart" properties: @{
  @"product_category": self.product.category,
}];

resetIdentity()

Description
Resets the state for a user that was previously identified with identify, creating a new user and session while clearing event properties. If the user wasn't previously identified, this method has no effect.

Examples
In this example, the app has a method userLoggedOut which is called when the user logs out of the app.

func userLoggedOut() {
  ...
  Heap.shared.resetIdentity()
}
- (void)userLoggedOut
{
  ...
  [Heap.sharedInstance resetIdentity];
}

addUserProperties(_ properties: [String: HeapPropertyValue])

Description
Add a collection of properties to be associated with the current user.

Parameters

  • properties: The Dictionary of properties to associate with the current user. Map keys are represented by strings and values can be any combination of any Int, Double, Bool, String, or type that implements the HeapPropertyValue protocol.

Objective-C method signatures

- (void)addUserProperties:(NSDictionary<NSString *, id <HeapObjcPropertyValue>> * _Nonnull)properties;

The Objective-C method has the same considerations as track. It accepts NSString and NSNumber property values, as well as any type that conforms to the HeapObjcPropertyValue protocol.

Examples
In this example, the app has a method userAuthenticated which is called when the user logs into the app.

func userAuthenticated(user: AppUser) {
  ...
  Heap.shared.addUserProperties([
    "membership_tier": user.membershipTier,
    "team_name": user.team.name,
  ])
}
- (void)userAuthenticated:(AppUser * _Nonnull)user
{
  ...
  [Heap.sharedInstance addUserProperties:@{
    @"membership_tier": user.membershipTier,
    @"team_name": user.team.name,
  }];
}

addEventProperties(_ properties: [String: HeapPropertyValue])

Description
Add a collection of properties to be associated with all future events from the current user.

Parameters

  • event: The string name of the event.
  • properties: The Dictionary of properties to associate with future events. Map keys are represented by strings and values can be any combination of any Int, Double, Bool, String, or type that implements the HeapPropertyValue protocol.

Objective-C method signatures

- (void)addEventProperties:(NSDictionary<NSString *, id <HeapObjcPropertyValue>> * _Nonnull)properties;

The Objective-C method has the same considerations as track. It accepts NSString and NSNumber property values, as well as any type that conforms to the HeapObjcPropertyValue protocol.

Examples
In this example, the app has a method contentLoaded which is called when a view controller loads data and we want to group future events based on the hero banner that was displayed.

func contentLoaded(content: HomeScreenContent) {
  ...
  if let heroName = content.hero?.name {
    Heap.shared.addEventProperties([
      "home_screen_hero": heroName,
    ])
  }
}
- (void)userAuthenticated:(AppUser * _Nonnull)user
{
  ...
  NSString *heroName = content.hero.name;
  if (heroName != nil) {
    [Heap.sharedInstance addEventProperties:@{
      @"home_screen_hero": heroName,
    }];
  }
}

removeEventProperty(_ name: String)

Description
Removes a single event-wide property that was previously added with addEventProperties.

Parameters

  • event: The string name of the event.

Examples
In this example, we want to track a property on all events while a view controller is on screen:

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
  Heap.shared.addEventProperties([
    "chat_popup_is_visible": true,
  ])
}

override func viewDidDisappear(_ animated: Bool) {
  super.viewDidDisappear(animated)
  Heap.shared.removeEventProperty("chat_popup_is_visible")
}
- (void)viewDidAppear:(BOOL)animated
{
  [super viewDidAppear: animated];
  [Heap.sharedInstance addEventProperties:@{
    @"chat_popup_is_visible": @YES,
  }];
}

- (void)viewDidDisappear:(BOOL)animated
{
  [super viewDidDisappear: animated];
  [Heap.sharedInstance removeEventProperty:@"chat_popup_is_visible"];
}

clearEventProperties()

Description
Removes all event-wide properties that were previously added with addEventProperties.

Examples
In this example, the app has a method checkoutComplete which is called when the finishes a purchase.

func checkoutComplete() {
  ...
  Heap.shared.clearEventProperties()
}
- (void)checkoutComplete
{
  ...
  [Heap.sharedInstance clearEventProperties];
}

userId: String? { get }

Description
Returns the current user ID if Heap is recording, otherwise nil.

Return Value
The current Heap-generated user ID used for tracking events, or nil if Heap is not recording.

Examples

let heapUserId = Heap.shared.userId
NSString *heapUserId = Heap.sharedInstance.userId;

identity: String? { get }

Description
Returns the identity if Heap is recording and the user has been identified, otherwise nil.

Return Value
The value that was provided to identify for the current user, or nil if Heap is not recording or identify has not been called.

Examples

let heapIdentity = Heap.shared.identity;
NSString *heapIdentity = Heap.sharedInstance.identity;

sessionId: String? { get }

Description
Returns the current session ID if Heap is recording and the session hasn't expired, otherwise nil.

This method is useful for reading the current state for purposes such as logging. If you are using the session ID for server-side events, it's recommended that you use fetchSessionId(), which will create a new session if needed.

Return Value
The current Heap-generated session ID, or nil if Heap isn't recording.

Examples

let heapSessionId = Heap.shared.sessionId;
NSString *heapSessionId = Heap.sharedInstance.sessionId;

fetchSessionId() -> String?

Description
Returns the current session ID if Heap is recording, otherwise nil.

If the previous session has expired, this will start a new session.

Return Value
The current Heap-generated session ID, or nil if Heap isn't recording.

Examples

let heapSessionId = Heap.shared.sessionId;
NSString *heapSessionId = Heap.sharedInstance.sessionId;

HeapSwiftCore.Option

A collection of options used for initializing Heap.

Note: In Swift, this can typically be used with . shorthand syntax. In Objective-C, the type name is HeapOption.

The Option dictionary accepts any value type and values are validated and copied during startRecording. To confirm which values are applied, use LogLevel.debug before startRecording.

baseUrl (String or URL)

The base URL to upload data to while Heap is running. This follows relative link mechanics must end with a / for paths to resolve correctly.

This parameter primarily exists for testing and proxying purposes, and in most use cases this parameter shouldn't be set.

uploadInterval (TimeInterval or Int or Double literal)

The interval at which event batches should be uploaded to the API. Defaults to 15 seconds.

For apps with limited connectivity, it may make sense to increase this interval. Note: Shorter upload intervals may impact device battery life but improve the experience while using live data feed to create events.

captureAdvertiserId (Bool)

Whether or not to track advertisingIdentifier in events. Defaults to false.

This has no effect if AdSupport hasn’t been linked in the app and will only provide a valid value if the user has authorized the app to read it.

messageBatchByteLimit (Bool)

The maximum size of message batches to send to the server in a single request, in bytes. Significantly smaller sizes will result in more requests. The default is 1MB.

This parameter primarily exists for testing purposes, and in most use cases, this parameter shouldn't be set.

messageBatchMessageLimit (Bool)

The maximum size of message batches to send to the server in a single request, in messages. Significantly smaller sizes will result in more requests. The default is 200.

This parameter primarily exists for testing purposes, and in most use cases, this parameter shouldn't be set.

Examples

// Without options
Heap.shared.startRecording("YOUR_APP_ID")

// With options
Heap.shared.startRecording("YOUR_APP_ID", with: [
  .uploadInterval: 60,
  .captureAdvertiserId: true,
])
// Without options
[Heap.sharedInstance startRecording:@"YOUR_APP_ID"];

// With options
[Heap.sharedInstance startRecording:@"YOUR_APP_ID" withOptions:@{
  HeapOption.uploadInterval: @60,
  HeapOption.captureAdvertiser: @YES,
}];

HeapSwiftCore.HeapPropertyValue and HeapSwiftCore.HeapObjcPropertyValue

By default, track, addUserProperties and addEventProperties accept properties of the following types in Swift:

  • Bool
  • Int, Int64, Int32, Int16, Int8
  • Double, Float
  • String, Substring
  • Literals of the above types.
  • NSString, NSNumber

Objective-C accepts just NSString and NSNumber because its dictionaries only accept objects.

Types are limited here to avoid issues where developers may accidentally pass object types to the methods which have no clear path to resolution. If you wish to pass an object as a property, Heap provides two protocols to do this: HeapPropertyValue for use in Swift and HeapObjcPropertyValue for use in Objective-C. Both require a single property definition: var heapValue: String { get }

Examples

struct Book {
  let title: String
  let author: String
  let year: Int
}

extension Book: HeapPropertyValue {
  var heapValue: String { title }
}
@interface Book: NSObject
@property (nonatomic, nonnull, copy) NSString *title;
@property (nonatomic, nonnull, copy) NSString *author;
@property (nonatomic) NSInteger year;
@end

@interface Book (PropertyValue) <HeapObjcPropertyValue>
@end

@implementation Book (PropertyValue)

- (nonnull NSString *)heapValue
{
  return self.title;
}

@end

HeapSwiftCore.HeapLogger

A logging utility for use by Heap and libraries built on top of it. This class provides several logging methods not described here, and two configuration properties that are useful for tailoring the log experience.

logLevel: LogLevel { get; set }

Description
Gets and sets the current log level. The default value is LogLevel.info (HeapLogLevelInfo in Objective-C).

Example

HeapLogger.shared.logLevel = .debug
HeapLogger.sharedInstance.logLevel = HeapLogLevelDebug;

Description
Gets and sets the current log channel used for printing logs. The default log channel prints all messages to the Xcode console and all levels except LogLevel.trace to the system console using OSLog using the bundle identifier as the subsystem and “Heap” as the category.

This property can be set to a custom implementation that reroutes logs to another logging tool, such as SwiftLog or CocoaLumberjack.

This property is not available in Objective-C.

Example

HeapLogger.shared.logChannel = MyCustomLogChannel()

HeapSwiftCore.LogLevel

This enum defines the different log levels available in Heap.

LogLevel.none (HeapLogLevelNone)

Description
Heap won't print any log messages.

LogLevel.error (HeapLogLevelError)

Description
Heap will only print the most critical log messages, such as when the SDK encounters an error and needs to shut down.

LogLevel.warn (HeapLogLevelWarn)

Description
Heap will print warning messages that are useful in a production environment, such as when an event is invalid or an upload fails.

This level is recommended for production environments so that developers are aware of failures that can impact data.

This level also includes error messages.

LogLevel.info (HeapLogLevelInfo)

Description
Heap will print messages that are useful in a production environment, such as when recording starts/stops, when a batch of events is successfully sent, or when a new session has begun.

This level is recommended for production environments so that developers can see Heap lifecycle messages in their own logging environment.

This level also includes error and warn messages.

LogLevel.debug (HeapLogLevelDebug)

Description
Heap will print messages that the implementing developer may find helpful. Messages may include things such as invalid environment ID value, truncated event names, or attempting to track an event before recording has started.

This level is recommended for developers implementing Heap during the development process to help with debugging normal installation and tracking issues.

This level also includes error, warn, and info messages.

LogLevel.trace (HeapLogLevelTrace)

Description
Heap will print messages that help the Heap team diagnose SDK issues. Heap support may ask the implementing developers to enable this log level to gain better insight into issues developers may have encountered when implementing the Heap SDK.

Full event details are also printed at this level.

This level is recommended when gathering information to send to Heap support personnel. Heap support may also ask that this level be turned on to help debug installation and tracking issues that require extra investigation.

This level also includes error, warn, info, and debug messages.

HeapSwiftCore.LogChannel

This protocol provides a single method for consuming Heap logs.

printLog(logLevel: HeapSwiftCore.LogLevel, message: () -> String, source: String?, file: String, line: UInt)

Description
Logs a message. Heap will have already filtered log messages based on LogLevel so the method is solely responsible for rendering the message.

Parameters

  • logLevel: The LogLevel associated with this message.
  • message: An unevaluated closure for rendering the log message.
  • source: A nullable string name of the source library that produced the message. This will be nil for messages coming from HeapSwiftCore, but will have a value for messages originating from bridging and autocapture libraries.
  • file: The file the message originated from, either #fileID or #file depending on the Swift version used.
  • line: The line in the file the message originated from, #line.

Example
The following is an example of a logger that sends messages through apple/swift-log:

import Logging

class SwiftLogChannel: LogChannel {

  let logger: Logger    
  
  func printLog(logLevel: HeapSwiftCore.LogLevel, message: () -> String, source: String?, file: String, line: UInt) {
    
    switch logLevel {
    case .trace:
      logger.trace(message(), file: file, line: line)
    case .debug:
      logger.debug(message(), file: file, line: line)
    case .info:
      logger.info(message(), file: file, line: line)
    case .warn:
      logger.warn(message(), file: file, line: line)
    case .error:
      logger.error(message(), file: file, line: line)
    default:
      break
    }
  }
}

...

HeapLogger.shared.logChannel = SwiftLogChannel(logger: logger)