Skip to content

Commit

Permalink
feat: add [String:Any] support to identify & track (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
hownowstephen committed Dec 13, 2021
1 parent bba303a commit 5a220c4
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ included: # paths to include during linting. `--path` is ignored if present.
excluded: # paths to ignore during linting. Takes precedence over `included`.
# - Source/ExcludedFolder
# - Source/ExcludedFile.swift
reporter: "emoji" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging)
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging)
23 changes: 21 additions & 2 deletions Sources/Tracking/CustomerIO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,35 @@ public extension CustomerIOInstance {
identify(identifier: identifier, body: body, onComplete: onComplete, jsonEncoder: jsonEncoder)
}

func identify(
identifier: String,
body: [String: Any],
onComplete: @escaping (Result<Void, CustomerIOError>) -> Void,
jsonEncoder: JSONEncoder? = nil
) {
identify(identifier: identifier, body: StringAnyEncodable(body), onComplete: onComplete,
jsonEncoder: jsonEncoder)
}

func track(
name: String,
data: [String: Any],
onComplete: @escaping (Result<Void, CustomerIOError>) -> Void,
jsonEncoder: JSONEncoder? = nil
) {
track(name: name, data: StringAnyEncodable(data), jsonEncoder: jsonEncoder, onComplete: onComplete)
}

func track(
name: String,
onComplete: @escaping (Result<Void, CustomerIOError>) -> Void
) {
track(name: name, data: EmptyRequestBody(), jsonEncoder: nil, onComplete: onComplete)
}

func screen(
name: String,
data: [String:Any],
data: [String: Any],
onComplete: @escaping (Result<Void, CustomerIOError>) -> Void
) {
screen(name: name, data: StringAnyEncodable(data), jsonEncoder: nil, onComplete: onComplete)
Expand Down
7 changes: 4 additions & 3 deletions Sources/Tracking/CustomerIOImplementation+ScreenViews.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public extension CustomerIOImplementation {
method_exchangeImplementations(originalMethod, swizzleMethod)
}

// lint allow start with _ since it's swizzled. Makes it stand out.
// swiftlint:disable:next identifier_name
@objc dynamic func _swizzled_UIKit_viewDidAppear(_ animated: Bool) {
_swizzled_UIKit_viewDidAppear(animated)

Expand Down Expand Up @@ -49,13 +51,12 @@ public extension CustomerIOImplementation {
}
return
}

screen(name: name, data: data) { _ in
// XXX: global error handling of result here
}

}

private static var defaultScreenViewBody: ScreenViewData {
ScreenViewData()
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Tracking/CustomerIOImplementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class CustomerIOImplementation: CustomerIOInstance {

private let identifyRepository: IdentifyRepository

var autoScreenViewBody: (() -> [String:Any])?
var autoScreenViewBody: (() -> [String: Any])?

/**
Constructor for singleton, only.
Expand Down
2 changes: 1 addition & 1 deletion Sources/Tracking/DeviceInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ internal enum DeviceInfo {
/// Device's model on which SDK is running eg. iPhone12,3
static let deviceInfo: String = UIDevice.deviceModelCode
/// Operating system and version of OS of the Device
static let osInfo: String = "\(UIDevice().systemName) \(UIDevice().systemVersion)"
static let osInfo: String = "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)"
/// Name of customer's application using the SDK
static let customerAppName: String = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? ""
/// Version of the customer's application using the SDK
Expand Down
2 changes: 2 additions & 0 deletions Sources/Tracking/Repository/IdentifyRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ internal class CIOIdentifyRepository: IdentifyRepository {
onComplete: onComplete)
}

// disable lint rule as background queue feature will fix this lint issue anyway
// swiftlint:disable:next function_parameter_count
func trackEvent<RequestBody: Encodable>(
type: EventType,
name: String,
Expand Down
2 changes: 1 addition & 1 deletion Sources/Tracking/Service/Request/TrackRequestBody.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal struct TrackRequestBody<T: Encodable>: Encodable {
public enum EventType: String, Codable {
case event
case screen

enum CodingKeys: String, CodingKey {
case event
case screen
Expand Down
2 changes: 1 addition & 1 deletion Sources/Tracking/Store/SdkConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public struct SdkConfig {
Handler to be called by our automatic screen tracker to generate `screen` event body variables. You can use
this to override our defaults and pass custom values in the body of the `screen` event
*/
public var autoScreenViewBody: (() -> [String:Any])?
public var autoScreenViewBody: (() -> [String: Any])?

internal var httpBaseUrls: HttpBaseUrls {
HttpBaseUrls(trackingApi: trackingApiUrl)
Expand Down
20 changes: 10 additions & 10 deletions Sources/Tracking/Util/StringAnyEncodable.swift
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import Foundation

public struct StringAnyEncodable: Encodable {
private let data: [String:AnyEncodable]
private let data: [String: AnyEncodable]

public init(_ data: [String:Any]) {
var d = [String:AnyEncodable]()
for (k, v) in data {
switch v{
public init(_ data: [String: Any]) {
var builtValue = [String: AnyEncodable]()
for (key, value) in data {
switch value {
case let enc as Encodable:
d[k] = AnyEncodable(enc)
case let dict as [String:Any]:
d[k] = AnyEncodable(StringAnyEncodable(dict))
builtValue[key] = AnyEncodable(enc)
case let dict as [String: Any]:
builtValue[key] = AnyEncodable(StringAnyEncodable(dict))
default:
// XXX: logger error
continue
}
}
self.data = d
self.data = builtValue
}

public func encode(to encoder: Encoder) throws {
try self.data.encode(to: encoder)
try data.encode(to: encoder)
}
}
111 changes: 111 additions & 0 deletions Sources/Tracking/autogenerated/AutoMockable.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,117 @@ public class KeyValueStorageMock: KeyValueStorage, TrackingMock {
}
}

/**
Class to easily create a mocked version of the `Logger` class.
This class is equipped with functions and properties ready for you to mock!
Note: This file is automatically generated. This means the mocks should always be up-to-date and has a consistent API.
See the SDK documentation to learn the basics behind using the mock classes in the SDK.
*/
public class LoggerMock: Logger, TrackingMock {
/// If *any* interactions done on mock. `true` if any method or property getter/setter called.
public var mockCalled: Bool = false //

init() {
TrackingMocks.shared.add(mock: self)
}

public func reset() {
mockCalled = false

debugCallsCount = 0
debugReceivedArguments = nil
debugReceivedInvocations = []
infoCallsCount = 0
infoReceivedArguments = nil
infoReceivedInvocations = []
errorCallsCount = 0
errorReceivedArguments = nil
errorReceivedInvocations = []
}

// MARK: - debug

/// Number of times the function was called.
public private(set) var debugCallsCount = 0
/// `true` if the function was ever called.
public var debugCalled: Bool {
debugCallsCount > 0
}

/// The arguments from the *last* time the function was called.
public private(set) var debugReceivedArguments: (String)?
/// Arguments from *all* of the times that the function was called.
public private(set) var debugReceivedInvocations: [String] = []
/**
Set closure to get called when function gets called. Great way to test logic or return a value for the function.
*/
public var debugClosure: ((String) -> Void)?

/// Mocked function for `debug(_ message: String)`. Your opportunity to return a mocked value and check result of mock in test code.
public func debug(_ message: String) {
mockCalled = true
debugCallsCount += 1
debugReceivedArguments = message
debugReceivedInvocations.append(message)
debugClosure?(message)
}

// MARK: - info

/// Number of times the function was called.
public private(set) var infoCallsCount = 0
/// `true` if the function was ever called.
public var infoCalled: Bool {
infoCallsCount > 0
}

/// The arguments from the *last* time the function was called.
public private(set) var infoReceivedArguments: (String)?
/// Arguments from *all* of the times that the function was called.
public private(set) var infoReceivedInvocations: [String] = []
/**
Set closure to get called when function gets called. Great way to test logic or return a value for the function.
*/
public var infoClosure: ((String) -> Void)?

/// Mocked function for `info(_ message: String)`. Your opportunity to return a mocked value and check result of mock in test code.
public func info(_ message: String) {
mockCalled = true
infoCallsCount += 1
infoReceivedArguments = message
infoReceivedInvocations.append(message)
infoClosure?(message)
}

// MARK: - error

/// Number of times the function was called.
public private(set) var errorCallsCount = 0
/// `true` if the function was ever called.
public var errorCalled: Bool {
errorCallsCount > 0
}

/// The arguments from the *last* time the function was called.
public private(set) var errorReceivedArguments: (String)?
/// Arguments from *all* of the times that the function was called.
public private(set) var errorReceivedInvocations: [String] = []
/**
Set closure to get called when function gets called. Great way to test logic or return a value for the function.
*/
public var errorClosure: ((String) -> Void)?

/// Mocked function for `error(_ message: String)`. Your opportunity to return a mocked value and check result of mock in test code.
public func error(_ message: String) {
mockCalled = true
errorCallsCount += 1
errorReceivedArguments = message
errorReceivedInvocations.append(message)
errorClosure?(message)
}
}

/**
Class to easily create a mocked version of the `ProfileStore` class.
This class is equipped with functions and properties ready for you to mock!
Expand Down
Loading

0 comments on commit 5a220c4

Please sign in to comment.