15 Commits

Author SHA1 Message Date
3f64e9682c Add stencil on color
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
PixeeBox/resgen.swift/pipeline/head There was a failure building this commit
2023-12-15 09:49:11 +01:00
eed20367b9 fix: Edit for NSObject in Firebase
Some checks failed
gitea-openium/resgen.swift/pipeline/pr-master There was a failure building this commit
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
PixeeBox/resgen.swift/pipeline/head There was a failure building this commit
2023-12-13 11:24:21 +01:00
43b5111d79 Actualiser Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-13 10:39:12 +01:00
2983093a9c Add Swiftlint 2023-12-13 10:39:12 +01:00
b4bbaa3bfd Fix Image 2023-12-13 10:39:12 +01:00
498c8fa4ae Fix Font 2023-12-13 10:39:12 +01:00
2957da6233 Fix Color 2023-12-13 10:39:12 +01:00
d79af06c38 docs: Add Analytics section in README
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-13 10:27:35 +01:00
d8937f2de6 docs: Add Analytics section in README 2023-12-13 10:23:31 +01:00
9b27f24197 docs: Add Analytics section in README 2023-12-13 10:20:53 +01:00
1d58fd5510 Edit sample file
Some checks failed
gitea-openium/resgen.swift/pipeline/pr-master There was a failure building this commit
2023-12-12 16:58:25 +01:00
f6c49bf626 Add parse error
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-11 11:19:19 +01:00
f1b62d83c4 Add missing print error
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-11 10:29:19 +01:00
ee5055efa5 Retours sur la structure du code
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-11 10:24:42 +01:00
6f8e3b6664 Add error handling
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-11 10:09:24 +01:00
59 changed files with 794 additions and 363 deletions

View File

@ -13,7 +13,7 @@
- Update plist `UIAppFonts` when generated fonts (use plistBuddy) - Update plist `UIAppFonts` when generated fonts (use plistBuddy)
- New parameter: `infoPlistPaths` - New parameter: `infoPlistPaths`
- Generate SwiftUI extensions for colors, fonts and images - Generate SwiftUI extensions for colors, fonts and images
- New parameter: `extensionNameSwiftUI` - New parameter: `extensionNameUIKit`
- Adding Makefile to install, unsintall and create man page. - Adding Makefile to install, unsintall and create man page.
## Fixes ## Fixes

View File

@ -1,12 +1,102 @@
{ {
"pins" : [ "pins" : [
{
"identity" : "collectionconcurrencykit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git",
"state" : {
"revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95",
"version" : "0.2.0"
}
},
{
"identity" : "cryptoswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
"state" : {
"revision" : "db51c407d3be4a051484a141bf0bff36c43d3b1e",
"version" : "1.8.0"
}
},
{
"identity" : "pathkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/PathKit.git",
"state" : {
"revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
"version" : "1.0.1"
}
},
{
"identity" : "sourcekitten",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/SourceKitten.git",
"state" : {
"revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56",
"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",
"location" : "https://github.com/apple/swift-argument-parser", "location" : "https://github.com/apple/swift-argument-parser",
"state" : { "state" : {
"revision" : "9f39744e025c7d377987f30b03770805dcb0bcd1", "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
"version" : "1.1.4" "version" : "1.2.3"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036",
"version" : "509.0.2"
}
},
{
"identity" : "swiftlint",
"kind" : "remoteSourceControl",
"location" : "https://github.com/realm/SwiftLint.git",
"state" : {
"revision" : "f17a4f9dfb6a6afb0408426354e4180daaf49cee",
"version" : "0.54.0"
}
},
{
"identity" : "swiftytexttable",
"kind" : "remoteSourceControl",
"location" : "https://github.com/scottrhoyt/SwiftyTextTable.git",
"state" : {
"revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3",
"version" : "0.9.0"
}
},
{
"identity" : "swxmlhash",
"kind" : "remoteSourceControl",
"location" : "https://github.com/drmohundro/SWXMLHash.git",
"state" : {
"revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f",
"version" : "7.0.2"
} }
}, },
{ {
@ -14,8 +104,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams.git", "location" : "https://github.com/jpsim/Yams.git",
"state" : { "state" : {
"revision" : "01835dc202670b5bb90d07f3eae41867e9ed29f6", "revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3",
"version" : "5.0.1" "version" : "5.0.6"
} }
} }
], ],

View File

@ -9,7 +9,9 @@ let package = Package(
dependencies: [ dependencies: [
// Dependencies declare other packages that this package depends on. // Dependencies declare other packages that this package depends on.
.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/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.
@ -19,8 +21,10 @@ 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")]
), ),
// Helper targets // Helper targets

View File

@ -16,7 +16,7 @@ iOS required to use the **real name** of the font, this name can be different fr
swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \ swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \
--extension-output-path "./Fonts/Generated" \ --extension-output-path "./Fonts/Generated" \
--extension-name "AppFont" \ --extension-name "AppFont" \
--extension-name-swift-ui "SUIAppFont" \ --extension-name-ui-kit "UIAppFont" \
--extension-suffix "GreatApp" \ --extension-suffix "GreatApp" \
--static-members true \ --static-members true \
--info-plist-paths "./path/one/to/Info.plist ./path/two/to/Info.plist" --info-plist-paths "./path/one/to/Info.plist ./path/two/to/Info.plist"
@ -27,8 +27,8 @@ swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \
1. `-f`: force generation 1. `-f`: force generation
2. Font input folder, it will search for every `.ttf` and `.otf` files specified in `fonts.txt` 2. Font input folder, it will search for every `.ttf` and `.otf` files specified in `fonts.txt`
3. `--extension-output-path`: path where to generate generated extension 3. `--extension-output-path`: path where to generate generated extension
4. `--extension-name` *(optional)* : name of the class to add UIKit getters 4. `--extension-name` *(optional)* : name of the class to add SwiftUI getters
5. `--extension-name-swift-ui` *(optional)* : name of the class to add SwiftUI getters 5. `--extension-name-ui-kit` *(optional)* : name of the class to add UIKit getters
6. `--extension-suffix` *(optional)* : additional text which is added to the filename (ex: `AppFont+GreatApp.swift`) 6. `--extension-suffix` *(optional)* : additional text which is added to the filename (ex: `AppFont+GreatApp.swift`)
7. `--static-members` *(optional)*: generate static properties or not 7. `--static-members` *(optional)*: generate static properties or not
8. `--info-plist-paths` *(optional)*: array of `.plist`, you can specify multiple `Info.plist` for multiple targets 8. `--info-plist-paths` *(optional)*: array of `.plist`, you can specify multiple `Info.plist` for multiple targets
@ -44,7 +44,7 @@ swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \
--xcassets-path "./Colors/colors.xcassets" \ --xcassets-path "./Colors/colors.xcassets" \
--extension-output-path "./Colors/Generated/" \ --extension-output-path "./Colors/Generated/" \
--extension-name "AppColor" \ --extension-name "AppColor" \
--extension-name-swift-ui "SUIAppColor" \ --extension-name-ui-kit "UIAppColor" \
--extension-suffix "GreatApp" \ --extension-suffix "GreatApp" \
--static-members true --static-members true
``` ```
@ -55,8 +55,8 @@ swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \
2. Input colors file 2. Input colors file
3. `--style` can be `all` or `light` 3. `--style` can be `all` or `light`
4. `--extension-output-path`: path where to generate generated extension 4. `--extension-output-path`: path where to generate generated extension
5. `--extension-name` *(optional)* : name of the class to add UIKit getters 5. `--extension-name` *(optional)* : name of the class to add SwiftUI getters
6. `--extension-name-swift-ui` *(optional)* : name of the class to add SwiftUI getters 6. `--extension-name-ui-kit` *(optional)* : name of the class to add UIKit getters
7. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppColor+GreatApp.swift`) 7. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppColor+GreatApp.swift`)
8. `--static-members` *(optional)*: generate static properties or not 8. `--static-members` *(optional)*: generate static properties or not
@ -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.
@ -142,7 +207,7 @@ swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
--xcassets-path "./Images/app.xcassets" \ --xcassets-path "./Images/app.xcassets" \
--extension-output-path "./Images/Generated" \ --extension-output-path "./Images/Generated" \
--extension-name "AppImage" \ --extension-name "AppImage" \
--extension-name-swift-ui "SUIAppImage" \ --extension-name-ui-kit "UIAppImage" \
--extension-suffix "GreatApp" \ --extension-suffix "GreatApp" \
--static-members true --static-members true
``` ```
@ -153,8 +218,8 @@ swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
2. Input images definitions file 2. Input images definitions file
3. `--xcassets-path`: xcasset path where to generate imagesets 3. `--xcassets-path`: xcasset path where to generate imagesets
4. `--extension-output-path`: path where to generate generated extension 4. `--extension-output-path`: path where to generate generated extension
5. `--extension-name` *(optional)* : name of the class to add UIKit getters 5. `--extension-name` *(optional)* : name of the class to add SwiftUI getters
6. `--extension-name-swift-ui` *(optional)* : name of the class to add SwiftUI getters 6. `--extension-name-ui-kit` *(optional)* : name of the class to add UIKit getters
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`) 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`)
7. `--static-members` *(optional)*: generate static properties or not 7. `--static-members` *(optional)*: generate static properties or not
@ -176,7 +241,7 @@ colors:
xcassetsPath: String xcassetsPath: String
extensionOutputPath: String extensionOutputPath: String
extensionName: String? extensionName: String?
extensionNameSwiftUI: String? extensionNameUIKit: String?
extensionSuffix: String? extensionSuffix: String?
staticMembers: Bool? staticMembers: Bool?
@ -185,7 +250,7 @@ fonts:
inputFile: String inputFile: String
extensionOutputPath: String extensionOutputPath: String
extensionName: String? extensionName: String?
extensionNameSwiftUI: String? extensionNameUIKit: String?
extensionSuffix: String? extensionSuffix: String?
staticMembers: Bool? staticMembers: Bool?
infoPlistPaths: [String] infoPlistPaths: [String]
@ -196,7 +261,7 @@ images:
xcassetsPath: String xcassetsPath: String
extensionOutputPath: String extensionOutputPath: String
extensionName: String? extensionName: String?
extensionNameSwiftUI: String? extensionNameUIKit: String?
extensionSuffix: String? extensionSuffix: String?
staticMembers: Bool? staticMembers: Bool?
@ -236,7 +301,7 @@ colors:
xcassetsPath: String xcassetsPath: String
extensionOutputPath: String extensionOutputPath: String
extensionName: String? extensionName: String?
extensionNameSwiftUI: String? extensionNameUIKit: String?
extensionSuffix: String? extensionSuffix: String?
staticMembers: Bool? staticMembers: Bool?
- -
@ -245,7 +310,7 @@ colors:
xcassetsPath: String xcassetsPath: String
extensionOutputPath: String extensionOutputPath: String
extensionName: String? extensionName: String?
extensionNameSwiftUI: String? extensionNameUIKit: String?
extensionSuffix: String? extensionSuffix: String?
staticMembers: Bool? staticMembers: Bool?
... ...

View File

@ -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")
} }
} }

View File

@ -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")!
} }
} }

View File

@ -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")!
}
}

View File

@ -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
]
) )
} }

View File

@ -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:

View File

@ -5,21 +5,21 @@ FORCE_FLAG="$1"
## Font ## Font
#swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/sampleFontsAll.txt" \ #swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/sampleFontsAll.txt" \
# --extension-output-path "./Fonts/Generated" \ # --extension-output-path "./Fonts/Generated" \
# --extension-name "UIFontYolo" \ # --extension-name "FontYolo" \
# --extension-name-swift-ui "FontYolo" \ # --extension-name-ui-kit "UIFontYolo" \
# --extension-suffix "GenAllScript" \ # --extension-suffix "GenAllScript" \
# --info-plist-paths "./Fonts/Generated/test.plist ./Fonts/Generated/test2.plist" # --info-plist-paths "./Fonts/Generated/test.plist ./Fonts/Generated/test2.plist"
# #
#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 "UIColorYolo" \ --extension-name "ColorYolo" \
# --extension-name-swift-ui "ColorYolo" \ --extension-name-ui-kit "UIColorYolo" \
# --extension-suffix "GenAllScript" --extension-suffix "GenAllScript"
# #
#echo "\n-------------------------\n" #echo "\n-------------------------\n"
# #
@ -49,22 +49,22 @@ FORCE_FLAG="$1"
# --extension-output-path "./Tags/Generated" \ # --extension-output-path "./Tags/Generated" \
# --extension-name "Tags" \ # --extension-name "Tags" \
# --extension-suffix "GenAllScript" # --extension-suffix "GenAllScript"
#
echo "\n-------------------------\n"
# Analytics #echo "\n-------------------------\n"
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \
--target "matomo firebase" \
--extension-output-path "./Tags/Generated" \
--extension-name "Analytics" \
--extension-suffix "GenAllScript"
echo "\n-------------------------\n" ## Analytics
#swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \
# --target "matomo firebase" \
# --extension-output-path "./Tags/Generated" \
# --extension-name "Analytics" \
# --extension-suffix "GenAllScript"
#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 "UIImage" \ # --extension-name "ImageYolo" \
# --extension-name-swift-ui "ImageYolo" \ # --extension-name-ui-kit "UIImageYolo" \
# --extension-suffix "GenAllScript" # --extension-suffix "GenAllScript"

View File

@ -47,8 +47,8 @@ images:
inputFile: ./Images/sampleImages.txt inputFile: ./Images/sampleImages.txt
xcassetsPath: ./Images/imagium.xcassets xcassetsPath: ./Images/imagium.xcassets
extensionOutputPath: ./Images/Generated extensionOutputPath: ./Images/Generated
extensionName: UIImage extensionName: ImageYolo
extensionNameSwiftUI: ImageYolo extensionNameUIKit: UIImageYolo
extensionSuffix: GenAllScript extensionSuffix: GenAllScript
@ -61,8 +61,8 @@ colors:
style: all style: all
xcassetsPath: ./Colors/colors.xcassets xcassetsPath: ./Colors/colors.xcassets
extensionOutputPath: ./Colors/Generated/ extensionOutputPath: ./Colors/Generated/
extensionName: UIColorYolo extensionName: ColorYolo
extensionNameSwiftUI: ColorYolo extensionNameUIKit: UIColorYolo
extensionSuffix: GenAllScript extensionSuffix: GenAllScript
@ -97,7 +97,7 @@ fonts:
- -
inputFile: ./Fonts/sampleFontsAll.txt inputFile: ./Fonts/sampleFontsAll.txt
extensionOutputPath: ./Fonts/Generated extensionOutputPath: ./Fonts/Generated
extensionName: UIFontYolo extensionName: FontYolo
extensionNameSwiftUI: FontYolo extensionNameUIKit: UIFontYolo
extensionSuffix: GenAllScript extensionSuffix: GenAllScript
infoPlistPaths: "./Fonts/Generated/test.plist ./Fonts/Generated/test2.plist" infoPlistPaths: "./Fonts/Generated/test.plist ./Fonts/Generated/test2.plist"

View File

@ -18,7 +18,6 @@ struct Analytics: ParsableCommand {
version: ResgenSwiftVersion version: ResgenSwiftVersion
) )
// MARK: - Static // MARK: - Static
static let toolName = "Analytics" static let toolName = "Analytics"
@ -33,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)
@ -48,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
} }
} }

View 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)"
}
}
}

View File

@ -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)
} }
@ -32,10 +32,10 @@ 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 = 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")

View File

@ -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 {
""" """
} }

View File

@ -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 {
""" """
} }

View File

@ -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)

View File

@ -6,6 +6,7 @@
// //
import Foundation import Foundation
import ToolCore
class AnalyticsDefinition { class AnalyticsDefinition {
let id: String let id: String
@ -18,21 +19,25 @@ 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).intersection(Set(self.tags)).isEmpty { if Set(inputTags).isDisjoint(with: tags) {
return false return false
} }
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
@ -47,7 +52,7 @@ class AnalyticsDefinition {
var result: String var result: String
if type == .screen { if type == .screen {
params = params.filter{ param in params = params.filter { param in
!param.replaceIn.isEmpty !param.replaceIn.isEmpty
} }
} }
@ -71,7 +76,7 @@ class AnalyticsDefinition {
return result return result
} }
private func replaceIn(){ private func replaceIn() {
for parameter in parameters { for parameter in parameters {
for rep in parameter.replaceIn { for rep in parameter.replaceIn {
switch rep { switch rep {
@ -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
}
}

View File

@ -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

View File

@ -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

View 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
}
}

View 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
}
}

View File

@ -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
} }
} }

View File

@ -21,8 +21,8 @@ struct Colors: ParsableCommand {
// MARK: - Static // MARK: - Static
static let toolName = "Color" static let toolName = "Color"
static let defaultExtensionName = "UIColor" static let defaultExtensionName = "Color"
static let defaultExtensionNameSUI = "Color" static let defaultExtensionNameUIKit = "UIColor"
static let assetsColorsFolderName = "Colors" static let assetsColorsFolderName = "Colors"
// MARK: - Command options // MARK: - Command options
@ -57,14 +57,14 @@ struct Colors: ParsableCommand {
staticVar: options.staticMembers, staticVar: options.staticMembers,
extensionName: options.extensionName, extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath, extensionFilePath: options.extensionFilePath,
isSwiftUI: false) isSwiftUI: true)
// Generate extension // Generate extension
ColorExtensionGenerator.writeExtensionFile(colors: parsedColors, ColorExtensionGenerator.writeExtensionFile(colors: parsedColors,
staticVar: options.staticMembers, staticVar: options.staticMembers,
extensionName: options.extensionNameSwiftUI, extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathSwiftUI, extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: true) isSwiftUI: false)
print("[\(Self.toolName)] Colors generated") print("[\(Self.toolName)] Colors generated")
} }
@ -89,7 +89,7 @@ struct Colors: ParsableCommand {
} }
// Extension for UIKit and SwiftUI should have different name // Extension for UIKit and SwiftUI should have different name
guard options.extensionName != options.extensionNameSwiftUI else { guard options.extensionName != options.extensionNameUIKit else {
let error = ColorsToolError.extensionNamesCollision(options.extensionName) let error = ColorsToolError.extensionNamesCollision(options.extensionName)
print(error.description) print(error.description)
Colors.exit(withError: error) Colors.exit(withError: error)

View File

@ -27,11 +27,11 @@ struct ColorsToolOptions: ParsableArguments {
@Option(help: "Tell if it will generate static properties or not") @Option(help: "Tell if it will generate static properties or not")
var staticMembers: Bool = false var staticMembers: Bool = false
@Option(help: "Extension name. If not specified, it will generate an UIColor extension.") @Option(help: "Extension name. If not specified, it will generate an Color extension.")
var extensionName: String = Colors.defaultExtensionName var extensionName: String = Colors.defaultExtensionName
@Option(help: "SwiftUI Extension name. If not specified, it will generate an Color extension.") @Option(help: "SwiftUI Extension name. If not specified, it will generate an UIColor extension.")
var extensionNameSwiftUI: String = Colors.defaultExtensionNameSUI var extensionNameUIKit: String = Colors.defaultExtensionNameUIKit
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift") @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
var extensionSuffix: String? var extensionSuffix: String?
@ -41,7 +41,7 @@ struct ColorsToolOptions: ParsableArguments {
extension ColorsToolOptions { extension ColorsToolOptions {
// MARK: - UIKit // MARK: - SwiftUI
var extensionFileName: String { var extensionFileName: String {
if let extensionSuffix = extensionSuffix { if let extensionSuffix = extensionSuffix {
@ -54,16 +54,16 @@ extension ColorsToolOptions {
"\(extensionOutputPath)/\(extensionFileName)" "\(extensionOutputPath)/\(extensionFileName)"
} }
// MARK: - SwiftUI // MARK: - UIKit
var extensionFileNameSwiftUI: String { var extensionFileNameUIKit: String {
if let extensionSuffix = extensionSuffix { if let extensionSuffix = extensionSuffix {
return "\(extensionNameSwiftUI)+\(extensionSuffix).swift" return "\(extensionNameUIKit)+\(extensionSuffix).swift"
} }
return "\(extensionNameSwiftUI).swift" return "\(extensionNameUIKit).swift"
} }
var extensionFilePathSwiftUI: String { var extensionFilePathUIKit: String {
"\(extensionOutputPath)/\(extensionFileNameSwiftUI)" "\(extensionOutputPath)/\(extensionFileNameUIKit)"
} }
} }

View File

@ -30,7 +30,7 @@ struct ColorExtensionGenerator {
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
do { do {
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
} catch (let error) { } catch let error {
let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription) let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription)
print(error.description) print(error.description)
Colors.exit(withError: error) Colors.exit(withError: error)
@ -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 {

View File

@ -38,7 +38,7 @@ struct ColorXcassetHelper {
let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath) let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath)
do { do {
try color.contentsJSON().write(to: contentsJsonPathURL, atomically: false, encoding: .utf8) try color.contentsJSON().write(to: contentsJsonPathURL, atomically: false, encoding: .utf8)
} catch (let error) { } catch let error {
let error = ColorsToolError.writeAsset(error.localizedDescription) let error = ColorsToolError.writeAsset(error.localizedDescription)
print(error.description) print(error.description)
Colors.exit(withError: error) Colors.exit(withError: error)

View File

@ -21,11 +21,11 @@ struct FontsOptions: ParsableArguments {
@Option(help: "Tell if it will generate static properties or methods") @Option(help: "Tell if it will generate static properties or methods")
var staticMembers: Bool = false var staticMembers: Bool = false
@Option(help: "Extension name. If not specified, it will generate an UIFont extension.") @Option(help: "Extension name. If not specified, it will generate an Font extension.")
var extensionName: String = Fonts.defaultExtensionName var extensionName: String = Fonts.defaultExtensionName
@Option(help: "Extension name. If not specified, it will generate an Font extension.") @Option(help: "Extension name. If not specified, it will generate an UIFont extension.")
var extensionNameSwiftUI: String = Fonts.defaultExtensionNameSUI var extensionNameUIKit: String = Fonts.defaultExtensionNameUIKit
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift") @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
var extensionSuffix: String = "" var extensionSuffix: String = ""
@ -38,7 +38,7 @@ struct FontsOptions: ParsableArguments {
extension FontsOptions { extension FontsOptions {
// MARK: - UIKit // MARK: - SwiftUI
var extensionFileName: String { var extensionFileName: String {
if extensionSuffix.isEmpty == false { if extensionSuffix.isEmpty == false {
@ -51,17 +51,17 @@ extension FontsOptions {
"\(extensionOutputPath)/\(extensionFileName)" "\(extensionOutputPath)/\(extensionFileName)"
} }
// MARK: - SwiftUI // MARK: - UIKit
var extensionFileNameSwiftUI: String { var extensionFileNameUIKit: String {
if extensionSuffix.isEmpty == false { if extensionSuffix.isEmpty == false {
return "\(extensionNameSwiftUI)+\(extensionSuffix).swift" return "\(extensionNameUIKit)+\(extensionSuffix).swift"
} }
return "\(extensionNameSwiftUI).swift" return "\(extensionNameUIKit).swift"
} }
var extensionFilePathSwiftUI: String { var extensionFilePathUIKit: String {
"\(extensionOutputPath)/\(extensionFileNameSwiftUI)" "\(extensionOutputPath)/\(extensionFileNameUIKit)"
} }
// MARK: - // MARK: -

View File

@ -21,8 +21,8 @@ struct Fonts: ParsableCommand {
// MARK: - Static // MARK: - Static
static let toolName = "Fonts" static let toolName = "Fonts"
static let defaultExtensionName = "UIFont" static let defaultExtensionName = "Font"
static let defaultExtensionNameSUI = "Font" static let defaultExtensionNameUIKit = "UIFont"
// MARK: - Command Options // MARK: - Command Options
@ -52,13 +52,13 @@ struct Fonts: ParsableCommand {
staticVar: options.staticMembers, staticVar: options.staticMembers,
extensionName: options.extensionName, extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath, extensionFilePath: options.extensionFilePath,
isSwiftUI: false) isSwiftUI: true)
FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames, FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames,
staticVar: options.staticMembers, staticVar: options.staticMembers,
extensionName: options.extensionNameSwiftUI, extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathSwiftUI, extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: true) isSwiftUI: false)
print("Info.plist has been updated with:") print("Info.plist has been updated with:")
print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames, infoPlistPaths: options.infoPlistPaths))") print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames, infoPlistPaths: options.infoPlistPaths))")
@ -79,7 +79,7 @@ struct Fonts: ParsableCommand {
} }
// Extension for UIKit and SwiftUI should have different name // Extension for UIKit and SwiftUI should have different name
guard options.extensionName != options.extensionNameSwiftUI else { guard options.extensionName != options.extensionNameUIKit else {
let error = FontsToolError.extensionNamesCollision(options.extensionName) let error = FontsToolError.extensionNamesCollision(options.extensionName)
print(error.description) print(error.description)
Fonts.exit(withError: error) Fonts.exit(withError: error)

View File

@ -36,7 +36,7 @@ class FontExtensionGenerator {
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
do { do {
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
} catch (let error) { } catch let error {
let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription) let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription)
print(error.description) print(error.description)
Fonts.exit(withError: error) Fonts.exit(withError: error)

View File

@ -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)"
} }
} }
} }

View File

@ -5,8 +5,6 @@
// Created by Thibaut Schmitt on 30/08/2022. // Created by Thibaut Schmitt on 30/08/2022.
// //
import Foundation
import Foundation import Foundation
import ArgumentParser import ArgumentParser

View File

@ -30,7 +30,7 @@ struct ArchitectureGenerator {
let architectureFilePathURL = URL(fileURLWithPath: "\(filePath)/\(filename)") let architectureFilePathURL = URL(fileURLWithPath: "\(filePath)/\(filename)")
do { do {
try architectureContent.write(to: architectureFilePathURL, atomically: false, encoding: .utf8) try architectureContent.write(to: architectureFilePathURL, atomically: false, encoding: .utf8)
} catch (let error) { } catch let error {
let error = GenerateError.writeFile(filename, error.localizedDescription) let error = GenerateError.writeFile(filename, error.localizedDescription)
print(error.description) print(error.description)
Generate.exit(withError: error) Generate.exit(withError: error)

View File

@ -127,7 +127,7 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
let xcassetsPath: String let xcassetsPath: String
let extensionOutputPath: String let extensionOutputPath: String
let extensionName: String? let extensionName: String?
let extensionNameSwiftUI: String? let extensionNameUIKit: String?
let extensionSuffix: String? let extensionSuffix: String?
private let staticMembers: Bool? private let staticMembers: Bool?
@ -143,7 +143,7 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
xcassetsPath: String, xcassetsPath: String,
extensionOutputPath: String, extensionOutputPath: String,
extensionName: String?, extensionName: String?,
extensionNameSwiftUI: String?, extensionNameUIKit: String?,
extensionSuffix: String?, extensionSuffix: String?,
staticMembers: Bool?) { staticMembers: Bool?) {
self.inputFile = inputFile self.inputFile = inputFile
@ -151,7 +151,7 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
self.xcassetsPath = xcassetsPath self.xcassetsPath = xcassetsPath
self.extensionOutputPath = extensionOutputPath self.extensionOutputPath = extensionOutputPath
self.extensionName = extensionName self.extensionName = extensionName
self.extensionNameSwiftUI = extensionNameSwiftUI self.extensionNameUIKit = extensionNameUIKit
self.extensionSuffix = extensionSuffix self.extensionSuffix = extensionSuffix
self.staticMembers = staticMembers self.staticMembers = staticMembers
} }
@ -164,7 +164,7 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
- Xcassets path: \(xcassetsPath) - Xcassets path: \(xcassetsPath)
- Extension output path: \(extensionOutputPath) - Extension output path: \(extensionOutputPath)
- Extension name: \(extensionName ?? "-") - Extension name: \(extensionName ?? "-")
- Extension name SwiftUI: \(extensionNameSwiftUI ?? "-") - Extension name UIKit: \(extensionNameUIKit ?? "-")
- Extension suffix: \(extensionSuffix ?? "-") - Extension suffix: \(extensionSuffix ?? "-")
""" """
} }
@ -174,7 +174,7 @@ struct FontsConfiguration: Codable, CustomDebugStringConvertible {
let inputFile: String let inputFile: String
let extensionOutputPath: String let extensionOutputPath: String
let extensionName: String? let extensionName: String?
let extensionNameSwiftUI: String? let extensionNameUIKit: String?
let extensionSuffix: String? let extensionSuffix: String?
let infoPlistPaths: String? let infoPlistPaths: String?
private let staticMembers: Bool? private let staticMembers: Bool?
@ -189,14 +189,14 @@ struct FontsConfiguration: Codable, CustomDebugStringConvertible {
internal init(inputFile: String, internal init(inputFile: String,
extensionOutputPath: String, extensionOutputPath: String,
extensionName: String?, extensionName: String?,
extensionNameSwiftUI: String?, extensionNameUIKit: String?,
extensionSuffix: String?, extensionSuffix: String?,
infoPlistPaths: String?, infoPlistPaths: String?,
staticMembers: Bool?) { staticMembers: Bool?) {
self.inputFile = inputFile self.inputFile = inputFile
self.extensionOutputPath = extensionOutputPath self.extensionOutputPath = extensionOutputPath
self.extensionName = extensionName self.extensionName = extensionName
self.extensionNameSwiftUI = extensionNameSwiftUI self.extensionNameUIKit = extensionNameUIKit
self.extensionSuffix = extensionSuffix self.extensionSuffix = extensionSuffix
self.infoPlistPaths = infoPlistPaths self.infoPlistPaths = infoPlistPaths
self.staticMembers = staticMembers self.staticMembers = staticMembers
@ -208,7 +208,7 @@ struct FontsConfiguration: Codable, CustomDebugStringConvertible {
- Input file: \(inputFile) - Input file: \(inputFile)
- Extension output path: \(extensionOutputPath) - Extension output path: \(extensionOutputPath)
- Extension name: \(extensionName ?? "-") - Extension name: \(extensionName ?? "-")
- Extension name SwiftUI: \(extensionNameSwiftUI ?? "-") - Extension name UIKit: \(extensionNameUIKit ?? "-")
- Extension suffix: \(extensionSuffix ?? "-") - Extension suffix: \(extensionSuffix ?? "-")
- InfoPlistPaths: \(infoPlistPaths ?? "-") - InfoPlistPaths: \(infoPlistPaths ?? "-")
""" """
@ -220,7 +220,7 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible {
let xcassetsPath: String let xcassetsPath: String
let extensionOutputPath: String let extensionOutputPath: String
let extensionName: String? let extensionName: String?
let extensionNameSwiftUI: String? let extensionNameUIKit: String?
let extensionSuffix: String? let extensionSuffix: String?
private let staticMembers: Bool? private let staticMembers: Bool?
@ -235,14 +235,14 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible {
xcassetsPath: String, xcassetsPath: String,
extensionOutputPath: String, extensionOutputPath: String,
extensionName: String?, extensionName: String?,
extensionNameSwiftUI: String?, extensionNameUIKit: String?,
extensionSuffix: String?, extensionSuffix: String?,
staticMembers: Bool?) { staticMembers: Bool?) {
self.inputFile = inputFile self.inputFile = inputFile
self.xcassetsPath = xcassetsPath self.xcassetsPath = xcassetsPath
self.extensionOutputPath = extensionOutputPath self.extensionOutputPath = extensionOutputPath
self.extensionName = extensionName self.extensionName = extensionName
self.extensionNameSwiftUI = extensionNameSwiftUI self.extensionNameUIKit = extensionNameUIKit
self.extensionSuffix = extensionSuffix self.extensionSuffix = extensionSuffix
self.staticMembers = staticMembers self.staticMembers = staticMembers
} }
@ -254,7 +254,7 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible {
- Xcassets path: \(xcassetsPath) - Xcassets path: \(xcassetsPath)
- Extension output path: \(extensionOutputPath) - Extension output path: \(extensionOutputPath)
- Extension name: \(extensionName ?? "-") - Extension name: \(extensionName ?? "-")
- Extension name SwiftUI: \(extensionNameSwiftUI ?? "-") - Extension name UIKit: \(extensionNameUIKit ?? "-")
- Extension suffix: \(extensionSuffix ?? "-") - Extension suffix: \(extensionSuffix ?? "-")
""" """
} }

View File

@ -38,10 +38,10 @@ extension ColorsConfiguration: Runnable {
extensionName extensionName
] ]
} }
if let extensionNameSwiftUI = extensionNameSwiftUI { if let extensionNameUIKit = extensionNameUIKit {
args += [ args += [
"--extension-name-swift-ui", "--extension-name-ui-kit",
extensionNameSwiftUI extensionNameUIKit
] ]
} }
if let extensionSuffix = extensionSuffix { if let extensionSuffix = extensionSuffix {

View File

@ -34,10 +34,10 @@ extension FontsConfiguration: Runnable {
extensionName extensionName
] ]
} }
if let extensionNameSwiftUI = extensionNameSwiftUI { if let extensionNameUIKit = extensionNameUIKit {
args += [ args += [
"--extension-name-swift-ui", "--extension-name-ui-kit",
extensionNameSwiftUI extensionNameUIKit
] ]
} }

View File

@ -36,10 +36,10 @@ extension ImagesConfiguration: Runnable {
extensionName extensionName
] ]
} }
if let extensionNameSwiftUI = extensionNameSwiftUI { if let extensionNameUIKit = extensionNameUIKit {
args += [ args += [
"--extension-name-swift-ui", "--extension-name-ui-kit",
extensionNameSwiftUI extensionNameUIKit
] ]
} }
if let extensionSuffix = extensionSuffix { if let extensionSuffix = extensionSuffix {

View File

@ -10,4 +10,3 @@ import Foundation
protocol Runnable { protocol Runnable {
func run(projectDirectory: String, force: Bool) func run(projectDirectory: String, force: Bool)
} }

View File

@ -18,7 +18,7 @@ extension FileManager {
for case let fileURL as URL in enumerator { for case let fileURL as URL in enumerator {
do { do {
let fileAttributes = try fileURL.resourceValues(forKeys:[.isRegularFileKey]) let fileAttributes = try fileURL.resourceValues(forKeys: [.isRegularFileKey])
if fileAttributes.isRegularFile! { if fileAttributes.isRegularFile! {
files.append(fileURL.relativePath) files.append(fileURL.relativePath)
} }
@ -41,7 +41,7 @@ extension FileManager {
for case let fileURL as URL in enumerator { for case let fileURL as URL in enumerator {
do { do {
let fileAttributes = try fileURL.resourceValues(forKeys:[.isDirectoryKey]) let fileAttributes = try fileURL.resourceValues(forKeys: [.isDirectoryKey])
if fileAttributes.isDirectory! && fileURL.lastPathComponent.hasSuffix(".imageset") { if fileAttributes.isDirectory! && fileURL.lastPathComponent.hasSuffix(".imageset") {
files.append(fileURL.lastPathComponent) files.append(fileURL.lastPathComponent)
} }

View File

@ -29,7 +29,7 @@ class ImageExtensionGenerator {
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
do { do {
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
} catch (let error) { } catch let error {
let error = ImagesError.writeFile(extensionFilePath, error.localizedDescription) let error = ImagesError.writeFile(extensionFilePath, error.localizedDescription)
print(error.description) print(error.description)
Images.exit(withError: error) Images.exit(withError: error)

View File

@ -21,8 +21,8 @@ struct Images: ParsableCommand {
// MARK: - Static // MARK: - Static
static let toolName = "Images" static let toolName = "Images"
static let defaultExtensionName = "UIImage" static let defaultExtensionName = "Image"
static let defaultExtensionNameSUI = "Image" static let defaultExtensionNameUIKit = "UIImage"
// MARK: - Command Options // MARK: - Command Options
@ -58,14 +58,14 @@ struct Images: ParsableCommand {
inputFilename: options.inputFilenameWithoutExt, inputFilename: options.inputFilenameWithoutExt,
extensionName: options.extensionName, extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath, extensionFilePath: options.extensionFilePath,
isSwiftUI: false) isSwiftUI: true)
ImageExtensionGenerator.generateExtensionFile(images: imagesToGenerate, ImageExtensionGenerator.generateExtensionFile(images: imagesToGenerate,
staticVar: options.staticMembers, staticVar: options.staticMembers,
inputFilename: options.inputFilenameWithoutExt, inputFilename: options.inputFilenameWithoutExt,
extensionName: options.extensionNameSwiftUI, extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathSwiftUI, extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: true) isSwiftUI: false)
print("[\(Self.toolName)] Images generated") print("[\(Self.toolName)] Images generated")
} }
@ -90,7 +90,7 @@ struct Images: ParsableCommand {
_ = Images.getSvgConverterPath() _ = Images.getSvgConverterPath()
// Extension for UIKit and SwiftUI should have different name // Extension for UIKit and SwiftUI should have different name
guard options.extensionName != options.extensionNameSwiftUI else { guard options.extensionName != options.extensionNameUIKit else {
let error = ImagesError.extensionNamesCollision(options.extensionName) let error = ImagesError.extensionNamesCollision(options.extensionName)
print(error.description) print(error.description)
Images.exit(withError: error) Images.exit(withError: error)

View File

@ -27,11 +27,11 @@ struct ImagesOptions: ParsableArguments {
@Option(help: "Tell if it will generate static properties or not") @Option(help: "Tell if it will generate static properties or not")
var staticMembers: Bool = false var staticMembers: Bool = false
@Option(help: "Extension name. If not specified, it will generate an UIImage extension.") @Option(help: "Extension name. If not specified, it will generate an Image extension.")
var extensionName: String = Images.defaultExtensionName var extensionName: String = Images.defaultExtensionName
@Option(help: "Extension name. If not specified, it will generate an Image extension.") @Option(help: "Extension name. If not specified, it will generate an UIImage extension.")
var extensionNameSwiftUI: String = Images.defaultExtensionNameSUI var extensionNameUIKit: String = Images.defaultExtensionNameUIKit
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift") @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift")
var extensionSuffix: String? var extensionSuffix: String?
@ -41,7 +41,7 @@ struct ImagesOptions: ParsableArguments {
extension ImagesOptions { extension ImagesOptions {
// MARK: - UIKit // MARK: - SwiftUI
var extensionFileName: String { var extensionFileName: String {
if let extensionSuffix = extensionSuffix { if let extensionSuffix = extensionSuffix {
@ -54,17 +54,17 @@ extension ImagesOptions {
"\(extensionOutputPath)/\(extensionFileName)" "\(extensionOutputPath)/\(extensionFileName)"
} }
// MARK: - SwiftUI // MARK: - UIKit
var extensionFileNameSwiftUI: String { var extensionFileNameUIKit: String {
if let extensionSuffix = extensionSuffix { if let extensionSuffix = extensionSuffix {
return "\(extensionNameSwiftUI)+\(extensionSuffix).swift" return "\(extensionNameUIKit)+\(extensionSuffix).swift"
} }
return "\(extensionNameSwiftUI).swift" return "\(extensionNameUIKit).swift"
} }
var extensionFilePathSwiftUI: String { var extensionFilePathUIKit: String {
"\(extensionOutputPath)/\(extensionFileNameSwiftUI)" "\(extensionOutputPath)/\(extensionFileNameUIKit)"
} }
// MARK: - // MARK: -

View File

@ -34,7 +34,7 @@ class StringsFileGenerator {
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath) let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
do { do {
try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8) try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8)
} catch (let error) { } catch let error {
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath) let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
print(error.description) print(error.description)
Stringium.exit(withError: error) Stringium.exit(withError: error)
@ -115,7 +115,7 @@ class StringsFileGenerator {
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 = StringiumError.writeFile(extensionFilePath, error.localizedDescription) let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
print(error.description) print(error.description)
Stringium.exit(withError: error) Stringium.exit(withError: error)

View File

@ -22,7 +22,7 @@ class TagsGenerator {
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 = StringiumError.writeFile(extensionFilePath, error.localizedDescription) let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
print(error.description) print(error.description)
Stringium.exit(withError: error) Stringium.exit(withError: error)

View File

@ -37,7 +37,7 @@ class Definition {
} }
func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool { func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool {
if Set(inputTags).intersection(Set(self.tags)).isEmpty { if Set(inputTags).isDisjoint(with: tags) {
return false return false
} }
return true return true

View File

@ -27,4 +27,3 @@ struct Strings: ParsableCommand {
} }
//Strings.main() //Strings.main()

View File

@ -18,7 +18,6 @@ struct Tags: ParsableCommand {
version: ResgenSwiftVersion version: ResgenSwiftVersion
) )
// MARK: - Static // MARK: - Static
static let toolName = "Tags" static let toolName = "Tags"

View File

@ -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()

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,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
}
} }

View File

View 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 %}
}

View 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 }}")
}

View 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 }}")!
}

View File

@ -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,

View File

@ -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

View File

@ -21,7 +21,7 @@ final class ColorsConfigurationTests: XCTestCase {
xcassetsPath: "path/to/assets.xcassets", xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Colors/Generated", extensionOutputPath: "Colors/Generated",
extensionName: nil, extensionName: nil,
extensionNameSwiftUI: nil, extensionNameUIKit: nil,
extensionSuffix: nil, extensionSuffix: nil,
staticMembers: false) staticMembers: false)
// When // When
@ -50,7 +50,7 @@ final class ColorsConfigurationTests: XCTestCase {
xcassetsPath: "path/to/assets.xcassets", xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Colors/Generated", extensionOutputPath: "Colors/Generated",
extensionName: "AppUIColor", extensionName: "AppUIColor",
extensionNameSwiftUI: "AppColor", extensionNameUIKit: "AppColor",
extensionSuffix: "Testing", extensionSuffix: "Testing",
staticMembers: false) staticMembers: false)
// When // When
@ -70,7 +70,7 @@ final class ColorsConfigurationTests: XCTestCase {
"false", "false",
"--extension-name", "--extension-name",
"AppUIColor", "AppUIColor",
"--extension-name-swift-ui", "--extension-name-ui-kit",
"AppColor", "AppColor",
"--extension-suffix", "--extension-suffix",
"Testing", "Testing",

View File

@ -19,7 +19,7 @@ final class FontsConfigurationTests: XCTestCase {
let testingConfiguration = FontsConfiguration(inputFile: "path/to/fonts.txt", let testingConfiguration = FontsConfiguration(inputFile: "path/to/fonts.txt",
extensionOutputPath: "Fonts/Generated", extensionOutputPath: "Fonts/Generated",
extensionName: nil, extensionName: nil,
extensionNameSwiftUI: nil, extensionNameUIKit: nil,
extensionSuffix: nil, extensionSuffix: nil,
infoPlistPaths: nil, infoPlistPaths: nil,
staticMembers: nil) staticMembers: nil)
@ -43,7 +43,7 @@ final class FontsConfigurationTests: XCTestCase {
let testingConfiguration = FontsConfiguration(inputFile: "path/to/fonts.txt", let testingConfiguration = FontsConfiguration(inputFile: "path/to/fonts.txt",
extensionOutputPath: "Fonts/Generated", extensionOutputPath: "Fonts/Generated",
extensionName: "AppUIFont", extensionName: "AppUIFont",
extensionNameSwiftUI: "AppFont", extensionNameUIKit: "AppFont",
extensionSuffix: "Testing", extensionSuffix: "Testing",
infoPlistPaths: "path/to/plist1.plist path/to/plist2.plist", infoPlistPaths: "path/to/plist1.plist path/to/plist2.plist",
staticMembers: true) staticMembers: true)
@ -60,7 +60,7 @@ final class FontsConfigurationTests: XCTestCase {
"true", "true",
"--extension-name", "--extension-name",
"AppUIFont", "AppUIFont",
"--extension-name-swift-ui", "--extension-name-ui-kit",
"AppFont", "AppFont",
"--extension-suffix", "--extension-suffix",
"Testing", "Testing",

View File

@ -20,7 +20,7 @@ final class ImagesConfigurationTests: XCTestCase {
xcassetsPath: "path/to/assets.xcassets", xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Images/Generated", extensionOutputPath: "Images/Generated",
extensionName: nil, extensionName: nil,
extensionNameSwiftUI: nil, extensionNameUIKit: nil,
extensionSuffix: nil, extensionSuffix: nil,
staticMembers: nil) staticMembers: nil)
@ -47,7 +47,7 @@ final class ImagesConfigurationTests: XCTestCase {
xcassetsPath: "path/to/assets.xcassets", xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Images/Generated", extensionOutputPath: "Images/Generated",
extensionName: "AppUIImage", extensionName: "AppUIImage",
extensionNameSwiftUI: "AppImage", extensionNameUIKit: "AppImage",
extensionSuffix: "Testing", extensionSuffix: "Testing",
staticMembers: true) staticMembers: true)
@ -66,7 +66,7 @@ final class ImagesConfigurationTests: XCTestCase {
"true", "true",
"--extension-name", "--extension-name",
"AppUIImage", "AppUIImage",
"--extension-name-swift-ui", "--extension-name-ui-kit",
"AppImage", "AppImage",
"--extension-suffix", "--extension-suffix",
"Testing", "Testing",

48
script/swiftlint.sh Executable file
View File

@ -0,0 +1,48 @@
#!/bin/sh
# Go to git repo root level
cd $(git rev-parse --show-toplevel)
if [[ "$BUILD_DIR" == *"IBDesignables"* ]] || [[ "$BUILD_DIR" == *"Previews"* ]] ; then
echo "not linting for IBDesignables/SwiftUI Previews builds";
exit 0
fi
SWIFT_LINT=$(which swiftlint)
if [[ -z $SWIFT_LINT ]] ; then
echo "warning: SwiftLint not installed, please download it from https://github.com/realm/SwiftLint"
exit 0
fi
if [[ $RUN_CLANG_STATIC_ANALYZER == "YES" ]] ; then
time $SWIFT_LINT
else
COUNT=0
##### Check for modified git files #####
FILES=$(git diff --name-only | grep -iv "^carthage" | grep -iv "^pods" | grep -iv "^vendor" | grep -v "R2" | grep ".swift$")
if [ ! -z "$FILES" ]; then
while read FILE_PATH; do
export SCRIPT_INPUT_FILE_$COUNT=$FILE_PATH
COUNT=$((COUNT + 1))
done <<< "$FILES"
fi
##### Check for modified files in unstaged/Staged area #####
FILES=$(git diff --name-only --cached --diff-filter=d | grep -iv "^carthage" | grep -iv "^pods" | grep -iv "^vendor" | grep -v "R2" | grep ".swift$")
if [ ! -z "$FILES" ]; then
while read FILE_PATH; do
export SCRIPT_INPUT_FILE_$COUNT=$FILE_PATH
COUNT=$((COUNT + 1))
done <<< "$FILES"
fi
##### Make the count avilable as global variable #####
export SCRIPT_INPUT_FILE_COUNT=$COUNT
env | grep SCRIPT_INPUT_FILE_
if [[ COUNT -ne 0 ]] ; then
time $SWIFT_LINT --use-script-input-files
fi
fi