Fix Tests Analytics #17

Merged
t.schmitt merged 6 commits from analytics into master 2025-07-17 14:11:28 +02:00
11 changed files with 475 additions and 446 deletions

View File

@@ -7,6 +7,7 @@ import FirebaseAnalytics
// MARK: - Protocol // MARK: - Protocol
protocol AnalyticsManagerProtocol { protocol AnalyticsManagerProtocol {
func logScreen( func logScreen(
name: String, name: String,
path: String, path: String,
@@ -23,75 +24,12 @@ protocol AnalyticsManagerProtocol {
func setEnable(_ enable: Bool) func setEnable(_ enable: Bool)
} }
// MARK: - Matomo
class MatomoAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Properties
private var tracker: MatomoTracker
// MARK: - Init
init(siteId: String, url: String) {
debugPrint("[Matomo service] Server URL: \(url)")
debugPrint("[Matomo service] Site ID: \(siteId)")
tracker = MatomoTracker(
siteId: siteId,
baseURL: URL(string: url)!
)
#if DEBUG
tracker.dispatchInterval = 5
#endif
#if DEBUG
tracker.logger = DefaultLogger(minLevel: .verbose)
#endif
debugPrint("[Matomo service] Configured with content base: \(tracker.contentBase?.absoluteString ?? "-")")
debugPrint("[Matomo service] Opt out: \(tracker.isOptedOut)")
}
// MARK: - Methods
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\(trackerUrl)" + "/" + "\(path)" + "iOS")
tracker.track(
view: [name],
url: urlString
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
tracker.track(
eventWithCategory: category,
action: action,
name: name,
number: nil,
url: nil
)
}
func setEnable(_ enable: Bool) {
tracker.isOptedOut = !enable
}
}
// MARK: - Firebase // MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol { class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Methods
func logScreen( func logScreen(
name: String, name: String,
path: String, path: String,
@@ -158,16 +96,16 @@ class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
parameters: parameters parameters: parameters
) )
} }
func setEnable(_ enable: Bool) { func setEnable(_ enable: Bool) {
Analytics.setAnalyticsCollectionEnabled(enable) Analytics.setAnalyticsCollectionEnabled(enable)
} }
} }
// MARK: - Traker Type // MARK: - Traker Type
enum TrackerType: CaseIterable { enum TrackerType: CaseIterable {
case matomo
case firebase case firebase
} }
@@ -191,7 +129,7 @@ class AnalyticsManager {
} }
} }
// MARK: - Methods // MARK: - Enable Methods
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) { private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
managers.forEach { (key, value) in managers.forEach { (key, value) in
@@ -211,14 +149,12 @@ class AnalyticsManager {
setAnalytics(enable: false, analytics) setAnalytics(enable: false, analytics)
} }
func configure(siteId: String, url: String) { func configure() {
managers[TrackerType.matomo] = MatomoAnalyticsManager(
siteId: siteId,
url: url
)
managers[TrackerType.firebase] = FirebaseAnalyticsManager() managers[TrackerType.firebase] = FirebaseAnalyticsManager()
} }
// MARK: - Private Log Methods
private func logScreen( private func logScreen(
name: String, name: String,
path: String, path: String,
@@ -255,8 +191,8 @@ class AnalyticsManager {
// MARK: - section_one // MARK: - section_one
func logScreenS1DefOne(title: String) { static func logScreenS1DefOne(title: String) {
logScreen( AnalyticsManager.shared.logScreen(
name: "s1 def one \(title)", name: "s1 def one \(title)",
path: "s1_def_one/\(title)", path: "s1_def_one/\(title)",
params: nil params: nil
@@ -283,8 +219,8 @@ class AnalyticsManager {
// MARK: - section_two // MARK: - section_two
func logScreenS2DefOne() { static func logScreenS2DefOne() {
logScreen( AnalyticsManager.shared.logScreen(
name: "s2 def one", name: "s2 def one",
path: "s2_def_one/", path: "s2_def_one/",
params: nil params: nil

View File

@@ -54,7 +54,7 @@ echo "\n-------------------------\n"
# Analytics # Analytics
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \ swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \
--target "firebase matomo" \ --target "firebase" \
--extension-output-path "./Tags/Generated" \ --extension-output-path "./Tags/Generated" \
--extension-name "Analytics" \ --extension-name "Analytics" \
--extension-suffix "GenAllScript" \ --extension-suffix "GenAllScript" \

View File

@@ -5,11 +5,14 @@
// Created by Loris Perret on 08/12/2023. // Created by Loris Perret on 08/12/2023.
// //
// CPD-OFF
import CoreVideo import CoreVideo
import Foundation import Foundation
import ToolCore import ToolCore
// Disabled cause it's a pain to handle in generated string // Disabled cause it's a pain to handle in generated string
// swiftlint:disable type_body_length
enum AnalyticsGenerator { enum AnalyticsGenerator {
@@ -94,7 +97,7 @@ enum AnalyticsGenerator {
\(Self.getAnalyticsProtocol(targets: targets)) \(Self.getAnalyticsProtocol(targets: targets))
\(Self.getTrackerTypeEnum()) \(Self.getTrackerTypeEnum(targets: targets))
// MARK: - Manager // MARK: - Manager
@@ -116,9 +119,9 @@ enum AnalyticsGenerator {
""" """
} }
private static func getTrackerTypeEnum() -> String { private static func getTrackerTypeEnum(targets: [TrackerType]) -> String {
var result: [String] = [] var result: [String] = []
TrackerType.allCases.forEach { type in targets.forEach { type in
result.append(" case \(type)") result.append(" case \(type)")
} }
@@ -126,6 +129,7 @@ enum AnalyticsGenerator {
// MARK: - Traker Type // MARK: - Traker Type
enum TrackerType: CaseIterable { enum TrackerType: CaseIterable {
\(result.joined(separator: "\n")) \(result.joined(separator: "\n"))
} }
""" """
@@ -141,7 +145,7 @@ enum AnalyticsGenerator {
} }
} }
// MARK: - Methods // MARK: - Enable Methods
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) { private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
managers.forEach { (key, value) in managers.forEach { (key, value) in
@@ -171,6 +175,7 @@ enum AnalyticsGenerator {
if targets.contains(TrackerType.matomo) { if targets.contains(TrackerType.matomo) {
result.append("import MatomoTracker") result.append("import MatomoTracker")
} }
if targets.contains(TrackerType.firebase) { if targets.contains(TrackerType.firebase) {
result.append("import FirebaseAnalytics") result.append("import FirebaseAnalytics")
} }
@@ -180,6 +185,8 @@ enum AnalyticsGenerator {
private static func getPrivateLogFunction() -> String { private static func getPrivateLogFunction() -> String {
""" """
// MARK: - Private Log Methods
private func logScreen( private func logScreen(
name: String, name: String,
path: String, path: String,
@@ -235,6 +242,7 @@ enum AnalyticsGenerator {
) )
""") """)
} }
if targets.contains(TrackerType.firebase) { if targets.contains(TrackerType.firebase) {
content.append(" managers[TrackerType.firebase] = FirebaseAnalyticsManager()") content.append(" managers[TrackerType.firebase] = FirebaseAnalyticsManager()")
} }
@@ -252,6 +260,7 @@ enum AnalyticsGenerator {
// MARK: - Protocol // MARK: - Protocol
protocol AnalyticsManagerProtocol { protocol AnalyticsManagerProtocol {
func logScreen( func logScreen(
name: String, name: String,
path: String, path: String,
@@ -267,7 +276,6 @@ enum AnalyticsGenerator {
func setEnable(_ enable: Bool) func setEnable(_ enable: Bool)
} }
""" """
var result: [String] = [proto] var result: [String] = [proto]
@@ -280,7 +288,7 @@ enum AnalyticsGenerator {
result.append(FirebaseGenerator.service) result.append(FirebaseGenerator.service)
} }
return result.joined(separator: "\n") return result.joined(separator: "\n\n")
} }
private static func getProperties( private static func getProperties(
@@ -319,3 +327,5 @@ enum AnalyticsGenerator {
""" """
} }
} }
// CPD-ON

View File

@@ -13,11 +13,11 @@ enum FirebaseGenerator {
static var service: String { static var service: String {
[ [
FirebaseGenerator.header, Self.header,
FirebaseGenerator.logScreen, Self.logScreen,
FirebaseGenerator.logEvent, Self.logEvent,
FirebaseGenerator.enable, Self.enable,
FirebaseGenerator.footer Self.footer
] ]
.joined(separator: "\n") .joined(separator: "\n")
} }
@@ -29,6 +29,9 @@ enum FirebaseGenerator {
// MARK: - Firebase // MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol { class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Methods
""" """
} }
@@ -105,6 +108,7 @@ enum FirebaseGenerator {
parameters: parameters parameters: parameters
) )
} }
""" """
} }
@@ -119,7 +123,6 @@ enum FirebaseGenerator {
private static var footer: String { private static var footer: String {
""" """
} }
""" """
} }
} }

View File

@@ -5,18 +5,20 @@
// Created by Loris Perret on 05/12/2023. // Created by Loris Perret on 05/12/2023.
// //
// CPD-OFF
import Foundation import Foundation
enum MatomoGenerator { enum MatomoGenerator {
static var service: String { static var service: String {
[ [
MatomoGenerator.header, Self.header,
MatomoGenerator.setup, Self.setup,
MatomoGenerator.logScreen, Self.logScreen,
MatomoGenerator.logEvent, Self.logEvent,
MatomoGenerator.enable, Self.enable,
MatomoGenerator.footer Self.footer
] ]
.joined(separator: "\n") .joined(separator: "\n")
} }
@@ -115,7 +117,8 @@ enum MatomoGenerator {
private static var footer: String { private static var footer: String {
""" """
} }
""" """
} }
} }
// CPD-ON

View File

@@ -60,8 +60,10 @@ class AnalyticsDefinition {
switch parameter.type { switch parameter.type {
case .bool: case .bool:
defaultValue = "\(parameter.defaultValue.lowercased())" defaultValue = "\(parameter.defaultValue.lowercased())"
case .int, .double: case .int, .double:
defaultValue = "\(parameter.defaultValue)" defaultValue = "\(parameter.defaultValue)"
case .string: case .string:
defaultValue = "\"\(parameter.defaultValue)\"" defaultValue = "\"\(parameter.defaultValue)\""
} }
@@ -90,9 +92,13 @@ class AnalyticsDefinition {
for rep in parameter.replaceIn { for rep in parameter.replaceIn {
switch rep { switch rep {
case "name": name = name.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))") case "name": name = name.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
case "path": path = path.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))") case "path": path = path.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
case "category": category = category.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))") case "category": category = category.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
case "action": action = action.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))") case "action": action = action.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
default: default:
if let param = parameters.first(where: { $0.name == rep }), param.value.isEmpty == false { if let param = parameters.first(where: { $0.name == rep }), param.value.isEmpty == false {
param.value = param.value.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))") param.value = param.value.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
@@ -117,8 +123,10 @@ class AnalyticsDefinition {
switch param.type { switch param.type {
case .bool: case .bool:
params.append("\"\(param.name)\": \(param.value.lowercased())") params.append("\"\(param.name)\": \(param.value.lowercased())")
case .int, .double: case .int, .double:
params.append("\"\(param.name)\": \(param.value)") params.append("\"\(param.name)\": \(param.value)")
case .string: case .string:
params.append("\"\(param.name)\": \"\(param.value)\"") params.append("\"\(param.name)\": \"\(param.value)\"")
} }

View File

@@ -8,6 +8,7 @@
import Foundation import Foundation
enum ParameterType: String { enum ParameterType: String {
case string = "String" case string = "String"
case int = "Int" case int = "Int"
case double = "Double" case double = "Double"

View File

@@ -78,18 +78,21 @@ class AnalyticsFileParser {
print(error.description) print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
case .bool: case .bool:
if Bool(value.lowercased()) == nil { if Bool(value.lowercased()) == nil {
let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)") let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)")
print(error.description) print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
case .double: case .double:
if Double(value) == nil { if Double(value) == nil {
let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)") let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)")
print(error.description) print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
case .string: case .string:
break break
} }
@@ -147,7 +150,7 @@ class AnalyticsFileParser {
} }
if let parameters { if let parameters {
definition.parameters = AnalyticsFileParser.getParameters(from: parameters) definition.parameters = Self.getParameters(from: parameters)
} }
return definition return definition

View File

@@ -62,7 +62,8 @@ final class AnalyticsDefinitionTests: XCTestCase {
func logScreenDefinitionName() { func logScreenDefinitionName() {
logScreen( logScreen(
name: "Ecran un", name: "Ecran un",
path: "ecran_un/" path: "ecran_un/",
params: nil
) )
} }
""" """
@@ -84,7 +85,7 @@ final class AnalyticsDefinitionTests: XCTestCase {
name: "Ecran un", name: "Ecran un",
action: "", action: "",
category: "", category: "",
params: [:] params: nil
) )
} }
""" """
@@ -103,9 +104,10 @@ final class AnalyticsDefinitionTests: XCTestCase {
// Expect // Expect
let expectScreen = """ let expectScreen = """
static func logScreenDefinitionName() { static func logScreenDefinitionName() {
logScreen( AnalyticsManager.shared.logScreen(
name: "Ecran un", name: "Ecran un",
path: "ecran_un/" path: "ecran_un/",
params: nil
) )
} }
""" """
@@ -123,11 +125,11 @@ final class AnalyticsDefinitionTests: XCTestCase {
// Expect // Expect
let expectEvent = """ let expectEvent = """
static func logEventDefinitionName() { static func logEventDefinitionName() {
logEvent( AnalyticsManager.shared.logEvent(
name: "Ecran un", name: "Ecran un",
action: "", action: "",
category: "", category: "",
params: [:] params: nil
) )
} }
""" """

View File

@@ -32,6 +32,182 @@ final class AnalyticsGeneratorTests: XCTestCase {
return definition return definition
} }
private func protocolString() -> String {
"""
// MARK: - Protocol
protocol AnalyticsManagerProtocol {
func logScreen(
name: String,
path: String,
params: [String: Any]?
)
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
func setEnable(_ enable: Bool)
}
"""
}
private func firebaseString() -> String {
"""
// MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Methods
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
var parameters = [
AnalyticsParameterScreenName: name as NSObject
]
if path.isEmpty == false {
parameters["path"] = path + "/iOS" as NSObject
}
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
AnalyticsEventScreenView,
parameters: parameters
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
AnalyticsParameterItemName: name.replacingOccurrences(of: " ", with: "_") as NSObject
]
if category.isEmpty == false {
parameters["AnalyticsParameterItemCategory"] = category as NSObject
}
if action.isEmpty == false {
parameters["action"] = action as NSObject
}
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
AnalyticsEventSelectContent,
parameters: parameters
)
}
func setEnable(_ enable: Bool) {
Analytics.setAnalyticsCollectionEnabled(enable)
}
}
"""
}
private func matomoString() -> String {
"""
// MARK: - Matomo
class MatomoAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Properties
private var tracker: MatomoTracker
// MARK: - Init
init(siteId: String, url: String) {
debugPrint("[Matomo service] Server URL: \\(url)")
debugPrint("[Matomo service] Site ID: \\(siteId)")
tracker = MatomoTracker(
siteId: siteId,
baseURL: URL(string: url)!
)
#if DEBUG
tracker.dispatchInterval = 5
#endif
#if DEBUG
tracker.logger = DefaultLogger(minLevel: .verbose)
#endif
debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")")
debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)")
}
// MARK: - Methods
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
tracker.track(
view: [name],
url: urlString
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
tracker.track(
eventWithCategory: category,
action: action,
name: name,
number: nil,
url: nil
)
}
func setEnable(_ enable: Bool) {
tracker.isOptedOut = !enable
}
}
"""
}
func testGeneratedExtensionContentFirebase() { func testGeneratedExtensionContentFirebase() {
// Given // Given
let sectionOne = AnalyticsCategory(id: "section_one") let sectionOne = AnalyticsCategory(id: "section_one")
@@ -65,64 +241,18 @@ final class AnalyticsGeneratorTests: XCTestCase {
let expect = """ let expect = """
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion) // Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
import Foundation
import FirebaseAnalytics import FirebaseAnalytics
// MARK: - Protocol \(protocolString())
protocol AnalyticsManagerProtocol { \(firebaseString())
func logScreen(name: String, path: String) // MARK: - Traker Type
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
}
// MARK: - Firebase enum TrackerType: CaseIterable {
class FirebaseAnalyticsManager: AnalyticsManagerProtocol { case firebase
func logScreen(name: String, path: String) {
var parameters = [
AnalyticsParameterScreenName: name as NSObject
]
Analytics.logEvent(
AnalyticsEventScreenView,
parameters: parameters
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
"action": action as NSObject,
"category": category as NSObject,
]
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
name.replacingOccurrences(of: [" "], with: "_"),
parameters: parameters
)
}
} }
// MARK: - Manager // MARK: - Manager
@@ -131,27 +261,59 @@ final class AnalyticsGeneratorTests: XCTestCase {
static var shared = AnalyticsManager() static var shared = AnalyticsManager()
private init() {}
// MARK: - Properties // MARK: - Properties
var managers: [AnalyticsManagerProtocol] = [] var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
private var isEnabled: Bool = true private var isEnabled: Bool {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
// MARK: - Methods // MARK: - Enable Methods
func setAnalyticsEnabled(_ enable: Bool) { private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
isEnabled = enable managers.forEach { (key, value) in
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
} }
func configure() { func configure() {
managers.append(FirebaseAnalyticsManager()) managers[TrackerType.firebase] = FirebaseAnalyticsManager()
} }
private func logScreen(name: String, path: String) { // MARK: - Private Log Methods
private func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return } guard isEnabled else { return }
managers.forEach { manager in managers.values.forEach { manager in
manager.logScreen(name: name, path: path) manager.logScreen(
name: name,
path: path,
params: params
)
} }
} }
@@ -163,7 +325,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
) { ) {
guard isEnabled else { return } guard isEnabled else { return }
managers.forEach { manager in managers.values.forEach { manager in
manager.logEvent( manager.logEvent(
name: name, name: name,
action: action, action: action,
@@ -178,7 +340,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
func logScreenS1DefOne() { func logScreenS1DefOne() {
logScreen( logScreen(
name: "s1 def one", name: "s1 def one",
path: "" path: "",
params: nil
) )
} }
@@ -187,7 +350,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
name: "s1 def two", name: "s1 def two",
action: "", action: "",
category: "", category: "",
params: [:] params: nil
) )
} }
@@ -196,7 +359,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
func logScreenS2DefOne() { func logScreenS2DefOne() {
logScreen( logScreen(
name: "s2 def one", name: "s2 def one",
path: "" path: "",
params: nil
) )
} }
} }
@@ -241,80 +405,18 @@ final class AnalyticsGeneratorTests: XCTestCase {
let expect = """ let expect = """
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion) // Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
import Foundation
import MatomoTracker import MatomoTracker
// MARK: - Protocol \(protocolString())
protocol AnalyticsManagerProtocol { \(matomoString())
func logScreen(name: String, path: String) // MARK: - Traker Type
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
}
// MARK: - Matomo enum TrackerType: CaseIterable {
class MatomoAnalyticsManager: AnalyticsManagerProtocol { case matomo
// MARK: - Properties
private var tracker: MatomoTracker
// MARK: - Init
init(siteId: String, url: String) {
debugPrint("[Matomo service] Server URL: \\(url)")
debugPrint("[Matomo service] Site ID: \\(siteId)")
tracker = MatomoTracker(
siteId: siteId,
baseURL: URL(string: url)!
)
#if DEBUG
tracker.dispatchInterval = 5
#endif
#if DEBUG
tracker.logger = DefaultLogger(minLevel: .verbose)
#endif
debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")")
debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)")
}
// MARK: - Methods
func logScreen(name: String, path: String) {
guard !tracker.isOptedOut else { return }
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
tracker.track(
view: [name],
url: urlString
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
guard !tracker.isOptedOut else { return }
tracker.track(
eventWithCategory: category,
action: action,
name: name,
number: nil,
url: nil
)
}
} }
// MARK: - Manager // MARK: - Manager
@@ -323,32 +425,62 @@ final class AnalyticsGeneratorTests: XCTestCase {
static var shared = AnalyticsManager() static var shared = AnalyticsManager()
private init() {}
// MARK: - Properties // MARK: - Properties
var managers: [AnalyticsManagerProtocol] = [] var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
private var isEnabled: Bool = true private var isEnabled: Bool {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
// MARK: - Methods // MARK: - Enable Methods
func setAnalyticsEnabled(_ enable: Bool) { private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
isEnabled = enable managers.forEach { (key, value) in
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
} }
func configure(siteId: String, url: String) { func configure(siteId: String, url: String) {
managers.append( managers[TrackerType.matomo] = MatomoAnalyticsManager(
MatomoAnalyticsManager(
siteId: siteId, siteId: siteId,
url: url url: url
) )
)
} }
private func logScreen(name: String, path: String) { // MARK: - Private Log Methods
private func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return } guard isEnabled else { return }
managers.forEach { manager in managers.values.forEach { manager in
manager.logScreen(name: name, path: path) manager.logScreen(
name: name,
path: path,
params: params
)
} }
} }
@@ -360,7 +492,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
) { ) {
guard isEnabled else { return } guard isEnabled else { return }
managers.forEach { manager in managers.values.forEach { manager in
manager.logEvent( manager.logEvent(
name: name, name: name,
action: action, action: action,
@@ -375,7 +507,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
func logScreenS1DefOne() { func logScreenS1DefOne() {
logScreen( logScreen(
name: "s1 def one", name: "s1 def one",
path: "s1_def_one/" path: "s1_def_one/",
params: nil
) )
} }
@@ -384,7 +517,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
name: "s1 def two", name: "s1 def two",
action: "test", action: "test",
category: "test", category: "test",
params: [:] params: nil
) )
} }
@@ -393,7 +526,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
func logScreenS2DefOne() { func logScreenS2DefOne() {
logScreen( logScreen(
name: "s2 def one", name: "s2 def one",
path: "s2_def_one/" path: "s2_def_one/",
params: nil
) )
} }
} }
@@ -439,125 +573,22 @@ final class AnalyticsGeneratorTests: XCTestCase {
let expect = """ let expect = """
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion) // Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
import Foundation
import MatomoTracker import MatomoTracker
import FirebaseAnalytics import FirebaseAnalytics
// MARK: - Protocol \(protocolString())
protocol AnalyticsManagerProtocol { \(matomoString())
func logScreen(name: String, path: String) \(firebaseString())
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
}
// MARK: - Matomo // MARK: - Traker Type
class MatomoAnalyticsManager: AnalyticsManagerProtocol { enum TrackerType: CaseIterable {
// MARK: - Properties case matomo
case firebase
private var tracker: MatomoTracker
// MARK: - Init
init(siteId: String, url: String) {
debugPrint("[Matomo service] Server URL: \\(url)")
debugPrint("[Matomo service] Site ID: \\(siteId)")
tracker = MatomoTracker(
siteId: siteId,
baseURL: URL(string: url)!
)
#if DEBUG
tracker.dispatchInterval = 5
#endif
#if DEBUG
tracker.logger = DefaultLogger(minLevel: .verbose)
#endif
debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")")
debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)")
}
// MARK: - Methods
func logScreen(name: String, path: String) {
guard !tracker.isOptedOut else { return }
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
tracker.track(
view: [name],
url: urlString
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
guard !tracker.isOptedOut else { return }
tracker.track(
eventWithCategory: category,
action: action,
name: name,
number: nil,
url: nil
)
}
}
// MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
func logScreen(name: String, path: String) {
var parameters = [
AnalyticsParameterScreenName: name as NSObject
]
Analytics.logEvent(
AnalyticsEventScreenView,
parameters: parameters
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
"action": action as NSObject,
"category": category as NSObject,
]
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
name.replacingOccurrences(of: [" "], with: "_"),
parameters: parameters
)
}
} }
// MARK: - Manager // MARK: - Manager
@@ -566,33 +597,63 @@ final class AnalyticsGeneratorTests: XCTestCase {
static var shared = AnalyticsManager() static var shared = AnalyticsManager()
private init() {}
// MARK: - Properties // MARK: - Properties
var managers: [AnalyticsManagerProtocol] = [] var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
private var isEnabled: Bool = true private var isEnabled: Bool {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
// MARK: - Methods // MARK: - Enable Methods
func setAnalyticsEnabled(_ enable: Bool) { private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
isEnabled = enable managers.forEach { (key, value) in
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
} }
func configure(siteId: String, url: String) { func configure(siteId: String, url: String) {
managers.append( managers[TrackerType.matomo] = MatomoAnalyticsManager(
MatomoAnalyticsManager(
siteId: siteId, siteId: siteId,
url: url url: url
) )
) managers[TrackerType.firebase] = FirebaseAnalyticsManager()
managers.append(FirebaseAnalyticsManager())
} }
private func logScreen(name: String, path: String) { // MARK: - Private Log Methods
private func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return } guard isEnabled else { return }
managers.forEach { manager in managers.values.forEach { manager in
manager.logScreen(name: name, path: path) manager.logScreen(
name: name,
path: path,
params: params
)
} }
} }
@@ -604,7 +665,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
) { ) {
guard isEnabled else { return } guard isEnabled else { return }
managers.forEach { manager in managers.values.forEach { manager in
manager.logEvent( manager.logEvent(
name: name, name: name,
action: action, action: action,
@@ -619,7 +680,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
func logScreenS1DefOne() { func logScreenS1DefOne() {
logScreen( logScreen(
name: "s1 def one", name: "s1 def one",
path: "s1_def_one/" path: "s1_def_one/",
params: nil
) )
} }
@@ -628,7 +690,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
name: "s1 def two", name: "s1 def two",
action: "test", action: "test",
category: "test", category: "test",
params: [:] params: nil
) )
} }
@@ -637,7 +699,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
func logScreenS2DefOne() { func logScreenS2DefOne() {
logScreen( logScreen(
name: "s2 def one", name: "s2 def one",
path: "s2_def_one/" path: "s2_def_one/",
params: nil
) )
} }
} }