4 Commits

Author SHA1 Message Date
abb7c8f8c8 Add Swiftlint
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-11 10:13:18 +01:00
92626b76ad Fix Image 2023-12-11 10:12:11 +01:00
449f16499b Fix Font 2023-12-11 10:12:11 +01:00
cd873ca5d9 Fix Color 2023-12-11 10:12:11 +01:00
30 changed files with 211 additions and 488 deletions

View File

@ -18,15 +18,6 @@
"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",
@ -36,24 +27,6 @@
"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",

View File

@ -11,7 +11,6 @@ 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.
@ -21,8 +20,7 @@ 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")]
), ),

View File

@ -133,71 +133,6 @@ 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.

View File

@ -4,25 +4,18 @@ 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")
} }
} }

View File

@ -4,25 +4,18 @@ 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")!
} }
} }

View File

@ -1,32 +0,0 @@
// 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")!
}
}

View File

@ -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 as NSObject AnalyticsParameterScreenName: name
] ]
Analytics.logEvent( Analytics.logEvent(
@ -96,25 +96,19 @@ class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
category: String, category: String,
params: [String: Any]? params: [String: Any]?
) { ) {
var parameters: [String:NSObject] = [ var parameters: [String:Any] = [
"action": action as NSObject, "action": action,
"category": category as NSObject, "category": category,
] ]
if let supplementaryParameters = params { if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters { parameters.merge(supplementaryParameters) { (origin, new) -> Any in
if parameters.contains(where: { (key: String, value: NSObject) in return origin
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
} }
} }
Analytics.logEvent( Analytics.logEvent(
name.replacingOccurrences(of: [" "], with: "_"), name,
parameters: parameters parameters: parameters
) )
} }
@ -175,22 +169,19 @@ class AnalyticsManager {
// MARK: - section_one // MARK: - section_one
func logScreenS1DefOne(title: String) { func logScreenS1DefOne() {
logScreen( logScreen(
name: "s1 def one \(title)", name: "s1 def one",
path: "s1_def_one/\(title)" path: "s1_def_one/"
) )
} }
func logEventS1DefTwo(title: String, count: String) { func logEventS1DefTwo() {
logEvent( logEvent(
name: "s1 def two", name: "s1 def two",
action: "test", action: "test",
category: "test", category: "test",
params: [ params: [:]
"title": title,
"count": count
]
) )
} }

View File

@ -3,13 +3,9 @@ categories:
- id: section_one - id: section_one
screens: screens:
- id: s1_def_one - id: s1_def_one
name: s1 def one _TITLE_ name: s1 def one
path: s1_def_one/_TITLE_ path: s1_def_one/
tags: ios,droid tags: ios
parameters:
- name: title
type: String
replaceIn: name,path
events: events:
- id: s1_def_two - id: s1_def_two
@ -17,11 +13,6 @@ 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:

View File

@ -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 "UIColorYolo" \ # --extension-name-ui-kit "UIhkjhkColorYolo" \
--extension-suffix "GenAllScript" # --extension-suffix "GenAllScript"
# #
#echo "\n-------------------------\n" #echo "\n-------------------------\n"
# #
@ -61,10 +61,10 @@ swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/sampleColors1.txt"
#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"

View File

@ -32,10 +32,6 @@ 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 // Check requirements
@ -66,13 +62,7 @@ struct Analytics: ParsableCommand {
print(error.description) print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
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 // Check if needed to regenerate
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
inputFilePath: options.inputFile, inputFilePath: options.inputFile,
@ -84,3 +74,19 @@ struct Analytics: ParsableCommand {
return true return true
} }
} }
extension Analytics {
enum TargetType: CaseIterable {
case matomo
case firebase
var value: String {
switch self {
case .matomo:
"matomo"
case .firebase:
"firebase"
}
}
}
}

View File

@ -8,20 +8,16 @@
import Foundation import Foundation
enum AnalyticsError: Error { enum AnalyticsError: Error {
case noValidTracker(String)
case fileNotExists(String) case fileNotExists(String)
case missingElement(String) case missingElement(String)
case invalidParameter(String) case invalidParameter(String)
case parseFailed(String)
case writeFile(String, String) case writeFile(String, String)
var description: String { var description: String {
switch self { switch self {
case .noValidTracker(let inputTargets):
return "error: [\(Analytics.toolName)] '\(inputTargets)' ne contient aucun tracker valid"
case .fileNotExists(let filename): case .fileNotExists(let filename):
return "error: [\(Analytics.toolName)] File \(filename) does not exists" return "error: [\(Analytics.toolName)] File \(filename) does not exists "
case .missingElement(let element): case .missingElement(let element):
return "error: [\(Analytics.toolName)] Missing \(element) for Matomo" return "error: [\(Analytics.toolName)] Missing \(element) for Matomo"
@ -29,9 +25,6 @@ enum AnalyticsError: Error {
case .invalidParameter(let reason): case .invalidParameter(let reason):
return "error: [\(Analytics.toolName)] Invalid parameter \(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): case .writeFile(let subErrorDescription, let filename):
return "error: [\(Analytics.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)" return "error: [\(Analytics.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
} }

View File

@ -10,13 +10,13 @@ import ToolCore
import CoreVideo import CoreVideo
class AnalyticsGenerator { class AnalyticsGenerator {
static var targets: [TrackerType] = [] static var targets: [Analytics.TargetType] = []
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: " ")
TrackerType.allCases.forEach { enumTarget in Analytics.TargetType.allCases.forEach { enumTarget in
if targetsString.contains(enumTarget.value) { if targetsString.contains(enumTarget.value) {
targets.append(enumTarget) targets.append(enumTarget)
} }
@ -32,7 +32,7 @@ class AnalyticsGenerator {
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
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 = AnalyticsError.writeFile(extensionFilePath, error.localizedDescription) let error = AnalyticsError.writeFile(extensionFilePath, error.localizedDescription)
print(error.description) print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
@ -58,7 +58,7 @@ class AnalyticsGenerator {
\(Self.getImport()) \(Self.getImport())
\(Self.getAnalyticsProtocol()) \(Self.getAnalytics())
// 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(TrackerType.matomo) { if targets.contains(Analytics.TargetType.matomo) {
result.append("import MatomoTracker") result.append("import MatomoTracker")
} }
if targets.contains(TrackerType.firebase) { if targets.contains(Analytics.TargetType.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(TrackerType.matomo) { if targets.contains(Analytics.TargetType.matomo) {
header = "func configure(siteId: String, url: String) {" header = "func configure(siteId: String, url: String) {"
} else if targets.contains(TrackerType.firebase) { } else if targets.contains(Analytics.TargetType.firebase) {
header = "func configure() {" header = "func configure() {"
} }
if targets.contains(TrackerType.matomo) { if targets.contains(Analytics.TargetType.matomo) {
content.append(""" content.append("""
managers.append( managers.append(
MatomoAnalyticsManager( MatomoAnalyticsManager(
@ -152,7 +152,7 @@ class AnalyticsGenerator {
) )
""") """)
} }
if targets.contains(TrackerType.firebase) { if targets.contains(Analytics.TargetType.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 getAnalyticsProtocol() -> String { private static func getAnalytics() -> 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(TrackerType.matomo) { if targets.contains(Analytics.TargetType.matomo) {
result.append(MatomoGenerator.service) result.append(MatomoGenerator.service.content)
} }
if targets.contains(TrackerType.firebase) { if targets.contains(Analytics.TargetType.firebase) {
result.append(FirebaseGenerator.service) result.append(FirebaseGenerator.service.content)
} }
return result.joined(separator: "\n") return result.joined(separator: "\n")

View File

@ -8,20 +8,19 @@
import Foundation import Foundation
enum FirebaseGenerator { enum FirebaseGenerator {
case service
static var service: String {
var content: String {
[ [
FirebaseGenerator.header, FirebaseGenerator.service.header,
FirebaseGenerator.logScreen, FirebaseGenerator.service.logScreen,
FirebaseGenerator.logEvent, FirebaseGenerator.service.logEvent,
FirebaseGenerator.footer FirebaseGenerator.service.footer
] ]
.joined(separator: "\n") .joined(separator: "\n")
} }
// MARK: - Private vars private var header: String {
private static var header: String {
""" """
// MARK: - Firebase // MARK: - Firebase
@ -29,11 +28,11 @@ enum FirebaseGenerator {
""" """
} }
private static var logScreen: String { private var logScreen: String {
""" """
func logScreen(name: String, path: String) { func logScreen(name: String, path: String) {
var parameters = [ var parameters = [
AnalyticsParameterScreenName: name as NSObject AnalyticsParameterScreenName: name
] ]
Analytics.logEvent( Analytics.logEvent(
@ -45,7 +44,7 @@ enum FirebaseGenerator {
""" """
} }
private static var logEvent: String { private var logEvent: String {
""" """
func logEvent( func logEvent(
name: String, name: String,
@ -53,32 +52,26 @@ enum FirebaseGenerator {
category: String, category: String,
params: [String: Any]? params: [String: Any]?
) { ) {
var parameters: [String:NSObject] = [ var parameters: [String:Any] = [
"action": action as NSObject, "action": action,
"category": category as NSObject, "category": category,
] ]
if let supplementaryParameters = params { if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters { parameters.merge(supplementaryParameters) { (origin, new) -> Any in
if parameters.contains(where: { (key: String, value: NSObject) in return origin
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
} }
} }
Analytics.logEvent( Analytics.logEvent(
name.replacingOccurrences(of: [" "], with: "_"), name,
parameters: parameters parameters: parameters
) )
} }
""" """
} }
private static var footer: String { private var footer: String {
""" """
} }

View File

@ -8,21 +8,20 @@
import Foundation import Foundation
enum MatomoGenerator { enum MatomoGenerator {
case service
static var service: String {
var content: String {
[ [
MatomoGenerator.header, MatomoGenerator.service.header,
MatomoGenerator.setup, MatomoGenerator.service.setup,
MatomoGenerator.logScreen, MatomoGenerator.service.logScreen,
MatomoGenerator.logEvent, MatomoGenerator.service.logEvent,
MatomoGenerator.footer MatomoGenerator.service.footer
] ]
.joined(separator: "\n") .joined(separator: "\n")
} }
// MARK: - Private vars private var header: String {
private static var header: String {
""" """
// MARK: - Matomo // MARK: - Matomo
@ -35,7 +34,7 @@ enum MatomoGenerator {
""" """
} }
private static var setup: String { private var setup: String {
""" """
// MARK: - Init // MARK: - Init
@ -64,7 +63,7 @@ enum MatomoGenerator {
""" """
} }
private static var logScreen: String { private 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 }
@ -80,7 +79,7 @@ enum MatomoGenerator {
""" """
} }
private static var logEvent: String { private var logEvent: String {
""" """
func logEvent( func logEvent(
name: String, name: String,
@ -101,7 +100,7 @@ enum MatomoGenerator {
""" """
} }
private static var footer: String { private var footer: String {
""" """
} }

View File

@ -11,14 +11,10 @@ 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)

View File

@ -6,7 +6,6 @@
// //
import Foundation import Foundation
import ToolCore
class AnalyticsDefinition { class AnalyticsDefinition {
let id: String let id: String
@ -19,16 +18,12 @@ 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
@ -36,8 +31,8 @@ class AnalyticsDefinition {
return true return true
} }
// MARK: - Private Methods // MARK: - 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
@ -101,7 +96,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 = """
[ [
@ -155,3 +150,27 @@ 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
}
}

View File

@ -17,7 +17,9 @@ struct AnalyticsCategoryDTO: Codable {
var events: [AnalyticsDefinitionEventDTO]? var events: [AnalyticsDefinitionEventDTO]?
} }
struct AnalyticsDefinitionScreenDTO: Codable { protocol AnalyticsDefinitionDTO: Codable {}
struct AnalyticsDefinitionScreenDTO: AnalyticsDefinitionDTO {
var id: String var id: String
var name: String var name: String
var tags: String var tags: String
@ -27,7 +29,7 @@ struct AnalyticsDefinitionScreenDTO: Codable {
var path: String? var path: String?
} }
struct AnalyticsDefinitionEventDTO: Codable { struct AnalyticsDefinitionEventDTO: AnalyticsDefinitionDTO {
var id: String var id: String
var name: String var name: String
var tags: String var tags: String

View File

@ -12,8 +12,6 @@ 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

View File

@ -1,16 +0,0 @@
//
// TagType.swift
//
//
// Created by Thibaut Schmitt on 08/12/2023.
//
import Foundation
extension AnalyticsDefinition {
enum TagType {
case screen
case event
}
}

View File

@ -1,29 +0,0 @@
//
// 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
}
}

View File

@ -15,48 +15,45 @@ 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 = AnalyticsError.fileNotExists(inputFile) let error = AnalyticsError.fileNotExists(inputFile)
print(error.description)
Analytics.exit(withError: error) 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 { } catch let error {
let error = AnalyticsError.parseFailed(error.localizedDescription)
print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
} }
private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] { private static func getParameters(fromData data: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
parameters.map { dtoParameter in var parameters: [AnalyticsParameter] = []
data.forEach { value in
// Type // Type
let type = dtoParameter.type.uppercasedFirst() let type = value.type.uppercasedFirst()
guard guard
type == "String" || type == "String" ||
type == "Int" || type == "Int" ||
type == "Double" || type == "Double" ||
type == "Bool" type == "Bool"
else { else {
let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)") let error = AnalyticsError.invalidParameter("type of \(value.name)")
print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
let parameter = AnalyticsParameter( let parameter: AnalyticsParameter = AnalyticsParameter(name: value.name, type: type)
name: dtoParameter.name,
type: type if let replaceIn = value.replaceIn {
)
if let replaceIn = dtoParameter.replaceIn {
parameter.replaceIn = replaceIn.components(separatedBy: ",") parameter.replaceIn = replaceIn.components(separatedBy: ",")
} }
return parameter parameters.append(parameter)
} }
return parameters
} }
private static func getTagDefinition( private static func getTagDefinition(
@ -67,24 +64,24 @@ class AnalyticsFileParser {
comments: String?, comments: String?,
parameters: [AnalyticsParameterDTO]? parameters: [AnalyticsParameterDTO]?
) -> AnalyticsDefinition { ) -> AnalyticsDefinition {
let definition = AnalyticsDefinition(id: id, name: name, type: type) let definition: AnalyticsDefinition = AnalyticsDefinition(id: id, name: name, type: type)
definition.tags = tags definition.tags = tags.components(separatedBy: ",")
.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(from: parameters) definition.parameters = Self.getParameters(fromData: parameters)
} }
return definition return definition
} }
private static func getTagDefinitionScreen(from screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] { private static func getTagDefinitionScreen(fromData screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] {
screens.map { screen in var definitions: [AnalyticsDefinition] = []
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,
@ -93,25 +90,28 @@ class AnalyticsFileParser {
comments: screen.comments, comments: screen.comments,
parameters: screen.parameters parameters: screen.parameters
) )
if target.contains(TrackerType.matomo.value) { if target.contains(Analytics.TargetType.matomo.value) {
// Path // Path
guard let path = screen.path else { guard let path = screen.path else {
let error = AnalyticsError.missingElement("screen path") let error = AnalyticsError.missingElement("screen path")
print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
definition.path = path definition.path = path
} }
return definition definitions.append(definition)
} }
return definitions
} }
private static func getTagDefinitionEvent(from events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] { private static func getTagDefinitionEvent(fromData events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] {
events.map { event in var definitions: [AnalyticsDefinition] = []
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,59 +120,54 @@ class AnalyticsFileParser {
comments: event.comments, comments: event.comments,
parameters: event.parameters parameters: event.parameters
) )
if target.contains(TrackerType.matomo.value) { if target.contains(Analytics.TargetType.matomo.value) {
// Category // Category
guard let category = event.category else { guard let category = event.category else {
let error = AnalyticsError.missingElement("event category") let error = AnalyticsError.missingElement("event category")
print(error.description)
Analytics.exit(withError: error) 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 = AnalyticsError.missingElement("event action") let error = AnalyticsError.missingElement("event action")
print(error.description)
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
definition.action = action definition.action = action
} }
return definition definitions.append(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 = Self.parseYaml() let tagFile: AnalyticsFile = Self.parseYaml()
var sections: [AnalyticsCategory] = []
return tagFile tagFile.categories.forEach { categorie in
.categories let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id)
.map { categorie in
let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id) if let screens = categorie.screens {
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
} }
} }

View File

@ -41,28 +41,12 @@ 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 {

View File

@ -8,8 +8,6 @@
import ToolCore import ToolCore
import Foundation import Foundation
import ArgumentParser import ArgumentParser
import Stencil
import PathKit
struct ResgenSwift: ParsableCommand { struct ResgenSwift: ParsableCommand {
@ -34,20 +32,6 @@ 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()

View File

@ -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,19 +89,4 @@ 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
}
} }

View File

@ -1,21 +0,0 @@
// 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 %}
}

View File

@ -1,4 +0,0 @@
/// Color {{ color.name }} is {{ color.light }} {{ color.light }} or {{ color.dark }} {{ color.dark }}"
{% if isStatic %}static {% endif %}var {{ color.name }}: Color {
Color("{{ color.name }}")
}

View File

@ -1,4 +0,0 @@
/// 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 }}")!
}

View File

@ -51,7 +51,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
] ]
// When // When
AnalyticsGenerator.targets = [TrackerType.firebase] AnalyticsGenerator.targets = [Analytics.TargetType.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 = [TrackerType.matomo] AnalyticsGenerator.targets = [Analytics.TargetType.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 = [TrackerType.matomo, TrackerType.firebase] AnalyticsGenerator.targets = [Analytics.TargetType.matomo, Analytics.TargetType.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,

View File

@ -1,5 +1,5 @@
// //
// DiffString.swift // File.swift
// //
// //
// Created by Loris Perret on 06/12/2023. // Created by Loris Perret on 06/12/2023.
@ -7,6 +7,7 @@
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