Compare commits
15 Commits
f0131e0828
...
stencil
Author | SHA1 | Date | |
---|---|---|---|
3f64e9682c | |||
eed20367b9 | |||
43b5111d79 | |||
2983093a9c | |||
b4bbaa3bfd | |||
498c8fa4ae | |||
2957da6233 | |||
d79af06c38 | |||
d8937f2de6 | |||
9b27f24197 | |||
1d58fd5510 | |||
f6c49bf626 | |||
f1b62d83c4 | |||
ee5055efa5 | |||
6f8e3b6664 |
@ -18,6 +18,15 @@
|
|||||||
"version" : "1.8.0"
|
"version" : "1.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "pathkit",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/kylef/PathKit.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
|
||||||
|
"version" : "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "sourcekitten",
|
"identity" : "sourcekitten",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@ -27,6 +36,24 @@
|
|||||||
"version" : "0.34.1"
|
"version" : "0.34.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "spectre",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/kylef/Spectre.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7",
|
||||||
|
"version" : "0.10.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "stencil",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/stencilproject/Stencil.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "4f222ac85d673f35df29962fc4c36ccfdaf9da5b",
|
||||||
|
"version" : "0.15.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swift-argument-parser",
|
"identity" : "swift-argument-parser",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
@ -11,6 +11,7 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
|
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
|
||||||
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.1"),
|
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.1"),
|
||||||
.package(url: "https://github.com/realm/SwiftLint.git", .upToNextMajor(from: "0.54.0")),
|
.package(url: "https://github.com/realm/SwiftLint.git", .upToNextMajor(from: "0.54.0")),
|
||||||
|
.package(url: "https://github.com/stencilproject/Stencil.git", .upToNextMajor(from: "0.15.1")),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
@ -20,7 +21,8 @@ let package = Package(
|
|||||||
dependencies: [
|
dependencies: [
|
||||||
"ToolCore",
|
"ToolCore",
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||||
"Yams"
|
"Yams",
|
||||||
|
"Stencil",
|
||||||
],
|
],
|
||||||
plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")]
|
plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")]
|
||||||
),
|
),
|
||||||
|
65
README.md
65
README.md
@ -133,6 +133,71 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
|||||||
|
|
||||||
> ⚠️ 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 typaloas `typealias Tags = String`.
|
||||||
|
|
||||||
|
|
||||||
|
## Analytics
|
||||||
|
|
||||||
|
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" \
|
||||||
|
--target "matomo firebase" \
|
||||||
|
--extension-output-path "./Analytics/Generated" \
|
||||||
|
--extension-name "AppAnalytics" \
|
||||||
|
--extension-suffix "GreatApp" \
|
||||||
|
--static-members true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
1. `-f`: force generation
|
||||||
|
2. Input tags file (must be YAML formatted)
|
||||||
|
3. `--target`: target with you will log UX
|
||||||
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
|
5. `--extension-name` *(optional)* : name of class to add the extension
|
||||||
|
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`.
|
||||||
|
|
||||||
|
### YAML
|
||||||
|
|
||||||
|
```
|
||||||
|
- id: s1_def_one
|
||||||
|
name: s1 def one _TITLE_
|
||||||
|
path: s1_def_one/_TITLE_
|
||||||
|
action: Tap
|
||||||
|
category: User
|
||||||
|
tags: ios,droid
|
||||||
|
comments:
|
||||||
|
parameters:
|
||||||
|
- name: title
|
||||||
|
type: String
|
||||||
|
replaceIn: name,path
|
||||||
|
```
|
||||||
|
|
||||||
|
1. `id`: name of the method (method name will be composed of `log` + `Event|Screen` + `id`)
|
||||||
|
2. `name`: name of the tag
|
||||||
|
3. `path` *(optional with firebase)* : needed for matomo but not with firebase (log screen)
|
||||||
|
4. `action` *(optional with firebase)* : needed for matomo but not with firebase (log event)
|
||||||
|
5. `category` *(optional with firebase)* : needed for matomo but not with firebase (log event)
|
||||||
|
6. `tags`: which platform target
|
||||||
|
7. `comments` *(optional)*
|
||||||
|
8. `parameters` *(optional)*
|
||||||
|
|
||||||
|
**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. `replaceIn` *(optional)*
|
||||||
|
|
||||||
|
**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)
|
||||||
|
|
||||||
|
|
||||||
## Images
|
## Images
|
||||||
|
|
||||||
Images generator will generate images assets along with extensions to access those images easily.
|
Images generator will generate images assets along with extensions to access those images easily.
|
||||||
|
@ -4,18 +4,25 @@ import SwiftUI
|
|||||||
|
|
||||||
extension ColorYolo {
|
extension ColorYolo {
|
||||||
|
|
||||||
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
|
||||||
|
/// Color red is #FF0000 #FF0000 or #FF0000 #FF0000"
|
||||||
var red: Color {
|
var red: Color {
|
||||||
Color("red")
|
Color("red")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
|
|
||||||
|
/// Color green_alpha_50 is #A000FF00 #A000FF00 or #A000FF00 #A000FF00"
|
||||||
var green_alpha_50: Color {
|
var green_alpha_50: Color {
|
||||||
Color("green_alpha_50")
|
Color("green_alpha_50")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
|
||||||
|
/// Color blue_light_dark is #0000FF #0000FF or #0000AA #0000AA"
|
||||||
var blue_light_dark: Color {
|
var blue_light_dark: Color {
|
||||||
Color("blue_light_dark")
|
Color("blue_light_dark")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,25 @@ import UIKit
|
|||||||
|
|
||||||
extension UIColorYolo {
|
extension UIColorYolo {
|
||||||
|
|
||||||
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
|
||||||
|
/// Color red is #FF0000 #FF0000 or #FF0000 #FF0000"
|
||||||
@objc var red: UIColor {
|
@objc var red: UIColor {
|
||||||
UIColor(named: "red")!
|
UIColor(named: "red")!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
|
|
||||||
|
/// Color green_alpha_50 is #A000FF00 #A000FF00 or #A000FF00 #A000FF00"
|
||||||
@objc var green_alpha_50: UIColor {
|
@objc var green_alpha_50: UIColor {
|
||||||
UIColor(named: "green_alpha_50")!
|
UIColor(named: "green_alpha_50")!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
|
||||||
|
/// Color blue_light_dark is #0000FF #0000FF or #0000AA #0000AA"
|
||||||
@objc var blue_light_dark: UIColor {
|
@objc var blue_light_dark: UIColor {
|
||||||
UIColor(named: "blue_light_dark")!
|
UIColor(named: "blue_light_dark")!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
// Generated by ResgenSwift.Color 1.2
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
extension UIhkjhkColorYolo {
|
||||||
|
|
||||||
|
|
||||||
|
/// Color red is #FF0000 #FF0000 or #FF0000 #FF0000"
|
||||||
|
@objc var red: UIColor {
|
||||||
|
UIColor(named: "red")!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Color green_alpha_50 is #A000FF00 #A000FF00 or #A000FF00 #A000FF00"
|
||||||
|
@objc var green_alpha_50: UIColor {
|
||||||
|
UIColor(named: "green_alpha_50")!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Color blue_light_dark is #0000FF #0000FF or #0000AA #0000AA"
|
||||||
|
@objc var blue_light_dark: UIColor {
|
||||||
|
UIColor(named: "blue_light_dark")!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
// Generated by ResgenSwift.Analytics 1.2
|
// Generated by ResgenSwift.Analytics 1.2
|
||||||
|
|
||||||
import MatomoTracker
|
import MatomoTracker
|
||||||
import Firebase
|
import FirebaseAnalytics
|
||||||
|
|
||||||
// MARK: - Protocol
|
// MARK: - Protocol
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ class MatomoAnalyticsManager: AnalyticsManagerProtocol {
|
|||||||
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
||||||
func logScreen(name: String, path: String) {
|
func logScreen(name: String, path: String) {
|
||||||
var parameters = [
|
var parameters = [
|
||||||
AnalyticsParameterScreenName: name
|
AnalyticsParameterScreenName: name as NSObject
|
||||||
]
|
]
|
||||||
|
|
||||||
Analytics.logEvent(
|
Analytics.logEvent(
|
||||||
@ -96,19 +96,25 @@ class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
|||||||
category: String,
|
category: String,
|
||||||
params: [String: Any]?
|
params: [String: Any]?
|
||||||
) {
|
) {
|
||||||
var parameters: [String:Any] = [
|
var parameters: [String:NSObject] = [
|
||||||
"action": action,
|
"action": action as NSObject,
|
||||||
"category": category,
|
"category": category as NSObject,
|
||||||
]
|
]
|
||||||
|
|
||||||
if let supplementaryParameters = params {
|
if let supplementaryParameters = params {
|
||||||
parameters.merge(supplementaryParameters) { (origin, new) -> Any in
|
for (newKey, newValue) in supplementaryParameters {
|
||||||
return origin
|
if parameters.contains(where: { (key: String, value: NSObject) in
|
||||||
|
key == newKey
|
||||||
|
}) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters[newKey] = newValue as? NSObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Analytics.logEvent(
|
Analytics.logEvent(
|
||||||
name,
|
name.replacingOccurrences(of: [" "], with: "_"),
|
||||||
parameters: parameters
|
parameters: parameters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -169,19 +175,22 @@ class AnalyticsManager {
|
|||||||
|
|
||||||
// MARK: - section_one
|
// MARK: - section_one
|
||||||
|
|
||||||
func logScreenS1DefOne() {
|
func logScreenS1DefOne(title: String) {
|
||||||
logScreen(
|
logScreen(
|
||||||
name: "s1 def one",
|
name: "s1 def one \(title)",
|
||||||
path: "s1_def_one/"
|
path: "s1_def_one/\(title)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func logEventS1DefTwo() {
|
func logEventS1DefTwo(title: String, count: String) {
|
||||||
logEvent(
|
logEvent(
|
||||||
name: "s1 def two",
|
name: "s1 def two",
|
||||||
action: "test",
|
action: "test",
|
||||||
category: "test",
|
category: "test",
|
||||||
params: []
|
params: [
|
||||||
|
"title": title,
|
||||||
|
"count": count
|
||||||
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,13 @@ categories:
|
|||||||
- id: section_one
|
- id: section_one
|
||||||
screens:
|
screens:
|
||||||
- id: s1_def_one
|
- id: s1_def_one
|
||||||
name: s1 def one
|
name: s1 def one _TITLE_
|
||||||
path: s1_def_one/
|
path: s1_def_one/_TITLE_
|
||||||
tags: ios
|
tags: ios,droid
|
||||||
|
parameters:
|
||||||
|
- name: title
|
||||||
|
type: String
|
||||||
|
replaceIn: name,path
|
||||||
|
|
||||||
events:
|
events:
|
||||||
- id: s1_def_two
|
- id: s1_def_two
|
||||||
@ -13,6 +17,11 @@ categories:
|
|||||||
action: test
|
action: test
|
||||||
category: test
|
category: test
|
||||||
tags: ios
|
tags: ios
|
||||||
|
parameters:
|
||||||
|
- name: title
|
||||||
|
type: String
|
||||||
|
- name: count
|
||||||
|
type: String
|
||||||
|
|
||||||
- id: section_two
|
- id: section_two
|
||||||
screens:
|
screens:
|
||||||
|
@ -12,14 +12,14 @@ FORCE_FLAG="$1"
|
|||||||
#
|
#
|
||||||
#echo "\n-------------------------\n"
|
#echo "\n-------------------------\n"
|
||||||
#
|
#
|
||||||
## Color
|
# Color
|
||||||
#swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/sampleColors1.txt" \
|
swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/sampleColors1.txt" \
|
||||||
# --style all \
|
--style all \
|
||||||
# --xcassets-path "./Colors/colors.xcassets" \
|
--xcassets-path "./Colors/colors.xcassets" \
|
||||||
# --extension-output-path "./Colors/Generated/" \
|
--extension-output-path "./Colors/Generated/" \
|
||||||
# --extension-name "ColorYolo" \
|
--extension-name "ColorYolo" \
|
||||||
# --extension-name-ui-kit "UIhkjhkColorYolo" \
|
--extension-name-ui-kit "UIColorYolo" \
|
||||||
# --extension-suffix "GenAllScript"
|
--extension-suffix "GenAllScript"
|
||||||
#
|
#
|
||||||
#echo "\n-------------------------\n"
|
#echo "\n-------------------------\n"
|
||||||
#
|
#
|
||||||
@ -61,10 +61,10 @@ FORCE_FLAG="$1"
|
|||||||
|
|
||||||
#echo "\n-------------------------\n"
|
#echo "\n-------------------------\n"
|
||||||
#
|
#
|
||||||
# Images
|
## Images
|
||||||
swift run -c release ResgenSwift images $FORCE_FLAG "./Images/sampleImages.txt" \
|
#swift run -c release ResgenSwift images $FORCE_FLAG "./Images/sampleImages.txt" \
|
||||||
--xcassets-path "./Images/imagium.xcassets" \
|
# --xcassets-path "./Images/imagium.xcassets" \
|
||||||
--extension-output-path "./Images/Generated" \
|
# --extension-output-path "./Images/Generated" \
|
||||||
--extension-name "ImageYolo" \
|
# --extension-name "ImageYolo" \
|
||||||
--extension-name-ui-kit "UIImageYolo" \
|
# --extension-name-ui-kit "UIImageYolo" \
|
||||||
--extension-suffix "GenAllScript"
|
# --extension-suffix "GenAllScript"
|
||||||
|
@ -32,8 +32,15 @@ struct Analytics: ParsableCommand {
|
|||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("[\(Self.toolName)] Starting analytics generation")
|
print("[\(Self.toolName)] Starting analytics generation")
|
||||||
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate analytics for target: \(options.target)")
|
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate analytics for target: \(options.target)")
|
||||||
|
|
||||||
|
// Check requirements
|
||||||
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
print("[\(Self.toolName)] Will generate analytics")
|
print("[\(Self.toolName)] Will generate analytics")
|
||||||
|
|
||||||
|
// Check requirements
|
||||||
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
// Parse input file
|
// Parse input file
|
||||||
let sections = AnalyticsFileParser.parse(options.inputFile, target: options.target)
|
let sections = AnalyticsFileParser.parse(options.inputFile, target: options.target)
|
||||||
|
|
||||||
@ -47,20 +54,33 @@ struct Analytics: ParsableCommand {
|
|||||||
|
|
||||||
print("[\(Self.toolName)] Analytics generated")
|
print("[\(Self.toolName)] Analytics generated")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// MARK: - Requirements
|
||||||
extension Analytics {
|
|
||||||
enum TargetType: CaseIterable {
|
private func checkRequirements() -> Bool {
|
||||||
case matomo
|
let fileManager = FileManager()
|
||||||
case firebase
|
|
||||||
|
|
||||||
var value: String {
|
// Input file
|
||||||
switch self {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
case .matomo:
|
let error = AnalyticsError.fileNotExists(options.inputFile)
|
||||||
"matomo"
|
print(error.description)
|
||||||
case .firebase:
|
Analytics.exit(withError: error)
|
||||||
"firebase"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard TrackerType.hasValidTarget(in: options.target) else {
|
||||||
|
let error = AnalyticsError.noValidTracker(options.target)
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if needed to regenerate
|
||||||
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
|
||||||
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath) else {
|
||||||
|
print("[\(Self.toolName)] Analytics are already up to date :) ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
Sources/ResgenSwift/Analytics/AnalyticsError.swift
Normal file
39
Sources/ResgenSwift/Analytics/AnalyticsError.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsError.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 11/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum AnalyticsError: Error {
|
||||||
|
case noValidTracker(String)
|
||||||
|
case fileNotExists(String)
|
||||||
|
case missingElement(String)
|
||||||
|
case invalidParameter(String)
|
||||||
|
case parseFailed(String)
|
||||||
|
case writeFile(String, String)
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
case .noValidTracker(let inputTargets):
|
||||||
|
return "error: [\(Analytics.toolName)] '\(inputTargets)' ne contient aucun tracker valid"
|
||||||
|
|
||||||
|
case .fileNotExists(let filename):
|
||||||
|
return "error: [\(Analytics.toolName)] File \(filename) does not exists"
|
||||||
|
|
||||||
|
case .missingElement(let element):
|
||||||
|
return "error: [\(Analytics.toolName)] Missing \(element) for Matomo"
|
||||||
|
|
||||||
|
case .invalidParameter(let reason):
|
||||||
|
return "error: [\(Analytics.toolName)] Invalid parameter \(reason)"
|
||||||
|
|
||||||
|
case .parseFailed(let baseError):
|
||||||
|
return "error: [\(Analytics.toolName)] Parse input file failed: \(baseError)"
|
||||||
|
|
||||||
|
case .writeFile(let subErrorDescription, let filename):
|
||||||
|
return "error: [\(Analytics.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,13 +10,13 @@ import ToolCore
|
|||||||
import CoreVideo
|
import CoreVideo
|
||||||
|
|
||||||
class AnalyticsGenerator {
|
class AnalyticsGenerator {
|
||||||
static var targets: [Analytics.TargetType] = []
|
static var targets: [TrackerType] = []
|
||||||
|
|
||||||
static func writeExtensionFiles(sections: [AnalyticsCategory], target: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
static func writeExtensionFiles(sections: [AnalyticsCategory], target: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
||||||
// Get target type from enum
|
// Get target type from enum
|
||||||
let targetsString: [String] = target.components(separatedBy: " ")
|
let targetsString: [String] = target.components(separatedBy: " ")
|
||||||
|
|
||||||
Analytics.TargetType.allCases.forEach { enumTarget in
|
TrackerType.allCases.forEach { enumTarget in
|
||||||
if targetsString.contains(enumTarget.value) {
|
if targetsString.contains(enumTarget.value) {
|
||||||
targets.append(enumTarget)
|
targets.append(enumTarget)
|
||||||
}
|
}
|
||||||
@ -33,9 +33,9 @@ class AnalyticsGenerator {
|
|||||||
do {
|
do {
|
||||||
try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
} catch let error {
|
} catch let error {
|
||||||
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
let error = AnalyticsError.writeFile(extensionFilePath, error.localizedDescription)
|
||||||
print(error.description)
|
print(error.description)
|
||||||
Stringium.exit(withError: error)
|
Analytics.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class AnalyticsGenerator {
|
|||||||
|
|
||||||
\(Self.getImport())
|
\(Self.getImport())
|
||||||
|
|
||||||
\(Self.getAnalytics())
|
\(Self.getAnalyticsProtocol())
|
||||||
// MARK: - Manager
|
// MARK: - Manager
|
||||||
|
|
||||||
class AnalyticsManager {
|
class AnalyticsManager {
|
||||||
@ -91,10 +91,10 @@ class AnalyticsGenerator {
|
|||||||
private static func getImport() -> String {
|
private static func getImport() -> String {
|
||||||
var result: [String] = []
|
var result: [String] = []
|
||||||
|
|
||||||
if targets.contains(Analytics.TargetType.matomo) {
|
if targets.contains(TrackerType.matomo) {
|
||||||
result.append("import MatomoTracker")
|
result.append("import MatomoTracker")
|
||||||
}
|
}
|
||||||
if targets.contains(Analytics.TargetType.firebase) {
|
if targets.contains(TrackerType.firebase) {
|
||||||
result.append("import FirebaseAnalytics")
|
result.append("import FirebaseAnalytics")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,13 +136,13 @@ class AnalyticsGenerator {
|
|||||||
var content: [String] = []
|
var content: [String] = []
|
||||||
let footer = " }"
|
let footer = " }"
|
||||||
|
|
||||||
if targets.contains(Analytics.TargetType.matomo) {
|
if targets.contains(TrackerType.matomo) {
|
||||||
header = "func configure(siteId: String, url: String) {"
|
header = "func configure(siteId: String, url: String) {"
|
||||||
} else if targets.contains(Analytics.TargetType.firebase) {
|
} else if targets.contains(TrackerType.firebase) {
|
||||||
header = "func configure() {"
|
header = "func configure() {"
|
||||||
}
|
}
|
||||||
|
|
||||||
if targets.contains(Analytics.TargetType.matomo) {
|
if targets.contains(TrackerType.matomo) {
|
||||||
content.append("""
|
content.append("""
|
||||||
managers.append(
|
managers.append(
|
||||||
MatomoAnalyticsManager(
|
MatomoAnalyticsManager(
|
||||||
@ -152,7 +152,7 @@ class AnalyticsGenerator {
|
|||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
if targets.contains(Analytics.TargetType.firebase) {
|
if targets.contains(TrackerType.firebase) {
|
||||||
content.append(" managers.append(FirebaseAnalyticsManager())")
|
content.append(" managers.append(FirebaseAnalyticsManager())")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ class AnalyticsGenerator {
|
|||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getAnalytics() -> String {
|
private static func getAnalyticsProtocol() -> String {
|
||||||
let proto = """
|
let proto = """
|
||||||
// MARK: - Protocol
|
// MARK: - Protocol
|
||||||
|
|
||||||
@ -182,12 +182,12 @@ class AnalyticsGenerator {
|
|||||||
|
|
||||||
var result: [String] = [proto]
|
var result: [String] = [proto]
|
||||||
|
|
||||||
if targets.contains(Analytics.TargetType.matomo) {
|
if targets.contains(TrackerType.matomo) {
|
||||||
result.append(MatomoGenerator.service.content)
|
result.append(MatomoGenerator.service)
|
||||||
}
|
}
|
||||||
|
|
||||||
if targets.contains(Analytics.TargetType.firebase) {
|
if targets.contains(TrackerType.firebase) {
|
||||||
result.append(FirebaseGenerator.service.content)
|
result.append(FirebaseGenerator.service)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.joined(separator: "\n")
|
return result.joined(separator: "\n")
|
||||||
|
@ -8,19 +8,20 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum FirebaseGenerator {
|
enum FirebaseGenerator {
|
||||||
case service
|
|
||||||
|
static var service: String {
|
||||||
var content: String {
|
|
||||||
[
|
[
|
||||||
FirebaseGenerator.service.header,
|
FirebaseGenerator.header,
|
||||||
FirebaseGenerator.service.logScreen,
|
FirebaseGenerator.logScreen,
|
||||||
FirebaseGenerator.service.logEvent,
|
FirebaseGenerator.logEvent,
|
||||||
FirebaseGenerator.service.footer
|
FirebaseGenerator.footer
|
||||||
]
|
]
|
||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private var header: String {
|
// MARK: - Private vars
|
||||||
|
|
||||||
|
private static var header: String {
|
||||||
"""
|
"""
|
||||||
// MARK: - Firebase
|
// MARK: - Firebase
|
||||||
|
|
||||||
@ -28,11 +29,11 @@ enum FirebaseGenerator {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private var logScreen: String {
|
private static var logScreen: String {
|
||||||
"""
|
"""
|
||||||
func logScreen(name: String, path: String) {
|
func logScreen(name: String, path: String) {
|
||||||
var parameters = [
|
var parameters = [
|
||||||
AnalyticsParameterScreenName: name
|
AnalyticsParameterScreenName: name as NSObject
|
||||||
]
|
]
|
||||||
|
|
||||||
Analytics.logEvent(
|
Analytics.logEvent(
|
||||||
@ -44,7 +45,7 @@ enum FirebaseGenerator {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private var logEvent: String {
|
private static var logEvent: String {
|
||||||
"""
|
"""
|
||||||
func logEvent(
|
func logEvent(
|
||||||
name: String,
|
name: String,
|
||||||
@ -52,26 +53,32 @@ enum FirebaseGenerator {
|
|||||||
category: String,
|
category: String,
|
||||||
params: [String: Any]?
|
params: [String: Any]?
|
||||||
) {
|
) {
|
||||||
var parameters: [String:Any] = [
|
var parameters: [String:NSObject] = [
|
||||||
"action": action,
|
"action": action as NSObject,
|
||||||
"category": category,
|
"category": category as NSObject,
|
||||||
]
|
]
|
||||||
|
|
||||||
if let supplementaryParameters = params {
|
if let supplementaryParameters = params {
|
||||||
parameters.merge(supplementaryParameters) { (origin, new) -> Any in
|
for (newKey, newValue) in supplementaryParameters {
|
||||||
return origin
|
if parameters.contains(where: { (key: String, value: NSObject) in
|
||||||
|
key == newKey
|
||||||
|
}) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters[newKey] = newValue as? NSObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Analytics.logEvent(
|
Analytics.logEvent(
|
||||||
name,
|
name.replacingOccurrences(of: [" "], with: "_"),
|
||||||
parameters: parameters
|
parameters: parameters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private var footer: String {
|
private static var footer: String {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,20 +8,21 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum MatomoGenerator {
|
enum MatomoGenerator {
|
||||||
case service
|
|
||||||
|
static var service: String {
|
||||||
var content: String {
|
|
||||||
[
|
[
|
||||||
MatomoGenerator.service.header,
|
MatomoGenerator.header,
|
||||||
MatomoGenerator.service.setup,
|
MatomoGenerator.setup,
|
||||||
MatomoGenerator.service.logScreen,
|
MatomoGenerator.logScreen,
|
||||||
MatomoGenerator.service.logEvent,
|
MatomoGenerator.logEvent,
|
||||||
MatomoGenerator.service.footer
|
MatomoGenerator.footer
|
||||||
]
|
]
|
||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private var header: String {
|
// MARK: - Private vars
|
||||||
|
|
||||||
|
private static var header: String {
|
||||||
"""
|
"""
|
||||||
// MARK: - Matomo
|
// MARK: - Matomo
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ enum MatomoGenerator {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private var setup: String {
|
private static var setup: String {
|
||||||
"""
|
"""
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
@ -63,7 +64,7 @@ enum MatomoGenerator {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private var logScreen: String {
|
private static var logScreen: String {
|
||||||
"""
|
"""
|
||||||
func logScreen(name: String, path: String) {
|
func logScreen(name: String, path: String) {
|
||||||
guard !tracker.isOptedOut else { return }
|
guard !tracker.isOptedOut else { return }
|
||||||
@ -79,7 +80,7 @@ enum MatomoGenerator {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private var logEvent: String {
|
private static var logEvent: String {
|
||||||
"""
|
"""
|
||||||
func logEvent(
|
func logEvent(
|
||||||
name: String,
|
name: String,
|
||||||
@ -100,7 +101,7 @@ enum MatomoGenerator {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private var footer: String {
|
private static var footer: String {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,10 +11,14 @@ class AnalyticsCategory {
|
|||||||
let id: String // OnBoarding
|
let id: String // OnBoarding
|
||||||
var definitions = [AnalyticsDefinition]()
|
var definitions = [AnalyticsDefinition]()
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
init(id: String) {
|
init(id: String) {
|
||||||
self.id = id
|
self.id = id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
|
func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
|
||||||
let allTags = definitions.flatMap { $0.tags }
|
let allTags = definitions.flatMap { $0.tags }
|
||||||
let allTagsSet = Set(allTags)
|
let allTagsSet = Set(allTags)
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
class AnalyticsDefinition {
|
class AnalyticsDefinition {
|
||||||
let id: String
|
let id: String
|
||||||
@ -18,12 +19,16 @@ class AnalyticsDefinition {
|
|||||||
var parameters: [AnalyticsParameter] = []
|
var parameters: [AnalyticsParameter] = []
|
||||||
var type: TagType
|
var type: TagType
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
init(id: String, name: String, type: TagType) {
|
init(id: String, name: String, type: TagType) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type = type
|
self.type = type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool {
|
func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool {
|
||||||
if Set(inputTags).isDisjoint(with: tags) {
|
if Set(inputTags).isDisjoint(with: tags) {
|
||||||
return false
|
return false
|
||||||
@ -31,8 +36,8 @@ class AnalyticsDefinition {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Methods
|
// MARK: - Private Methods
|
||||||
|
|
||||||
private func getFuncName() -> String {
|
private func getFuncName() -> String {
|
||||||
var pascalCaseTitle: String = ""
|
var pascalCaseTitle: String = ""
|
||||||
id.components(separatedBy: "_").forEach { word in
|
id.components(separatedBy: "_").forEach { word in
|
||||||
@ -96,7 +101,7 @@ class AnalyticsDefinition {
|
|||||||
supplementaryParams.forEach { param in
|
supplementaryParams.forEach { param in
|
||||||
params.append("\"\(param.name)\": \(param.name)")
|
params.append("\"\(param.name)\": \(param.name)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.count > 1 {
|
if params.count > 1 {
|
||||||
result = """
|
result = """
|
||||||
[
|
[
|
||||||
@ -150,27 +155,3 @@ class AnalyticsDefinition {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AnalyticsDefinition {
|
|
||||||
enum TagType {
|
|
||||||
case screen
|
|
||||||
case event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
func replacingFirstOccurrence(of: String, with: String) -> Self {
|
|
||||||
if let range = self.range(of: of) {
|
|
||||||
let tmp = self.replacingOccurrences(
|
|
||||||
of: of,
|
|
||||||
with: with,
|
|
||||||
options: .literal,
|
|
||||||
range: range
|
|
||||||
)
|
|
||||||
|
|
||||||
return tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -17,9 +17,7 @@ struct AnalyticsCategoryDTO: Codable {
|
|||||||
var events: [AnalyticsDefinitionEventDTO]?
|
var events: [AnalyticsDefinitionEventDTO]?
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol AnalyticsDefinitionDTO: Codable {}
|
struct AnalyticsDefinitionScreenDTO: Codable {
|
||||||
|
|
||||||
struct AnalyticsDefinitionScreenDTO: AnalyticsDefinitionDTO {
|
|
||||||
var id: String
|
var id: String
|
||||||
var name: String
|
var name: String
|
||||||
var tags: String
|
var tags: String
|
||||||
@ -29,7 +27,7 @@ struct AnalyticsDefinitionScreenDTO: AnalyticsDefinitionDTO {
|
|||||||
var path: String?
|
var path: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AnalyticsDefinitionEventDTO: AnalyticsDefinitionDTO {
|
struct AnalyticsDefinitionEventDTO: Codable {
|
||||||
var id: String
|
var id: String
|
||||||
var name: String
|
var name: String
|
||||||
var tags: String
|
var tags: String
|
||||||
|
@ -12,6 +12,8 @@ class AnalyticsParameter {
|
|||||||
var type: String
|
var type: String
|
||||||
var replaceIn: [String] = []
|
var replaceIn: [String] = []
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
init(name: String, type: String) {
|
init(name: String, type: String) {
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type = type
|
self.type = type
|
||||||
|
16
Sources/ResgenSwift/Analytics/Model/TagType.swift
Normal file
16
Sources/ResgenSwift/Analytics/Model/TagType.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// TagType.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 08/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension AnalyticsDefinition {
|
||||||
|
|
||||||
|
enum TagType {
|
||||||
|
case screen
|
||||||
|
case event
|
||||||
|
}
|
||||||
|
}
|
29
Sources/ResgenSwift/Analytics/Model/TargetType.swift
Normal file
29
Sources/ResgenSwift/Analytics/Model/TargetType.swift
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// TargetType.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 08/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum TrackerType: CaseIterable {
|
||||||
|
case matomo
|
||||||
|
case firebase
|
||||||
|
|
||||||
|
var value: String {
|
||||||
|
switch self {
|
||||||
|
case .matomo:
|
||||||
|
"matomo"
|
||||||
|
case .firebase:
|
||||||
|
"firebase"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func hasValidTarget(in targets: String) -> Bool {
|
||||||
|
for tracker in Self.allCases where targets.contains(tracker.value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -14,46 +14,49 @@ class AnalyticsFileParser {
|
|||||||
|
|
||||||
private static func parseYaml() -> AnalyticsFile {
|
private static func parseYaml() -> AnalyticsFile {
|
||||||
guard let data = FileManager().contents(atPath: inputFile) else {
|
guard let data = FileManager().contents(atPath: inputFile) else {
|
||||||
let error = GenerateError.fileNotExists(inputFile)
|
let error = AnalyticsError.fileNotExists(inputFile)
|
||||||
Generate.exit(withError: error)
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let tagFile = try YAMLDecoder().decode(AnalyticsFile.self, from: data)
|
let tagFile = try YAMLDecoder().decode(AnalyticsFile.self, from: data)
|
||||||
return tagFile
|
return tagFile
|
||||||
} catch let error {
|
} catch {
|
||||||
Generate.exit(withError: error)
|
let error = AnalyticsError.parseFailed(error.localizedDescription)
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getParameters(fromData data: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
|
private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
|
||||||
var parameters: [AnalyticsParameter] = []
|
parameters.map { dtoParameter in
|
||||||
|
|
||||||
data.forEach { value in
|
|
||||||
// Type
|
// Type
|
||||||
|
|
||||||
let type = value.type.uppercasedFirst()
|
let type = dtoParameter.type.uppercasedFirst()
|
||||||
|
|
||||||
guard
|
guard
|
||||||
type == "String" ||
|
type == "String" ||
|
||||||
type == "Int" ||
|
type == "Int" ||
|
||||||
type == "Double" ||
|
type == "Double" ||
|
||||||
type == "Bool"
|
type == "Bool"
|
||||||
else {
|
else {
|
||||||
let error = GenerateError.invalidParameter("type of \(value.name)")
|
let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)")
|
||||||
Generate.exit(withError: error)
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
let parameter: AnalyticsParameter = AnalyticsParameter(name: value.name, type: type)
|
let parameter = AnalyticsParameter(
|
||||||
|
name: dtoParameter.name,
|
||||||
if let replaceIn = value.replaceIn {
|
type: type
|
||||||
|
)
|
||||||
|
|
||||||
|
if let replaceIn = dtoParameter.replaceIn {
|
||||||
parameter.replaceIn = replaceIn.components(separatedBy: ",")
|
parameter.replaceIn = replaceIn.components(separatedBy: ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters.append(parameter)
|
return parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
return parameters
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getTagDefinition(
|
private static func getTagDefinition(
|
||||||
@ -64,24 +67,24 @@ class AnalyticsFileParser {
|
|||||||
comments: String?,
|
comments: String?,
|
||||||
parameters: [AnalyticsParameterDTO]?
|
parameters: [AnalyticsParameterDTO]?
|
||||||
) -> AnalyticsDefinition {
|
) -> AnalyticsDefinition {
|
||||||
let definition: AnalyticsDefinition = AnalyticsDefinition(id: id, name: name, type: type)
|
let definition = AnalyticsDefinition(id: id, name: name, type: type)
|
||||||
definition.tags = tags.components(separatedBy: ",")
|
definition.tags = tags
|
||||||
|
.components(separatedBy: ",")
|
||||||
|
.map { $0.removeLeadingTrailingWhitespace() }
|
||||||
|
|
||||||
if let comments = comments {
|
if let comments = comments {
|
||||||
definition.comments = comments
|
definition.comments = comments
|
||||||
}
|
}
|
||||||
|
|
||||||
if let parameters = parameters {
|
if let parameters = parameters {
|
||||||
definition.parameters = Self.getParameters(fromData: parameters)
|
definition.parameters = Self.getParameters(from: parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
return definition
|
return definition
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getTagDefinitionScreen(fromData screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] {
|
private static func getTagDefinitionScreen(from screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] {
|
||||||
var definitions: [AnalyticsDefinition] = []
|
screens.map { screen in
|
||||||
|
|
||||||
for screen in screens {
|
|
||||||
let definition: AnalyticsDefinition = Self.getTagDefinition(
|
let definition: AnalyticsDefinition = Self.getTagDefinition(
|
||||||
id: screen.id,
|
id: screen.id,
|
||||||
name: screen.name,
|
name: screen.name,
|
||||||
@ -90,28 +93,25 @@ class AnalyticsFileParser {
|
|||||||
comments: screen.comments,
|
comments: screen.comments,
|
||||||
parameters: screen.parameters
|
parameters: screen.parameters
|
||||||
)
|
)
|
||||||
|
|
||||||
if target.contains(Analytics.TargetType.matomo.value) {
|
if target.contains(TrackerType.matomo.value) {
|
||||||
// Path
|
// Path
|
||||||
|
|
||||||
guard let path = screen.path else {
|
guard let path = screen.path else {
|
||||||
let error = GenerateError.missingElement("screen path")
|
let error = AnalyticsError.missingElement("screen path")
|
||||||
Generate.exit(withError: error)
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
definition.path = path
|
definition.path = path
|
||||||
}
|
}
|
||||||
|
|
||||||
definitions.append(definition)
|
return definition
|
||||||
}
|
}
|
||||||
|
|
||||||
return definitions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getTagDefinitionEvent(fromData events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] {
|
private static func getTagDefinitionEvent(from events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] {
|
||||||
var definitions: [AnalyticsDefinition] = []
|
events.map { event in
|
||||||
|
|
||||||
for event in events {
|
|
||||||
let definition: AnalyticsDefinition = Self.getTagDefinition(
|
let definition: AnalyticsDefinition = Self.getTagDefinition(
|
||||||
id: event.id,
|
id: event.id,
|
||||||
name: event.name,
|
name: event.name,
|
||||||
@ -120,54 +120,59 @@ class AnalyticsFileParser {
|
|||||||
comments: event.comments,
|
comments: event.comments,
|
||||||
parameters: event.parameters
|
parameters: event.parameters
|
||||||
)
|
)
|
||||||
|
|
||||||
if target.contains(Analytics.TargetType.matomo.value) {
|
if target.contains(TrackerType.matomo.value) {
|
||||||
// Category
|
// Category
|
||||||
|
|
||||||
guard let category = event.category else {
|
guard let category = event.category else {
|
||||||
let error = GenerateError.missingElement("event category")
|
let error = AnalyticsError.missingElement("event category")
|
||||||
Generate.exit(withError: error)
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
definition.category = category
|
definition.category = category
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
|
|
||||||
guard let action = event.action else {
|
guard let action = event.action else {
|
||||||
let error = GenerateError.missingElement("event action")
|
let error = AnalyticsError.missingElement("event action")
|
||||||
Generate.exit(withError: error)
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
definition.action = action
|
definition.action = action
|
||||||
}
|
}
|
||||||
|
|
||||||
definitions.append(definition)
|
return definition
|
||||||
}
|
}
|
||||||
|
|
||||||
return definitions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func parse(_ inputFile: String, target: String) -> [AnalyticsCategory] {
|
static func parse(_ inputFile: String, target: String) -> [AnalyticsCategory] {
|
||||||
self.inputFile = inputFile
|
self.inputFile = inputFile
|
||||||
self.target = target
|
self.target = target
|
||||||
|
|
||||||
let tagFile: AnalyticsFile = Self.parseYaml()
|
let tagFile = Self.parseYaml()
|
||||||
var sections: [AnalyticsCategory] = []
|
|
||||||
|
|
||||||
tagFile.categories.forEach { categorie in
|
return tagFile
|
||||||
let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id)
|
.categories
|
||||||
|
.map { categorie in
|
||||||
if let screens = categorie.screens {
|
let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id)
|
||||||
section.definitions.append(contentsOf: Self.getTagDefinitionScreen(fromData: screens))
|
|
||||||
|
if let screens = categorie.screens {
|
||||||
|
section
|
||||||
|
.definitions
|
||||||
|
.append(
|
||||||
|
contentsOf: Self.getTagDefinitionScreen(from: screens)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let events = categorie.events {
|
||||||
|
section
|
||||||
|
.definitions
|
||||||
|
.append(
|
||||||
|
contentsOf: Self.getTagDefinitionEvent(from: events)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return section
|
||||||
}
|
}
|
||||||
|
|
||||||
if let events = categorie.events {
|
|
||||||
section.definitions.append(contentsOf: Self.getTagDefinitionEvent(fromData: events))
|
|
||||||
}
|
|
||||||
|
|
||||||
sections.append(section)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sections
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,12 +41,28 @@ struct ColorExtensionGenerator {
|
|||||||
staticVar: Bool,
|
staticVar: Bool,
|
||||||
extensionName: String,
|
extensionName: String,
|
||||||
isSwiftUI: Bool) -> String {
|
isSwiftUI: Bool) -> String {
|
||||||
[
|
// [
|
||||||
Self.getHeader(extensionClassname: extensionName, isSwiftUI: isSwiftUI),
|
// Self.getHeader(extensionClassname: extensionName, isSwiftUI: isSwiftUI),
|
||||||
Self.getProperties(for: colors, withStaticVar: staticVar, isSwiftUI: isSwiftUI),
|
// Self.getProperties(for: colors, withStaticVar: staticVar, isSwiftUI: isSwiftUI),
|
||||||
Self.getFooter()
|
// Self.getFooter()
|
||||||
]
|
// ]
|
||||||
.joined(separator: "\n")
|
// .joined(separator: "\n")
|
||||||
|
do {
|
||||||
|
let context: [String: Any] = [
|
||||||
|
"colors": colors,
|
||||||
|
"isStatic": staticVar,
|
||||||
|
"isSwiftUI": isSwiftUI,
|
||||||
|
"toolName": Colors.toolName,
|
||||||
|
"resgenVersion": ResgenSwiftVersion,
|
||||||
|
"extensionClassname": extensionName
|
||||||
|
]
|
||||||
|
|
||||||
|
return try ResgenSwift.environment.renderTemplate(name: "Colors/main_template", context: context)
|
||||||
|
} catch {
|
||||||
|
// let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
||||||
|
// print(error.description)
|
||||||
|
Colors.exit(withError: error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getHeader(extensionClassname: String, isSwiftUI: Bool) -> String {
|
private static func getHeader(extensionClassname: String, isSwiftUI: Bool) -> String {
|
||||||
|
@ -13,11 +13,6 @@ enum GenerateError: Error {
|
|||||||
case commandError([String], String)
|
case commandError([String], String)
|
||||||
case writeFile(String, String)
|
case writeFile(String, String)
|
||||||
|
|
||||||
// Analytics
|
|
||||||
|
|
||||||
case missingElement(String)
|
|
||||||
case invalidParameter(String)
|
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .fileNotExists(let filename):
|
case .fileNotExists(let filename):
|
||||||
@ -34,12 +29,6 @@ enum GenerateError: Error {
|
|||||||
|
|
||||||
case .writeFile(let filename, let info):
|
case .writeFile(let filename, let info):
|
||||||
return "error: [\(Generate.toolName)] An error occured while writing file in \(filename): \(info)"
|
return "error: [\(Generate.toolName)] An error occured while writing file in \(filename): \(info)"
|
||||||
|
|
||||||
case .missingElement(let element):
|
|
||||||
return "error: [\(Generate.toolName)] Missing \(element) for Matomo"
|
|
||||||
|
|
||||||
case .invalidParameter(let reason):
|
|
||||||
return "error: [\(Generate.toolName)] Invalid parameter \(reason)"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
import ToolCore
|
import ToolCore
|
||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
import Stencil
|
||||||
|
import PathKit
|
||||||
|
|
||||||
struct ResgenSwift: ParsableCommand {
|
struct ResgenSwift: ParsableCommand {
|
||||||
|
|
||||||
@ -32,6 +34,20 @@ struct ResgenSwift: ParsableCommand {
|
|||||||
// subcommand is not given on the command line.
|
// subcommand is not given on the command line.
|
||||||
//defaultSubcommand: Twine.self
|
//defaultSubcommand: Twine.self
|
||||||
)
|
)
|
||||||
|
|
||||||
|
static let projectDirectory = URL(fileURLWithPath: #file) // ProjectDir/Sources/ResgenSwift/main.swift
|
||||||
|
.deletingLastPathComponent() // ProjectDir/Sources/ResgenSwift/
|
||||||
|
.deletingLastPathComponent() // ProjectDir/Sources/
|
||||||
|
.deletingLastPathComponent() // ProjectDir/
|
||||||
|
|
||||||
|
static let environment = Environment(
|
||||||
|
loader: FileSystemLoader(
|
||||||
|
paths: [
|
||||||
|
Path("\(projectDirectory.path)/Templates/")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print(ResgenSwift.projectDirectory.path)
|
||||||
ResgenSwift.main()
|
ResgenSwift.main()
|
||||||
|
@ -63,7 +63,7 @@ public extension String {
|
|||||||
replacingOccurrences(of: "~", with: "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)")
|
replacingOccurrences(of: "~", with: "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func colorComponent() -> (alpha: String, red: String, green: String, blue: String) {
|
func colorComponent() -> (alpha: String, red: String, green: String, blue: String) {
|
||||||
var alpha: String = "FF"
|
var alpha: String = "FF"
|
||||||
var red: String
|
var red: String
|
||||||
var green: String
|
var green: String
|
||||||
@ -89,4 +89,19 @@ public extension String {
|
|||||||
func uppercasedFirst() -> String {
|
func uppercasedFirst() -> String {
|
||||||
prefix(1).uppercased() + dropFirst()
|
prefix(1).uppercased() + dropFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func replacingFirstOccurrence(of: String, with: String) -> Self {
|
||||||
|
if let range = self.range(of: of) {
|
||||||
|
let tmp = self.replacingOccurrences(
|
||||||
|
of: of,
|
||||||
|
with: with,
|
||||||
|
options: .literal,
|
||||||
|
range: range
|
||||||
|
)
|
||||||
|
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
0
Templates/Colors/json_template
Normal file
0
Templates/Colors/json_template
Normal file
21
Templates/Colors/main_template
Normal file
21
Templates/Colors/main_template
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Generated by ResgenSwift.{{ toolName }} {{ resgenVersion }}
|
||||||
|
{% if isSwiftUI %}
|
||||||
|
import SwiftUI
|
||||||
|
{% else %}
|
||||||
|
import UIKit
|
||||||
|
{% endif %}
|
||||||
|
extension {{ extensionClassname }} {
|
||||||
|
{% for color in colors %}
|
||||||
|
{% if isSwiftUI %}
|
||||||
|
/// Color {{ color.name }} is {{ color.light }} {{ color.light }} or {{ color.dark }} {{ color.dark }}"
|
||||||
|
{% if isStatic %}static {% endif %}var {{ color.name }}: Color {
|
||||||
|
Color("{{ color.name }}")
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
/// Color {{ color.name }} is {{ color.light }} {{ color.light }} or {{ color.dark }} {{ color.dark }}"
|
||||||
|
{% if isStatic %}static {% else %}@objc {% endif %}var {{ color.name }}: UIColor {
|
||||||
|
UIColor(named: "{{ color.name }}")!
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
4
Templates/Colors/swiftui_template
Normal file
4
Templates/Colors/swiftui_template
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/// Color {{ color.name }} is {{ color.light }} {{ color.light }} or {{ color.dark }} {{ color.dark }}"
|
||||||
|
{% if isStatic %}static {% endif %}var {{ color.name }}: Color {
|
||||||
|
Color("{{ color.name }}")
|
||||||
|
}
|
4
Templates/Colors/uikit_template
Normal file
4
Templates/Colors/uikit_template
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/// Color {{ color.name }} is {{ color.light }} {{ color.light }} or {{ color.dark }} {{ color.dark }}"
|
||||||
|
{% if isStatic %}static {% else %}@objc {% endif %}var {{ color.name }}: UIColor {
|
||||||
|
UIColor(named: "{{ color.name }}")!
|
||||||
|
}
|
@ -51,7 +51,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
|||||||
]
|
]
|
||||||
|
|
||||||
// When
|
// When
|
||||||
AnalyticsGenerator.targets = [Analytics.TargetType.firebase]
|
AnalyticsGenerator.targets = [TrackerType.firebase]
|
||||||
let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree],
|
let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree],
|
||||||
tags: ["ios", "iosonly"],
|
tags: ["ios", "iosonly"],
|
||||||
staticVar: false,
|
staticVar: false,
|
||||||
@ -216,7 +216,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
|||||||
]
|
]
|
||||||
|
|
||||||
// When
|
// When
|
||||||
AnalyticsGenerator.targets = [Analytics.TargetType.matomo]
|
AnalyticsGenerator.targets = [TrackerType.matomo]
|
||||||
let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree],
|
let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree],
|
||||||
tags: ["ios", "iosonly"],
|
tags: ["ios", "iosonly"],
|
||||||
staticVar: false,
|
staticVar: false,
|
||||||
@ -409,7 +409,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
|
|||||||
]
|
]
|
||||||
|
|
||||||
// When
|
// When
|
||||||
AnalyticsGenerator.targets = [Analytics.TargetType.matomo, Analytics.TargetType.firebase]
|
AnalyticsGenerator.targets = [TrackerType.matomo, TrackerType.firebase]
|
||||||
let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree],
|
let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree],
|
||||||
tags: ["ios", "iosonly"],
|
tags: ["ios", "iosonly"],
|
||||||
staticVar: false,
|
staticVar: false,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// DiffString.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Loris Perret on 06/12/2023.
|
// Created by Loris Perret on 06/12/2023.
|
||||||
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
/// Find first differing character between two strings
|
/// Find first differing character between two strings
|
||||||
///
|
///
|
||||||
/// :param: s1 First String
|
/// :param: s1 First String
|
||||||
|
Reference in New Issue
Block a user