Compare commits
10 Commits
2.2.1
...
739e4b5bef
Author | SHA1 | Date | |
---|---|---|---|
739e4b5bef | |||
bc17e23039 | |||
ae69a63a6a | |||
821af9ee08 | |||
b509cf2797 | |||
29f0b91e8f | |||
f29a076541 | |||
8f7fd8cbe2 | |||
323bd18e00 | |||
17aecbc8ec |
83
README.md
83
README.md
@@ -133,7 +133,7 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppTags+GreatApp.swift`)
|
||||
7. `--static-members` *(optional)*: generate static properties or not
|
||||
|
||||
> ⚠️ If extension name is not set or is `Tags`, it will generate the following typaloas `typealias Tags = String`.
|
||||
> ⚠️ If extension name is not set or is `Tags`, it will generate the following typealias `typealias Tags = String`.
|
||||
|
||||
|
||||
## Analytics
|
||||
@@ -141,7 +141,7 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
||||
Analytics will generate all you need to analyze UX with Matomo or Firebase Analytics. Input files are formatted in YAML. This command will generate a manager for each target and an AnalyticsManager. This is this one you will need to use. And it will generate a method for all tags you have declared in the YAML file. Next, you will need to use the `configure()` method of AnalyticsManager and if you want to use matomo to set up the `siteId` and the `url` of the site.
|
||||
|
||||
```sh
|
||||
swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
||||
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/analytics.yml" \
|
||||
--target "matomo firebase" \
|
||||
--extension-output-path "./Analytics/Generated" \
|
||||
--extension-name "AppAnalytics" \
|
||||
@@ -159,7 +159,7 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppAnalytics+GreatApp.swift`)
|
||||
7. `--static-members` *(optional)*: generate static properties or not
|
||||
|
||||
> ⚠️ If extension name is not set or is `Analytics`, it will generate the following typaloas `typealias Analytics = String`.
|
||||
> ⚠️ If extension name is not set or is `Analytics`, it will generate the following typealias `typealias Analytics = String`.
|
||||
|
||||
### YAML
|
||||
|
||||
@@ -186,19 +186,83 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
||||
7. `comments` *(optional)*
|
||||
8. `parameters` *(optional)*
|
||||
|
||||
**Parameters**
|
||||
**Parameters**
|
||||
|
||||
You can use parameters in generate methods.
|
||||
|
||||
1. `name`: name of the parameter
|
||||
2. `type`: type of the parameter (Int, String, Bool, Double)
|
||||
3. `value`: value of the parameter
|
||||
4. `defaultValue`: defaultValue of the parameter
|
||||
3. `replaceIn` *(optional)*
|
||||
|
||||
**Value**
|
||||
|
||||
If you want to send another parameter with a static value. For example, you want to send to which screen the event is triggered.
|
||||
You can add the parameter 'screenName' for example and its 'value' is 'Home'. With this, you do not need to specify the value in the function call.
|
||||
|
||||
**DefaultValue**
|
||||
|
||||
If you want ta add a parameter in the call of the function but you want to make it optionnal with a default value you need to use this property.
|
||||
|
||||
Example:
|
||||
```
|
||||
events:
|
||||
id: id_of_tag
|
||||
name: _TITLE_
|
||||
tags: ios,droid
|
||||
parameters:
|
||||
- name: title
|
||||
type: String
|
||||
defaultValue: someTitle
|
||||
```
|
||||
|
||||
The generated method will be:
|
||||
```
|
||||
logIdOfTag(title: String = "someTitle")
|
||||
```
|
||||
|
||||
**Replace in**
|
||||
|
||||
This is section is equivalent of `%s | %d | %f | %@`. You can put the content of the parameter in *name*, *path*, *action*, *category*.
|
||||
You need to put `_` + `NAME OF THE PARAMETER` + `_` in the target and which target you want in the value of `replaceIn`. (name need to be in uppercase)
|
||||
You need to put `_` + `NAME OF THE PARAMETER` + `_` in the target and which target you want in the value of `replaceIn`. (name need to be in uppercase).
|
||||
You can't use `value`and `replaceIn`in thge same time.
|
||||
|
||||
Example:
|
||||
```
|
||||
events:
|
||||
id: id_of_tag
|
||||
name: _TITLE_
|
||||
tags: ios,droid
|
||||
parameters:
|
||||
- name: title
|
||||
type: String
|
||||
replaceIn: name
|
||||
```
|
||||
|
||||
In this sample, we want to add the parameter `title` in the field `name`. So, we need to place `_TITLE_` in the field `name`.
|
||||
|
||||
The generated method will be:
|
||||
```
|
||||
logIdOfTag(title: String)
|
||||
```
|
||||
|
||||
You can also want to replace a parameter in an other parameter. You can do this with the `replaceIn` property. The condition is that the parameter which will use the `replaceIn`need to have the `value`property
|
||||
|
||||
Example:
|
||||
```
|
||||
events:
|
||||
id: id_of_tag
|
||||
name: title
|
||||
tags: ios,droid
|
||||
parameters:
|
||||
- name: something
|
||||
type: String
|
||||
value: test _TEXT_
|
||||
- name: text
|
||||
type: String
|
||||
replaceIn: something
|
||||
```
|
||||
|
||||
## Images
|
||||
|
||||
@@ -287,6 +351,15 @@ tags:
|
||||
extensionName: String?
|
||||
extensionSuffix: String?
|
||||
staticMembers: Bool?
|
||||
|
||||
analytics:
|
||||
-
|
||||
inputFile: String
|
||||
target: String
|
||||
extensionOutputPath: String
|
||||
extensionName: String?
|
||||
extensionSuffix: String?
|
||||
staticMembers: Bool?
|
||||
```
|
||||
|
||||
### Multiple configurations
|
||||
|
@@ -1,90 +1,59 @@
|
||||
// Generated by ResgenSwift.Analytics 2.1.0
|
||||
|
||||
import MatomoTracker
|
||||
import Foundation
|
||||
import FirebaseAnalytics
|
||||
|
||||
// MARK: - Protocol
|
||||
|
||||
protocol AnalyticsManagerProtocol {
|
||||
|
||||
func logScreen(name: String, path: String)
|
||||
func logScreen(
|
||||
name: String,
|
||||
path: String,
|
||||
params: [String: Any]?
|
||||
)
|
||||
|
||||
func logEvent(
|
||||
name: String,
|
||||
action: String,
|
||||
category: String,
|
||||
params: [String: Any]?
|
||||
)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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
|
||||
)
|
||||
}
|
||||
func setEnable(_ enable: Bool)
|
||||
}
|
||||
|
||||
// MARK: - Firebase
|
||||
|
||||
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
||||
func logScreen(name: String, path: String) {
|
||||
|
||||
// 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
|
||||
@@ -98,10 +67,17 @@ class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
||||
params: [String: Any]?
|
||||
) {
|
||||
var parameters: [String:NSObject] = [
|
||||
"action": action as NSObject,
|
||||
"category": category as 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
|
||||
@@ -115,10 +91,21 @@ class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
||||
}
|
||||
|
||||
Analytics.logEvent(
|
||||
name.replacingOccurrences(of: [" "], with: "_"),
|
||||
AnalyticsEventSelectContent,
|
||||
parameters: parameters
|
||||
)
|
||||
}
|
||||
|
||||
func setEnable(_ enable: Bool) {
|
||||
Analytics.setAnalyticsCollectionEnabled(enable)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Traker Type
|
||||
|
||||
enum TrackerType: CaseIterable {
|
||||
|
||||
case firebase
|
||||
}
|
||||
|
||||
// MARK: - Manager
|
||||
@@ -127,33 +114,59 @@ class AnalyticsManager {
|
||||
|
||||
static var shared = AnalyticsManager()
|
||||
|
||||
private init() {}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var managers: [AnalyticsManagerProtocol] = []
|
||||
var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
|
||||
|
||||
private var isEnabled: Bool = true
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
func setAnalyticsEnabled(_ enable: Bool) {
|
||||
isEnabled = enable
|
||||
private var isEnabled: Bool {
|
||||
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
func configure(siteId: String, url: String) {
|
||||
managers.append(
|
||||
MatomoAnalyticsManager(
|
||||
siteId: siteId,
|
||||
url: url
|
||||
)
|
||||
)
|
||||
managers.append(FirebaseAnalyticsManager())
|
||||
// MARK: - Enable Methods
|
||||
|
||||
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
|
||||
managers.forEach { (key, value) in
|
||||
if analytics.contains(where: { type in
|
||||
type == key
|
||||
}) {
|
||||
value.setEnable(enable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func logScreen(name: String, path: String) {
|
||||
func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
|
||||
setAnalytics(enable: true, analytics)
|
||||
}
|
||||
|
||||
func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
|
||||
setAnalytics(enable: false, analytics)
|
||||
}
|
||||
|
||||
func configure() {
|
||||
managers[TrackerType.firebase] = FirebaseAnalyticsManager()
|
||||
}
|
||||
|
||||
// MARK: - Private Log Methods
|
||||
|
||||
private func logScreen(
|
||||
name: String,
|
||||
path: String,
|
||||
params: [String: Any]?
|
||||
) {
|
||||
guard isEnabled else { return }
|
||||
|
||||
managers.forEach { manager in
|
||||
manager.logScreen(name: name, path: path)
|
||||
managers.values.forEach { manager in
|
||||
manager.logScreen(
|
||||
name: name,
|
||||
path: path,
|
||||
params: params
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +178,7 @@ class AnalyticsManager {
|
||||
) {
|
||||
guard isEnabled else { return }
|
||||
|
||||
managers.forEach { manager in
|
||||
managers.values.forEach { manager in
|
||||
manager.logEvent(
|
||||
name: name,
|
||||
action: action,
|
||||
@@ -177,31 +190,39 @@ class AnalyticsManager {
|
||||
|
||||
// MARK: - section_one
|
||||
|
||||
func logScreenS1DefOne(title: String) {
|
||||
logScreen(
|
||||
static func logScreenS1DefOne(title: String) {
|
||||
AnalyticsManager.shared.logScreen(
|
||||
name: "s1 def one \(title)",
|
||||
path: "s1_def_one/\(title)"
|
||||
path: "s1_def_one/\(title)",
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
|
||||
func logEventS1DefTwo(title: String, count: String) {
|
||||
logEvent(
|
||||
static func logEventS1DefTwo(
|
||||
title: String,
|
||||
count: String,
|
||||
test2: String = "test"
|
||||
) {
|
||||
AnalyticsManager.shared.logEvent(
|
||||
name: "s1 def two",
|
||||
action: "test",
|
||||
category: "test",
|
||||
params: [
|
||||
"title": title,
|
||||
"count": count
|
||||
"count": count,
|
||||
"test": "test",
|
||||
"test2": test2
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - section_two
|
||||
|
||||
func logScreenS2DefOne() {
|
||||
logScreen(
|
||||
static func logScreenS2DefOne() {
|
||||
AnalyticsManager.shared.logScreen(
|
||||
name: "s2 def one",
|
||||
path: "s2_def_one/"
|
||||
path: "s2_def_one/",
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,12 @@ categories:
|
||||
type: String
|
||||
- name: count
|
||||
type: String
|
||||
- name: test
|
||||
type: String
|
||||
value: test
|
||||
- name: test2
|
||||
type: String
|
||||
defaultValue: test
|
||||
|
||||
- id: section_two
|
||||
screens:
|
||||
|
@@ -50,14 +50,15 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/sampleTags.txt
|
||||
--extension-name "Tags" \
|
||||
--extension-suffix "GenAllScript"
|
||||
|
||||
#echo "\n-------------------------\n"
|
||||
echo "\n-------------------------\n"
|
||||
|
||||
# Analytics
|
||||
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \
|
||||
--target "matomo firebase" \
|
||||
--target "firebase" \
|
||||
--extension-output-path "./Tags/Generated" \
|
||||
--extension-name "Analytics" \
|
||||
--extension-suffix "GenAllScript"
|
||||
--extension-suffix "GenAllScript" \
|
||||
--static-members true
|
||||
|
||||
echo "\n-------------------------\n"
|
||||
|
||||
|
@@ -89,36 +89,77 @@ enum AnalyticsGenerator {
|
||||
) -> String {
|
||||
"""
|
||||
// Generated by ResgenSwift.\(Analytics.toolName) \(ResgenSwiftVersion)
|
||||
|
||||
\(Self.getImport(targets: targets))
|
||||
|
||||
\(Self.getAnalyticsProtocol(targets: targets))
|
||||
|
||||
\(getImport(targets: targets))
|
||||
|
||||
\(getAnalyticsProtocol(targets: targets))
|
||||
\(Self.getTrackerTypeEnum(targets: targets))
|
||||
|
||||
// MARK: - Manager
|
||||
|
||||
class AnalyticsManager {
|
||||
|
||||
|
||||
static var shared = AnalyticsManager()
|
||||
|
||||
private init() {}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var managers: [AnalyticsManagerProtocol] = []
|
||||
|
||||
\(getEnabledContent())
|
||||
|
||||
\(getAnalyticsProperties(targets: targets))
|
||||
|
||||
\(getPrivateLogFunction())
|
||||
|
||||
var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
|
||||
|
||||
\(Self.getEnabledContent())
|
||||
|
||||
\(Self.getAnalyticsProperties(targets: targets))
|
||||
|
||||
\(Self.getPrivateLogFunction())
|
||||
"""
|
||||
}
|
||||
|
||||
|
||||
private static func getTrackerTypeEnum(targets: [TrackerType]) -> String {
|
||||
var result: [String] = []
|
||||
targets.forEach { type in
|
||||
result.append(" case \(type)")
|
||||
}
|
||||
|
||||
return """
|
||||
// MARK: - Traker Type
|
||||
|
||||
enum TrackerType: CaseIterable {
|
||||
|
||||
\(result.joined(separator: "\n"))
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
private static func getEnabledContent() -> String {
|
||||
"""
|
||||
private var isEnabled: Bool = true
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
func setAnalyticsEnabled(_ enable: Bool) {
|
||||
isEnabled = enable
|
||||
private var isEnabled: Bool {
|
||||
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Enable Methods
|
||||
|
||||
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
|
||||
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)
|
||||
}
|
||||
"""
|
||||
}
|
||||
@@ -126,6 +167,8 @@ enum AnalyticsGenerator {
|
||||
private static func getImport(targets: [TrackerType]) -> String {
|
||||
var result: [String] = []
|
||||
|
||||
result.append("import Foundation")
|
||||
|
||||
if targets.contains(TrackerType.matomo) {
|
||||
result.append("import MatomoTracker")
|
||||
}
|
||||
@@ -138,11 +181,21 @@ enum AnalyticsGenerator {
|
||||
|
||||
private static func getPrivateLogFunction() -> String {
|
||||
"""
|
||||
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 }
|
||||
|
||||
managers.forEach { manager in
|
||||
manager.logScreen(name: name, path: path)
|
||||
|
||||
managers.values.forEach { manager in
|
||||
manager.logScreen(
|
||||
name: name,
|
||||
path: path,
|
||||
params: params
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,8 +206,8 @@ enum AnalyticsGenerator {
|
||||
params: [String: Any]?
|
||||
) {
|
||||
guard isEnabled else { return }
|
||||
|
||||
managers.forEach { manager in
|
||||
|
||||
managers.values.forEach { manager in
|
||||
manager.logEvent(
|
||||
name: name,
|
||||
action: action,
|
||||
@@ -179,16 +232,14 @@ enum AnalyticsGenerator {
|
||||
|
||||
if targets.contains(TrackerType.matomo) {
|
||||
content.append("""
|
||||
managers.append(
|
||||
MatomoAnalyticsManager(
|
||||
siteId: siteId,
|
||||
url: url
|
||||
)
|
||||
managers[TrackerType.matomo] = MatomoAnalyticsManager(
|
||||
siteId: siteId,
|
||||
url: url
|
||||
)
|
||||
""")
|
||||
}
|
||||
if targets.contains(TrackerType.firebase) {
|
||||
content.append(" managers.append(FirebaseAnalyticsManager())")
|
||||
content.append(" managers[TrackerType.firebase] = FirebaseAnalyticsManager()")
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -204,16 +255,22 @@ enum AnalyticsGenerator {
|
||||
// MARK: - Protocol
|
||||
|
||||
protocol AnalyticsManagerProtocol {
|
||||
|
||||
func logScreen(name: String, path: String)
|
||||
|
||||
func logScreen(
|
||||
name: String,
|
||||
path: String,
|
||||
params: [String: Any]?
|
||||
)
|
||||
|
||||
func logEvent(
|
||||
name: String,
|
||||
action: String,
|
||||
category: String,
|
||||
params: [String: Any]?
|
||||
)
|
||||
|
||||
func setEnable(_ enable: Bool)
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
var result: [String] = [proto]
|
||||
@@ -226,7 +283,7 @@ enum AnalyticsGenerator {
|
||||
result.append(FirebaseGenerator.service)
|
||||
}
|
||||
|
||||
return result.joined(separator: "\n")
|
||||
return result.joined(separator: "\n\n")
|
||||
}
|
||||
|
||||
private static func getProperties(
|
||||
|
@@ -13,10 +13,11 @@ enum FirebaseGenerator {
|
||||
|
||||
static var service: String {
|
||||
[
|
||||
Self.header,
|
||||
Self.logScreen,
|
||||
Self.logEvent,
|
||||
Self.footer
|
||||
FirebaseGenerator.header,
|
||||
FirebaseGenerator.logScreen,
|
||||
FirebaseGenerator.logEvent,
|
||||
FirebaseGenerator.enable,
|
||||
FirebaseGenerator.footer
|
||||
]
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
@@ -28,16 +29,39 @@ enum FirebaseGenerator {
|
||||
// MARK: - Firebase
|
||||
|
||||
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
"""
|
||||
}
|
||||
|
||||
private static var logScreen: String {
|
||||
"""
|
||||
func logScreen(name: String, path: String) {
|
||||
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
|
||||
@@ -56,10 +80,17 @@ enum FirebaseGenerator {
|
||||
params: [String: Any]?
|
||||
) {
|
||||
var parameters: [String:NSObject] = [
|
||||
"action": action as NSObject,
|
||||
"category": category as 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
|
||||
@@ -73,17 +104,25 @@ enum FirebaseGenerator {
|
||||
}
|
||||
|
||||
Analytics.logEvent(
|
||||
name.replacingOccurrences(of: [" "], with: "_"),
|
||||
AnalyticsEventSelectContent,
|
||||
parameters: parameters
|
||||
)
|
||||
}
|
||||
|
||||
"""
|
||||
}
|
||||
|
||||
|
||||
private static var enable: String {
|
||||
"""
|
||||
func setEnable(_ enable: Bool) {
|
||||
Analytics.setAnalyticsCollectionEnabled(enable)
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
private static var footer: String {
|
||||
"""
|
||||
}
|
||||
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
@@ -11,11 +11,12 @@ enum MatomoGenerator {
|
||||
|
||||
static var service: String {
|
||||
[
|
||||
Self.header,
|
||||
Self.setup,
|
||||
Self.logScreen,
|
||||
Self.logEvent,
|
||||
Self.footer
|
||||
MatomoGenerator.header,
|
||||
MatomoGenerator.setup,
|
||||
MatomoGenerator.logScreen,
|
||||
MatomoGenerator.logEvent,
|
||||
MatomoGenerator.enable,
|
||||
MatomoGenerator.footer
|
||||
]
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
@@ -66,8 +67,11 @@ enum MatomoGenerator {
|
||||
|
||||
private static var logScreen: String {
|
||||
"""
|
||||
func logScreen(name: String, path: String) {
|
||||
guard !tracker.isOptedOut else { return }
|
||||
func logScreen(
|
||||
name: String,
|
||||
path: String,
|
||||
params: [String: Any]?
|
||||
) {
|
||||
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
|
||||
|
||||
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
|
||||
@@ -88,8 +92,6 @@ enum MatomoGenerator {
|
||||
category: String,
|
||||
params: [String: Any]?
|
||||
) {
|
||||
guard !tracker.isOptedOut else { return }
|
||||
|
||||
tracker.track(
|
||||
eventWithCategory: category,
|
||||
action: action,
|
||||
@@ -98,13 +100,21 @@ enum MatomoGenerator {
|
||||
url: nil
|
||||
)
|
||||
}
|
||||
|
||||
"""
|
||||
}
|
||||
|
||||
private static var enable: String {
|
||||
"""
|
||||
func setEnable(_ enable: Bool) {
|
||||
tracker.isOptedOut = !enable
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
private static var footer: String {
|
||||
"""
|
||||
}
|
||||
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
@@ -51,17 +51,23 @@ class AnalyticsDefinition {
|
||||
}
|
||||
|
||||
private func getParameters() -> String {
|
||||
var params = parameters
|
||||
var result: String
|
||||
|
||||
if type == .screen {
|
||||
params = params.filter { param in
|
||||
!param.replaceIn.isEmpty
|
||||
|
||||
let paramsString = parameters.compactMap { parameter -> String? in
|
||||
guard parameter.value.isEmpty else { return nil }
|
||||
|
||||
let defaultValue: String
|
||||
switch parameter.type {
|
||||
case .bool:
|
||||
defaultValue = "\(parameter.defaultValue.lowercased())"
|
||||
case .int, .double:
|
||||
defaultValue = "\(parameter.defaultValue)"
|
||||
case .string:
|
||||
defaultValue = "\"\(parameter.defaultValue)\""
|
||||
}
|
||||
}
|
||||
|
||||
let paramsString = params.map { parameter in
|
||||
"\(parameter.name): \(parameter.type)"
|
||||
|
||||
let defaultValueString = parameter.defaultValue.isEmpty ? "" : " = \(defaultValue)"
|
||||
return "\(parameter.name): \(parameter.type.rawValue)\(defaultValueString)"
|
||||
}
|
||||
|
||||
if paramsString.count > 2 {
|
||||
@@ -87,7 +93,10 @@ class AnalyticsDefinition {
|
||||
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 "action": action = action.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
|
||||
default: break
|
||||
default:
|
||||
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))")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,7 +111,18 @@ class AnalyticsDefinition {
|
||||
}
|
||||
|
||||
supplementaryParams.forEach { param in
|
||||
params.append("\"\(param.name)\": \(param.name)")
|
||||
if param.value.isEmpty {
|
||||
params.append("\"\(param.name)\": \(param.name)")
|
||||
} else {
|
||||
switch param.type {
|
||||
case .bool:
|
||||
params.append("\"\(param.name)\": \(param.value.lowercased())")
|
||||
case .int, .double:
|
||||
params.append("\"\(param.name)\": \(param.value)")
|
||||
case .string:
|
||||
params.append("\"\(param.name)\": \"\(param.value)\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if params.count > 1 {
|
||||
@@ -116,14 +136,15 @@ class AnalyticsDefinition {
|
||||
[\(params.joined(separator: ", "))]
|
||||
"""
|
||||
} else {
|
||||
result = "[:]"
|
||||
result = "nil"
|
||||
}
|
||||
|
||||
if type == .screen {
|
||||
return """
|
||||
logScreen(
|
||||
name: "\(name)",
|
||||
path: "\(path)"
|
||||
path: "\(path)",
|
||||
params: \(result)
|
||||
)
|
||||
"""
|
||||
} else {
|
||||
@@ -153,7 +174,7 @@ class AnalyticsDefinition {
|
||||
replaceIn()
|
||||
return """
|
||||
static func \(getFuncName())\(getParameters()) {
|
||||
\(getlogFunction())
|
||||
AnalyticsManager.shared.\(getlogFunction())
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
@@ -46,5 +46,7 @@ struct AnalyticsParameterDTO: Codable {
|
||||
|
||||
var name: String
|
||||
var type: String
|
||||
var value: String?
|
||||
var defaultValue: String?
|
||||
var replaceIn: String?
|
||||
}
|
||||
|
@@ -12,13 +12,17 @@ class AnalyticsParameter {
|
||||
// MARK: - Properties
|
||||
|
||||
var name: String
|
||||
var type: String
|
||||
var type: ParameterType
|
||||
var value: String
|
||||
var defaultValue: String
|
||||
var replaceIn: [String] = []
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init(name: String, type: String) {
|
||||
|
||||
init(name: String, type: ParameterType, value: String, defaultValue: String) {
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.value = value
|
||||
self.defaultValue = defaultValue
|
||||
}
|
||||
}
|
||||
|
15
Sources/ResgenSwift/Analytics/Model/ParameterType.swift
Normal file
15
Sources/ResgenSwift/Analytics/Model/ParameterType.swift
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Loris Perret on 17/07/2024.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum ParameterType: String {
|
||||
case string = "String"
|
||||
case int = "Int"
|
||||
case double = "Double"
|
||||
case bool = "Bool"
|
||||
}
|
@@ -67,25 +67,58 @@ class AnalyticsFileParser {
|
||||
}
|
||||
}
|
||||
|
||||
private func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
|
||||
parameters.map { dtoParameter in
|
||||
private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
|
||||
func verify(value: String?, for type: ParameterType) {
|
||||
guard let value, value.isEmpty == false else { return }
|
||||
|
||||
switch type {
|
||||
case .int:
|
||||
if Int(value) == nil {
|
||||
let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)")
|
||||
print(error.description)
|
||||
Analytics.exit(withError: error)
|
||||
}
|
||||
case .bool:
|
||||
if Bool(value.lowercased()) == nil {
|
||||
let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)")
|
||||
print(error.description)
|
||||
Analytics.exit(withError: error)
|
||||
}
|
||||
case .double:
|
||||
if Double(value) == nil {
|
||||
let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)")
|
||||
print(error.description)
|
||||
Analytics.exit(withError: error)
|
||||
}
|
||||
case .string:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return parameters.map { dtoParameter in
|
||||
// Type
|
||||
let type = dtoParameter.type.uppercasedFirst()
|
||||
|
||||
guard
|
||||
type == "String" ||
|
||||
type == "Int" ||
|
||||
type == "Double" ||
|
||||
type == "Bool"
|
||||
else {
|
||||
guard let typeEnum = ParameterType(rawValue: type) else {
|
||||
let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)")
|
||||
print(error.description)
|
||||
Analytics.exit(withError: error)
|
||||
}
|
||||
|
||||
if dtoParameter.value != nil, dtoParameter.replaceIn != nil {
|
||||
let error = AnalyticsError.invalidParameter("you can't set 'value' and 'replaceIn' for \(dtoParameter.name)")
|
||||
print(error.description)
|
||||
Analytics.exit(withError: error)
|
||||
}
|
||||
|
||||
verify(value: dtoParameter.value, for: typeEnum)
|
||||
verify(value: dtoParameter.defaultValue, for: typeEnum)
|
||||
|
||||
let parameter = AnalyticsParameter(
|
||||
name: dtoParameter.name,
|
||||
type: type
|
||||
type: typeEnum,
|
||||
value: dtoParameter.value ?? "",
|
||||
defaultValue: dtoParameter.defaultValue ?? ""
|
||||
)
|
||||
|
||||
if let replaceIn = dtoParameter.replaceIn {
|
||||
@@ -114,7 +147,7 @@ class AnalyticsFileParser {
|
||||
}
|
||||
|
||||
if let parameters {
|
||||
definition.parameters = getParameters(from: parameters)
|
||||
definition.parameters = AnalyticsFileParser.getParameters(from: parameters)
|
||||
}
|
||||
|
||||
return definition
|
||||
@@ -140,6 +173,8 @@ class AnalyticsFileParser {
|
||||
Analytics.exit(withError: error)
|
||||
}
|
||||
|
||||
definition.path = path
|
||||
} else if let path = screen.path {
|
||||
definition.path = path
|
||||
}
|
||||
|
||||
@@ -176,6 +211,14 @@ class AnalyticsFileParser {
|
||||
}
|
||||
|
||||
definition.action = action
|
||||
} else {
|
||||
if let category = event.category {
|
||||
definition.category = category
|
||||
}
|
||||
|
||||
if let action = event.action {
|
||||
definition.action = action
|
||||
}
|
||||
}
|
||||
|
||||
return definition
|
||||
|
@@ -62,7 +62,8 @@ final class AnalyticsDefinitionTests: XCTestCase {
|
||||
func logScreenDefinitionName() {
|
||||
logScreen(
|
||||
name: "Ecran un",
|
||||
path: "ecran_un/"
|
||||
path: "ecran_un/",
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
"""
|
||||
@@ -84,7 +85,7 @@ final class AnalyticsDefinitionTests: XCTestCase {
|
||||
name: "Ecran un",
|
||||
action: "",
|
||||
category: "",
|
||||
params: [:]
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
"""
|
||||
@@ -103,9 +104,10 @@ final class AnalyticsDefinitionTests: XCTestCase {
|
||||
// Expect
|
||||
let expectScreen = """
|
||||
static func logScreenDefinitionName() {
|
||||
logScreen(
|
||||
AnalyticsManager.shared.logScreen(
|
||||
name: "Ecran un",
|
||||
path: "ecran_un/"
|
||||
path: "ecran_un/",
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
"""
|
||||
@@ -123,11 +125,11 @@ final class AnalyticsDefinitionTests: XCTestCase {
|
||||
// Expect
|
||||
let expectEvent = """
|
||||
static func logEventDefinitionName() {
|
||||
logEvent(
|
||||
AnalyticsManager.shared.logEvent(
|
||||
name: "Ecran un",
|
||||
action: "",
|
||||
category: "",
|
||||
params: [:]
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
"""
|
||||
|
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// AnalyticsGeneratorTests.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 06/09/2022.
|
||||
//
|
||||
@@ -14,7 +14,7 @@ import ToolCore
|
||||
@testable import ResgenSwift
|
||||
|
||||
final class AnalyticsGeneratorTests: XCTestCase {
|
||||
|
||||
|
||||
private func getAnalyticsDefinition(
|
||||
id: String,
|
||||
path: String = "",
|
||||
@@ -31,7 +31,183 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
definition.category = category
|
||||
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() {
|
||||
// Given
|
||||
let sectionOne = AnalyticsCategory(id: "section_one")
|
||||
@@ -39,19 +215,19 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
getAnalyticsDefinition(id: "s1_def_one", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]),
|
||||
getAnalyticsDefinition(id: "s1_def_two", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]),
|
||||
]
|
||||
|
||||
|
||||
let sectionTwo = AnalyticsCategory(id: "section_two")
|
||||
sectionTwo.definitions = [
|
||||
getAnalyticsDefinition(id: "s2_def_one", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]),
|
||||
getAnalyticsDefinition(id: "s2_def_two", name: "s2 def two", type: .event, tags: ["droid","droidonly"]),
|
||||
]
|
||||
|
||||
|
||||
let sectionThree = AnalyticsCategory(id: "section_three")
|
||||
sectionThree.definitions = [
|
||||
getAnalyticsDefinition(id: "s3_def_one", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]),
|
||||
getAnalyticsDefinition(id: "s3_def_two", name: "s3 def two", type: .event, tags: ["droid","droidonly"]),
|
||||
]
|
||||
|
||||
|
||||
// When
|
||||
let extensionContent = AnalyticsGenerator.getExtensionContent(
|
||||
targets: [TrackerType.firebase],
|
||||
@@ -65,64 +241,18 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
let expect = """
|
||||
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
|
||||
|
||||
import Foundation
|
||||
import FirebaseAnalytics
|
||||
|
||||
// MARK: - Protocol
|
||||
\(protocolString())
|
||||
|
||||
protocol AnalyticsManagerProtocol {
|
||||
\(firebaseString())
|
||||
|
||||
func logScreen(name: String, path: String)
|
||||
func logEvent(
|
||||
name: String,
|
||||
action: String,
|
||||
category: String,
|
||||
params: [String: Any]?
|
||||
)
|
||||
}
|
||||
// MARK: - Traker Type
|
||||
|
||||
// MARK: - Firebase
|
||||
enum TrackerType: CaseIterable {
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
case firebase
|
||||
}
|
||||
|
||||
// MARK: - Manager
|
||||
@@ -131,27 +261,59 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
|
||||
static var shared = AnalyticsManager()
|
||||
|
||||
private init() {}
|
||||
|
||||
// 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) {
|
||||
isEnabled = enable
|
||||
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
|
||||
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() {
|
||||
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 }
|
||||
|
||||
managers.forEach { manager in
|
||||
manager.logScreen(name: name, path: path)
|
||||
managers.values.forEach { manager in
|
||||
manager.logScreen(
|
||||
name: name,
|
||||
path: path,
|
||||
params: params
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +325,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
) {
|
||||
guard isEnabled else { return }
|
||||
|
||||
managers.forEach { manager in
|
||||
managers.values.forEach { manager in
|
||||
manager.logEvent(
|
||||
name: name,
|
||||
action: action,
|
||||
@@ -178,7 +340,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
func logScreenS1DefOne() {
|
||||
logScreen(
|
||||
name: "s1 def one",
|
||||
path: ""
|
||||
path: "",
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -187,7 +350,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
name: "s1 def two",
|
||||
action: "",
|
||||
category: "",
|
||||
params: [:]
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -196,19 +359,20 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
func logScreenS2DefOne() {
|
||||
logScreen(
|
||||
name: "s2 def one",
|
||||
path: ""
|
||||
path: "",
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
if extensionContent != expect {
|
||||
print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect))
|
||||
}
|
||||
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||
}
|
||||
|
||||
|
||||
func testGeneratedExtensionContentMatomo() {
|
||||
// Given
|
||||
let sectionOne = AnalyticsCategory(id: "section_one")
|
||||
@@ -216,19 +380,19 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]),
|
||||
getAnalyticsDefinition(id: "s1_def_two", action: "test", category: "test", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]),
|
||||
]
|
||||
|
||||
|
||||
let sectionTwo = AnalyticsCategory(id: "section_two")
|
||||
sectionTwo.definitions = [
|
||||
getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]),
|
||||
getAnalyticsDefinition(id: "s2_def_two", action: "test", category: "test", name: "s2 def two", type: .event, tags: ["droid","droidonly"]),
|
||||
]
|
||||
|
||||
|
||||
let sectionThree = AnalyticsCategory(id: "section_three")
|
||||
sectionThree.definitions = [
|
||||
getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]),
|
||||
getAnalyticsDefinition(id: "s3_def_two", action: "test", category: "test", name: "s3 def two", type: .event, tags: ["droid","droidonly"]),
|
||||
]
|
||||
|
||||
|
||||
// When
|
||||
let extensionContent = AnalyticsGenerator.getExtensionContent(
|
||||
targets: [TrackerType.matomo],
|
||||
@@ -241,80 +405,18 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
let expect = """
|
||||
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
|
||||
|
||||
import Foundation
|
||||
import MatomoTracker
|
||||
|
||||
// MARK: - Protocol
|
||||
\(protocolString())
|
||||
|
||||
protocol AnalyticsManagerProtocol {
|
||||
\(matomoString())
|
||||
|
||||
func logScreen(name: String, path: String)
|
||||
func logEvent(
|
||||
name: String,
|
||||
action: String,
|
||||
category: String,
|
||||
params: [String: Any]?
|
||||
)
|
||||
}
|
||||
// MARK: - Traker Type
|
||||
|
||||
// MARK: - Matomo
|
||||
enum TrackerType: CaseIterable {
|
||||
|
||||
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) {
|
||||
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
|
||||
)
|
||||
}
|
||||
case matomo
|
||||
}
|
||||
|
||||
// MARK: - Manager
|
||||
@@ -323,32 +425,62 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
|
||||
static var shared = AnalyticsManager()
|
||||
|
||||
private init() {}
|
||||
|
||||
// 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) {
|
||||
isEnabled = enable
|
||||
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
|
||||
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) {
|
||||
managers.append(
|
||||
MatomoAnalyticsManager(
|
||||
siteId: siteId,
|
||||
url: url
|
||||
)
|
||||
managers[TrackerType.matomo] = MatomoAnalyticsManager(
|
||||
siteId: siteId,
|
||||
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 }
|
||||
|
||||
managers.forEach { manager in
|
||||
manager.logScreen(name: name, path: path)
|
||||
managers.values.forEach { manager in
|
||||
manager.logScreen(
|
||||
name: name,
|
||||
path: path,
|
||||
params: params
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,7 +492,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
) {
|
||||
guard isEnabled else { return }
|
||||
|
||||
managers.forEach { manager in
|
||||
managers.values.forEach { manager in
|
||||
manager.logEvent(
|
||||
name: name,
|
||||
action: action,
|
||||
@@ -375,7 +507,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
func logScreenS1DefOne() {
|
||||
logScreen(
|
||||
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",
|
||||
action: "test",
|
||||
category: "test",
|
||||
params: [:]
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -393,19 +526,20 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
func logScreenS2DefOne() {
|
||||
logScreen(
|
||||
name: "s2 def one",
|
||||
path: "s2_def_one/"
|
||||
path: "s2_def_one/",
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
if extensionContent != expect {
|
||||
print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect))
|
||||
}
|
||||
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||
}
|
||||
|
||||
|
||||
func testGeneratedExtensionContentMatomoAndFirebase() {
|
||||
// Given
|
||||
let sectionOne = AnalyticsCategory(id: "section_one")
|
||||
@@ -413,19 +547,19 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]),
|
||||
getAnalyticsDefinition(id: "s1_def_two", action: "test", category: "test", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]),
|
||||
]
|
||||
|
||||
|
||||
let sectionTwo = AnalyticsCategory(id: "section_two")
|
||||
sectionTwo.definitions = [
|
||||
getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]),
|
||||
getAnalyticsDefinition(id: "s2_def_two", action: "test", category: "test", name: "s2 def two", type: .event, tags: ["droid","droidonly"]),
|
||||
]
|
||||
|
||||
|
||||
let sectionThree = AnalyticsCategory(id: "section_three")
|
||||
sectionThree.definitions = [
|
||||
getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]),
|
||||
getAnalyticsDefinition(id: "s3_def_two", action: "test", category: "test", name: "s3 def two", type: .event, tags: ["droid","droidonly"]),
|
||||
]
|
||||
|
||||
|
||||
// When
|
||||
let extensionContent = AnalyticsGenerator.getExtensionContent(
|
||||
targets: [TrackerType.matomo, TrackerType.firebase],
|
||||
@@ -439,125 +573,22 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
let expect = """
|
||||
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
|
||||
|
||||
import Foundation
|
||||
import MatomoTracker
|
||||
import FirebaseAnalytics
|
||||
|
||||
// MARK: - Protocol
|
||||
\(protocolString())
|
||||
|
||||
protocol AnalyticsManagerProtocol {
|
||||
\(matomoString())
|
||||
|
||||
func logScreen(name: String, path: String)
|
||||
func logEvent(
|
||||
name: String,
|
||||
action: String,
|
||||
category: String,
|
||||
params: [String: Any]?
|
||||
)
|
||||
}
|
||||
\(firebaseString())
|
||||
|
||||
// MARK: - Matomo
|
||||
// MARK: - Traker Type
|
||||
|
||||
class MatomoAnalyticsManager: AnalyticsManagerProtocol {
|
||||
enum TrackerType: CaseIterable {
|
||||
|
||||
// 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: - 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
|
||||
)
|
||||
}
|
||||
case matomo
|
||||
case firebase
|
||||
}
|
||||
|
||||
// MARK: - Manager
|
||||
@@ -566,33 +597,63 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
|
||||
static var shared = AnalyticsManager()
|
||||
|
||||
private init() {}
|
||||
|
||||
// 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) {
|
||||
isEnabled = enable
|
||||
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
|
||||
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) {
|
||||
managers.append(
|
||||
MatomoAnalyticsManager(
|
||||
siteId: siteId,
|
||||
url: url
|
||||
)
|
||||
managers[TrackerType.matomo] = MatomoAnalyticsManager(
|
||||
siteId: siteId,
|
||||
url: url
|
||||
)
|
||||
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 }
|
||||
|
||||
managers.forEach { manager in
|
||||
manager.logScreen(name: name, path: path)
|
||||
|
||||
managers.values.forEach { manager in
|
||||
manager.logScreen(
|
||||
name: name,
|
||||
path: path,
|
||||
params: params
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,7 +665,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
) {
|
||||
guard isEnabled else { return }
|
||||
|
||||
managers.forEach { manager in
|
||||
managers.values.forEach { manager in
|
||||
manager.logEvent(
|
||||
name: name,
|
||||
action: action,
|
||||
@@ -619,7 +680,8 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
func logScreenS1DefOne() {
|
||||
logScreen(
|
||||
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",
|
||||
action: "test",
|
||||
category: "test",
|
||||
params: [:]
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -637,13 +699,14 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
||||
func logScreenS2DefOne() {
|
||||
logScreen(
|
||||
name: "s2 def one",
|
||||
path: "s2_def_one/"
|
||||
path: "s2_def_one/",
|
||||
params: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
if extensionContent != expect {
|
||||
print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect))
|
||||
}
|
||||
|
Reference in New Issue
Block a user