6 Commits

Author SHA1 Message Date
caa3295dce Fix SwiftLint warning
All checks were successful
gitea-openium/resgen.swift/pipeline/head This commit looks good
gitea-openium/resgen.swift/pipeline/pr-master This commit looks good
2025-07-18 14:09:25 +02:00
352633fddd Add ArgumentParser dependency to ToolCore target
Some checks reported warnings
gitea-openium/resgen.swift/pipeline/head This commit is unstable
2025-07-18 11:58:19 +02:00
7162f13166 Add visibility parameters to control scope of generated extension
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2025-07-18 11:53:46 +02:00
beca2c6b2b Increment version to 2.2.0
All checks were successful
gitea-openium/resgen.swift/pipeline/head This commit looks good
2025-07-17 16:05:16 +02:00
3092376b65 Optional generation of extension (R.xx)
Optional generation of Colors/Fonts/Images/Stringium extension

Fix swiftlint warning
2025-07-17 16:04:51 +02:00
c3b8ebfb37 Analytics improvement (#9)
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
Co-authored-by: Quentin Bandera <q.bandera@openium.fr>
Reviewed-on: #9
Co-authored-by: Loris Perret <l.perret@openium.fr>
Co-committed-by: Loris Perret <l.perret@openium.fr>
2025-07-17 09:15:43 +02:00
74 changed files with 2771 additions and 1403 deletions

View File

@ -40,7 +40,15 @@ let package = Package(
),
// Helper targets
.target(name: "ToolCore"),
.target(
name: "ToolCore",
dependencies: [
.product(
name: "ArgumentParser",
package: "swift-argument-parser"
)
]
),
// Test targets
.testTarget(

105
README.md
View File

@ -2,13 +2,13 @@
ResgenSwift is a package, fully written in Swift, to help you automatize ressource update and generation.
> 🧐 For all commands, see samples files in `SampleFiles`
> 🧐 For all commands, see samples files in `SampleFiles` and use `resgen-swift help` and `resgen-swift help <subcommand>` for detailed help.
## Fonts
Font generator generates an extension of `UIFont` and `Font` (or custom classes). It also prints content of `UIAppFonts` from your project `.plist`. If project `.plist` is specified, it will update `UIAppFonts` content of all `.plist`.
iOS required to use the **real name** of the font, this name can be different from its filename. To get the **real name**, it uses `fc-scan`. So, be sure that the `$PATH` contains path of `fc-scan`.
iOS required to use the **real name** of the font, this name can be different from its filename. To get the **real name**, it uses `fc-scan`. So, be sure that your `$PATH` variable contains path of `fc-scan`.
**Example**
@ -36,7 +36,7 @@ swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \
## Colors
Colors generator generates an extension of `UIColor` (or a custom class) along with colorsets in specified xcassets.
Colors generator generates colorsets in specified xcassets and an extension of `Color` (or a custom class) associated to those colorsets. If the extension name is not specified, no extension will be generated.
```sh
swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \
@ -54,12 +54,13 @@ swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \
1. `-f`: force generation
2. Input colors file
3. `--style` can be `all` or `light`
4. `--extension-output-path`: path where to generate generated extension
4. `--extension-output-path` *(optional)* : path where to generate generated extension
5. `--extension-name` *(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`)
8. `--static-members` *(optional)*: generate static properties or not
> ⚠️ Passing a `extensionOutputPath` without any `extensionName` does not generate extension. **But** passing an `extensionName` without `extensionOutputPath` will result in a error.
## Strings
@ -133,7 +134,7 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppTags+GreatApp.swift`)
7. `--static-members` *(optional)*: generate static properties or not
> ⚠️ If extension name is not set or is `Tags`, it will generate the following typaloas `typealias Tags = String`.
> ⚠️ If extension name is not set or is `Tags`, it will generate the following typealias `typealias Tags = String`.
## Analytics
@ -141,11 +142,9 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
Analytics will generate all you need to analyze UX with Matomo or Firebase Analytics. Input files are formatted in YAML. This command will generate a manager for each target and an AnalyticsManager. This is this one you will need to use. And it will generate a method for all tags you have declared in the YAML file. Next, you will need to use the `configure()` method of AnalyticsManager and if you want to use matomo to set up the `siteId` and the `url` of the site.
```sh
swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/analytics.yml" \
--target "matomo firebase" \
--extension-output-path "./Analytics/Generated" \
--extension-name "AppAnalytics" \
--extension-suffix "GreatApp" \
--output-file "./Analytics/Generated/AppAnalytics+GreatApp.swift" \
--static-members true
```
@ -154,12 +153,10 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
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`)
4. `--output-file`: file where where to generate generated code, must contains path and filename (ex: `./Analytics/Generated/AppAnalytics+GreatApp.swift`)
7. `--static-members` *(optional)*: generate static properties or not
> ⚠️ If extension name is not set or is `Analytics`, it will generate the following typaloas `typealias Analytics = String`.
> ⚠️ If extension name is not set or is `Analytics`, it will generate the following typealias `typealias Analytics = String`.
### YAML
@ -186,23 +183,87 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
7. `comments` *(optional)*
8. `parameters` *(optional)*
**Parameters**
**Parameters**
You can use parameters in generate methods.
1. `name`: name of the parameter
2. `type`: type of the parameter (Int, String, Bool, Double)
3. `value`: value of the parameter
4. `defaultValue`: defaultValue of the parameter
3. `replaceIn` *(optional)*
**Value**
If you want to send another parameter with a static value. For example, you want to send to which screen the event is triggered.
You can add the parameter 'screenName' for example and its 'value' is 'Home'. With this, you do not need to specify the value in the function call.
**DefaultValue**
If you want ta add a parameter in the call of the function but you want to make it optionnal with a default value you need to use this property.
Example:
```
events:
id: id_of_tag
name: _TITLE_
tags: ios,droid
parameters:
- name: title
type: String
defaultValue: someTitle
```
The generated method will be:
```
logIdOfTag(title: String = "someTitle")
```
**Replace in**
This is section is equivalent of `%s | %d | %f | %@`. You can put the content of the parameter in *name*, *path*, *action*, *category*.
You need to put `_` + `NAME OF THE PARAMETER` + `_` in the target and which target you want in the value of `replaceIn`. (name need to be in uppercase)
You need to put `_` + `NAME OF THE PARAMETER` + `_` in the target and which target you want in the value of `replaceIn`. (name need to be in uppercase).
You can't use `value`and `replaceIn`in thge same time.
Example:
```
events:
id: id_of_tag
name: _TITLE_
tags: ios,droid
parameters:
- name: title
type: String
replaceIn: name
```
In this sample, we want to add the parameter `title` in the field `name`. So, we need to place `_TITLE_` in the field `name`.
The generated method will be:
```
logIdOfTag(title: String)
```
You can also want to replace a parameter in an other parameter. You can do this with the `replaceIn` property. The condition is that the parameter which will use the `replaceIn`need to have the `value`property
Example:
```
events:
id: id_of_tag
name: title
tags: ios,droid
parameters:
- name: something
type: String
value: test _TEXT_
- name: text
type: String
replaceIn: something
```
## Images
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. If the extension name is not specified, no extension will be generated.
```sh
swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
@ -219,12 +280,13 @@ swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
1. `-f`: force generation
2. Input images definitions file
3. `--xcassets-path`: xcasset path where to generate imagesets
4. `--extension-output-path`: path where to generate generated extension
4. `--extension-output-path` *(optional)* : path where to generate generated extension
5. `--extension-name` *(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`)
7. `--static-members` *(optional)*: generate static properties or not
> ⚠️ Passing a `extensionOutputPath` without any `extensionName` does not generate extension. **But** passing an `extensionName` without `extensionOutputPath` will result in a error.
> ⚠️ Svg images will be copied in the assets and rendered as "Original", however if those images are not rendered correctly you can force the png generation by adding the key word "png" like this: id arrow_back 15 ? png
## All at once
@ -287,6 +349,15 @@ tags:
extensionName: String?
extensionSuffix: String?
staticMembers: Bool?
analytics:
-
inputFile: String
target: String
extensionOutputPath: String
extensionName: String?
extensionSuffix: String?
staticMembers: Bool?
```
### Multiple configurations

View File

@ -1,21 +1,21 @@
// Generated by ResgenSwift.Color 2.1.0
// Generated by ResgenSwift.Color 2.2.0
import SwiftUI
extension ColorYolo {
/// Color red is #FF0000 (light) or #FF0000 (dark)"
var red: Color {
public var red: Color {
Color("red")
}
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
var green_alpha_50: Color {
public var green_alpha_50: Color {
Color("green_alpha_50")
}
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
var blue_light_dark: Color {
public var blue_light_dark: Color {
Color("blue_light_dark")
}
}

View File

@ -1,21 +1,21 @@
// Generated by ResgenSwift.Color 1.2
// Generated by ResgenSwift.Color 2.2.0
import UIKit
extension UIColorYolo {
/// Color red is #FF0000 (light) or #FF0000 (dark)"
@objc var red: UIColor {
@objc public var red: UIColor {
UIColor(named: "red")!
}
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
@objc var green_alpha_50: UIColor {
@objc public var green_alpha_50: UIColor {
UIColor(named: "green_alpha_50")!
}
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
@objc var blue_light_dark: UIColor {
@objc public var blue_light_dark: UIColor {
UIColor(named: "blue_light_dark")!
}
}

View File

@ -1,10 +1,10 @@
// Generated by ResgenSwift.Fonts 2.1.0
// Generated by ResgenSwift.Fonts 2.2.0
import SwiftUI
extension FontYolo {
enum FontName: String {
public enum FontName: String {
case LatoItalic = "Lato-Italic"
case LatoLightItalic = "Lato-LightItalic"
case LatoHairline = "Lato-Hairline"
@ -19,43 +19,43 @@ extension FontYolo {
// MARK: - Getter
func LatoItalic(withSize size: CGFloat) -> Font {
public func LatoItalic(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoItalic.rawValue, size: size)
}
func LatoLightItalic(withSize size: CGFloat) -> Font {
public func LatoLightItalic(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoLightItalic.rawValue, size: size)
}
func LatoHairline(withSize size: CGFloat) -> Font {
public func LatoHairline(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoHairline.rawValue, size: size)
}
func LatoBold(withSize size: CGFloat) -> Font {
public func LatoBold(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoBold.rawValue, size: size)
}
func LatoBlack(withSize size: CGFloat) -> Font {
public func LatoBlack(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoBlack.rawValue, size: size)
}
func LatoRegular(withSize size: CGFloat) -> Font {
public func LatoRegular(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoRegular.rawValue, size: size)
}
func LatoBlackItalic(withSize size: CGFloat) -> Font {
public func LatoBlackItalic(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoBlackItalic.rawValue, size: size)
}
func LatoBoldItalic(withSize size: CGFloat) -> Font {
public func LatoBoldItalic(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoBoldItalic.rawValue, size: size)
}
func LatoLight(withSize size: CGFloat) -> Font {
public func LatoLight(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoLight.rawValue, size: size)
}
func LatoHairlineItalic(withSize size: CGFloat) -> Font {
public func LatoHairlineItalic(withSize size: CGFloat) -> Font {
Font.custom(FontName.LatoHairlineItalic.rawValue, size: size)
}
}

View File

@ -1,10 +1,10 @@
// Generated by ResgenSwift.Fonts 2.1.0
// Generated by ResgenSwift.Fonts 2.2.0
import UIKit
extension UIFontYolo {
enum FontName: String {
public enum FontName: String {
case LatoItalic = "Lato-Italic"
case LatoLightItalic = "Lato-LightItalic"
case LatoHairline = "Lato-Hairline"
@ -19,43 +19,43 @@ extension UIFontYolo {
// MARK: - Getter
func LatoItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
}
func LatoLightItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
}
func LatoHairline(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
}
func LatoBold(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoBold.rawValue, size: size)!
}
func LatoBlack(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
}
func LatoRegular(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
}
func LatoBlackItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
}
func LatoBoldItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
}
func LatoLight(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoLight.rawValue, size: size)!
}
func LatoHairlineItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
}
public func LatoItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
}
public func LatoLightItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
}
public func LatoHairline(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
}
public func LatoBold(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoBold.rawValue, size: size)!
}
public func LatoBlack(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
}
public func LatoRegular(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
}
public func LatoBlackItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
}
public func LatoBoldItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
}
public func LatoLight(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoLight.rawValue, size: size)!
}
public func LatoHairlineItalic(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
}
}

View File

@ -1,31 +1,31 @@
// Generated by ResgenSwift.Images 2.1.0
// Generated by ResgenSwift.Images 2.2.0
// Images from sampleImages
import SwiftUI
extension ImageYolo {
var article_notification_pull_detail: Image {
public var article_notification_pull_detail: Image {
Image("article_notification_pull_detail")
}
var article_notification_pull: Image {
public var article_notification_pull: Image {
Image("article_notification_pull")
}
var new_article: Image {
public var new_article: Image {
Image("new_article")
}
var welcome_background: Image {
public var welcome_background: Image {
Image("welcome_background")
}
var article_trash: Image {
public var article_trash: Image {
Image("article_trash")
}
var ic_close_article: Image {
public var ic_close_article: Image {
Image("ic_close_article")
}
}

View File

@ -1,31 +1,31 @@
// Generated by ResgenSwift.Images 2.1.0
// Generated by ResgenSwift.Images 2.2.0
// Images from sampleImages
import UIKit
extension UIImageYolo {
var article_notification_pull_detail: UIImage {
public var article_notification_pull_detail: UIImage {
UIImage(named: "article_notification_pull_detail")!
}
var article_notification_pull: UIImage {
public var article_notification_pull: UIImage {
UIImage(named: "article_notification_pull")!
}
var new_article: UIImage {
public var new_article: UIImage {
UIImage(named: "new_article")!
}
var welcome_background: UIImage {
public var welcome_background: UIImage {
UIImage(named: "welcome_background")!
}
var article_trash: UIImage {
public var article_trash: UIImage {
UIImage(named: "article_trash")!
}
var ic_close_article: UIImage {
public var ic_close_article: UIImage {
UIImage(named: "ic_close_article")!
}
}

View File

@ -1,4 +1,4 @@
// Generated by ResgenSwift.Strings.Stringium 2.1.0
// Generated by ResgenSwift.Strings.Stringium 2.2.0
import UIKit
@ -6,7 +6,7 @@ fileprivate let kStringsFileName = "sampleStrings"
extension String {
enum KeyGenAllScript: String {
public enum KeyGenAllScript: String {
case param_lang = "param_lang"
case generic_back = "generic_back"
case generic_loading_data = "generic_loading_data"
@ -14,7 +14,7 @@ extension String {
case test_equal_symbol = "test_equal_symbol"
case placeholders_test_one = "placeholders_test_one"
var keyPath: KeyPath<String, String> {
public var keyPath: KeyPath<String, String> {
switch self {
case .param_lang: return \String.param_lang
case .generic_back: return \String.generic_back
@ -33,7 +33,7 @@ extension String {
///
/// Comment :
/// No comment
var param_lang: String {
public var param_lang: String {
NSLocalizedString("param_lang", tableName: kStringsFileName, bundle: Bundle.main, value: "en", comment: "")
}
@ -44,7 +44,7 @@ extension String {
///
/// Comment :
/// No comment
var generic_back: String {
public var generic_back: String {
NSLocalizedString("generic_back", tableName: kStringsFileName, bundle: Bundle.main, value: "Back", comment: "")
}
@ -53,7 +53,7 @@ extension String {
///
/// Comment :
/// No comment
var generic_loading_data: String {
public var generic_loading_data: String {
NSLocalizedString("generic_loading_data", tableName: kStringsFileName, bundle: Bundle.main, value: "Loading data...", comment: "")
}
@ -62,7 +62,7 @@ extension String {
///
/// Comment :
/// No comment
var generic_welcome_firstname_format: String {
public var generic_welcome_firstname_format: String {
NSLocalizedString("generic_welcome_firstname_format", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "")
}
/// Translation in en :
@ -70,7 +70,7 @@ extension String {
///
/// Comment :
/// No comment
func generic_welcome_firstname_format(arg0: String) -> String {
public func generic_welcome_firstname_format(arg0: String) -> String {
String(format: self.generic_welcome_firstname_format, arg0)
}
@ -81,7 +81,7 @@ extension String {
///
/// Comment :
/// No comment
var test_equal_symbol: String {
public var test_equal_symbol: String {
NSLocalizedString("test_equal_symbol", tableName: kStringsFileName, bundle: Bundle.main, value: "1€ = 1 point !", comment: "")
}
@ -92,7 +92,7 @@ extension String {
///
/// Comment :
/// No comment
var placeholders_test_one: String {
public var placeholders_test_one: String {
NSLocalizedString("placeholders_test_one", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "")
}
/// Translation in en :
@ -100,7 +100,7 @@ extension String {
///
/// Comment :
/// No comment
func placeholders_test_one(arg0: String, arg1: String, arg2: Int) -> String {
public func placeholders_test_one(arg0: String, arg1: String, arg2: Int) -> String {
String(format: self.placeholders_test_one, arg0, arg1, arg2)
}
}

View File

@ -0,0 +1,150 @@
{
"sourceLanguage" : "en",
"strings" : {
"generic_back" : {
"comment" : "",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Back"
}
},
"en-us" : {
"stringUnit" : {
"state" : "translated",
"value" : "Back"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Retour"
}
}
}
},
"generic_loading_data" : {
"comment" : "",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Loading data..."
}
},
"en-us" : {
"stringUnit" : {
"state" : "translated",
"value" : "Loading data..."
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Chargement des données..."
}
}
}
},
"generic_welcome_firstname_format" : {
"comment" : "",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Welcome \\\"%@\\\" !"
}
},
"en-us" : {
"stringUnit" : {
"state" : "translated",
"value" : "Welcome \\\"%@\\\" !"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bienvenue \\\"%@\\\" !"
}
}
}
},
"param_lang" : {
"comment" : "",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "en"
}
},
"en-us" : {
"stringUnit" : {
"state" : "translated",
"value" : "en-us"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "fr"
}
}
}
},
"placeholders_test_one" : {
"comment" : "",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "You %%: %2$@ %1$@ Age: %3$d"
}
},
"en-us" : {
"stringUnit" : {
"state" : "translated",
"value" : "You %%: %2$@ %1$@ Age: %3$d"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Vous %%: %1$@ %2$@ Age: %3$d"
}
}
}
},
"test_equal_symbol" : {
"comment" : "",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "1€ = 1 point !"
}
},
"en-us" : {
"stringUnit" : {
"state" : "translated",
"value" : "1€ = 1 point !"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "1€ = 1 point !"
}
}
}
}
},
"version" : "1.0"
}

View File

@ -1,19 +1,27 @@
// Generated by ResgenSwift.Analytics 2.1.0
// Generated by ResgenSwift.Analytics 2.2.0
import Foundation
import MatomoTracker
import FirebaseAnalytics
// MARK: - Protocol
protocol AnalyticsManagerProtocol {
public protocol AnalyticsManagerProtocol {
func logScreen(
name: String,
path: String,
params: [String: Any]?
)
func logScreen(name: String, path: String)
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
func setEnable(_ enable: Bool)
}
// MARK: - Matomo
@ -48,8 +56,11 @@ class MatomoAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Methods
func logScreen(name: String, path: String) {
guard !tracker.isOptedOut else { return }
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\(trackerUrl)" + "/" + "\(path)" + "iOS")
@ -65,8 +76,6 @@ class MatomoAnalyticsManager: AnalyticsManagerProtocol {
category: String,
params: [String: Any]?
) {
guard !tracker.isOptedOut else { return }
tracker.track(
eventWithCategory: category,
action: action,
@ -75,32 +84,30 @@ class MatomoAnalyticsManager: AnalyticsManagerProtocol {
url: nil
)
}
func setEnable(_ enable: Bool) {
tracker.isOptedOut = !enable
}
}
// MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
func logScreen(name: String, path: String) {
// MARK: - Methods
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
var parameters = [
AnalyticsParameterScreenName: name as NSObject
]
Analytics.logEvent(
AnalyticsEventScreenView,
parameters: parameters
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
"action": action as NSObject,
"category": category as NSObject,
]
if path.isEmpty == false {
parameters["path"] = path + "/iOS" as NSObject
}
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
@ -115,45 +122,123 @@ class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
}
Analytics.logEvent(
name.replacingOccurrences(of: [" "], with: "_"),
AnalyticsEventScreenView,
parameters: parameters
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
AnalyticsParameterItemName: name.replacingOccurrences(of: " ", with: "_") as NSObject
]
if category.isEmpty == false {
parameters["AnalyticsParameterItemCategory"] = category as NSObject
}
if action.isEmpty == false {
parameters["action"] = action as NSObject
}
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
AnalyticsEventSelectContent,
parameters: parameters
)
}
func setEnable(_ enable: Bool) {
Analytics.setAnalyticsCollectionEnabled(enable)
}
}
// MARK: - Traker Type
public enum TrackerType: CaseIterable {
case matomo
case firebase
}
// MARK: - Manager
class AnalyticsManager {
public class AnalyticsManager {
static var shared = AnalyticsManager()
public static var shared = AnalyticsManager()
private init() {}
// MARK: - Properties
var managers: [AnalyticsManagerProtocol] = []
var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
private var isEnabled: Bool = true
// MARK: - Methods
func setAnalyticsEnabled(_ enable: Bool) {
isEnabled = enable
private var isEnabled: Bool {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
func configure(siteId: String, url: String) {
managers.append(
MatomoAnalyticsManager(
siteId: siteId,
url: url
)
// MARK: - Enable Methods
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
managers.forEach { (key, value) in
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
public func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
public func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
}
public func configure(siteId: String, url: String) {
managers[TrackerType.matomo] = MatomoAnalyticsManager(
siteId: siteId,
url: url
)
managers.append(FirebaseAnalyticsManager())
managers[TrackerType.firebase] = FirebaseAnalyticsManager()
}
private func logScreen(name: String, path: String) {
// MARK: - Private Log Methods
private func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return }
managers.forEach { manager in
manager.logScreen(name: name, path: path)
managers.values.forEach { manager in
manager.logScreen(
name: name,
path: path,
params: params
)
}
}
@ -165,7 +250,7 @@ class AnalyticsManager {
) {
guard isEnabled else { return }
managers.forEach { manager in
managers.values.forEach { manager in
manager.logEvent(
name: name,
action: action,
@ -177,31 +262,39 @@ class AnalyticsManager {
// MARK: - section_one
func logScreenS1DefOne(title: String) {
public func logScreenS1DefOne(title: String) {
logScreen(
name: "s1 def one \(title)",
path: "s1_def_one/\(title)"
path: "s1_def_one/\(title)",
params: nil
)
}
func logEventS1DefTwo(title: String, count: String) {
public func logEventS1DefTwo(
title: String,
count: String,
test2: String = "test"
) {
logEvent(
name: "s1 def two",
action: "test",
category: "test",
params: [
"title": title,
"count": count
"count": count,
"test": "test",
"test2": test2
]
)
}
// MARK: - section_two
func logScreenS2DefOne() {
public func logScreenS2DefOne() {
logScreen(
name: "s2 def one",
path: "s2_def_one/"
path: "s2_def_one/",
params: nil
)
}
}

View File

@ -1,4 +1,4 @@
// Generated by ResgenSwift.Strings.Tags 2.1.0
// Generated by ResgenSwift.Strings.Tags 2.2.0
import UIKit
@ -11,8 +11,7 @@ extension Tags {
///
/// Comment :
/// No comment
var screen_one: String {
public var screen_one: String {
"Ecran un"
}
@ -21,8 +20,7 @@ extension Tags {
///
/// Comment :
/// No comment
var screen_two: String {
public var screen_two: String {
"Ecran deux"
}
}

View File

@ -22,6 +22,12 @@ categories:
type: String
- name: count
type: String
- name: test
type: String
value: test
- name: test2
type: String
defaultValue: test
- id: section_two
screens:

View File

@ -50,14 +50,15 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/sampleTags.txt
--extension-name "Tags" \
--extension-suffix "GenAllScript"
#echo "\n-------------------------\n"
echo "\n-------------------------\n"
# Analytics
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \
--target "matomo firebase" \
--target "firebase" \
--extension-output-path "./Tags/Generated" \
--extension-name "Analytics" \
--extension-suffix "GenAllScript"
--extension-suffix "GenAllScript" \
--static-members true
echo "\n-------------------------\n"

View File

@ -30,13 +30,14 @@ architecture:
#
strings:
-
inputFile: ./Strings/sampleStrings.txt
outputPath: ./Strings/Generated
langs: "fr en en-us"
defaultLang: en
extensionOutputPath: ./Strings/Generated
extensionName: String
extensionSuffix: GenAllScript
inputFile: ./Strings/sampleStrings.txt
outputPath: ./Strings/Generated
langs: "fr en en-us"
defaultLang: en
extensionOutputPath: ./Strings/Generated
extensionName: String
extensionSuffix: GenAllScript
visibility: public
#
@ -50,6 +51,7 @@ images:
extensionName: ImageYolo
extensionNameUIKit: UIImageYolo
extensionSuffix: GenAllScript
visibility: public
#
@ -57,13 +59,14 @@ images:
#
colors:
-
inputFile: ./Colors/sampleColors1.txt
style: all
xcassetsPath: ./Colors/colors.xcassets
extensionOutputPath: ./Colors/Generated/
extensionName: ColorYolo
extensionNameUIKit: UIColorYolo
extensionSuffix: GenAllScript
inputFile: ./Colors/sampleColors1.txt
style: all
xcassetsPath: ./Colors/colors.xcassets
extensionOutputPath: ./Colors/Generated/
extensionName: ColorYolo
extensionNameUIKit: UIColorYolo
extensionSuffix: GenAllScript
visibility: public
#
@ -76,6 +79,7 @@ tags:
extensionOutputPath: ./Tags/Generated
extensionName: Tags
extensionSuffix: GenAllScript
visibility: public
#
@ -85,9 +89,8 @@ analytics:
-
inputFile: ./Tags/sampleTags.yml
target: "matomo firebase"
extensionOutputPath: ./Tags/Generated
extensionName: Analytics
extensionSuffix: GenAllScript
outputFile: ./Tags/Generated/Analytics+GenAllScript.swift
visibility: public
#
@ -95,9 +98,10 @@ analytics:
#
fonts:
-
inputFile: ./Fonts/sampleFontsAll.txt
extensionOutputPath: ./Fonts/Generated
extensionName: FontYolo
extensionNameUIKit: UIFontYolo
extensionSuffix: GenAllScript
infoPlistPaths: "./Fonts/Generated/test.plist ./Fonts/Generated/test2.plist"
inputFile: ./Fonts/sampleFontsAll.txt
extensionOutputPath: ./Fonts/Generated
extensionName: FontYolo
extensionNameUIKit: UIFontYolo
extensionSuffix: GenAllScript
infoPlistPaths: "./Fonts/Generated/test.plist ./Fonts/Generated/test2.plist"
visibility: public

View File

@ -50,8 +50,8 @@ struct Analytics: ParsableCommand {
target: options.target,
tags: ["ios", "iosonly"],
staticVar: options.staticMembers,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath
outputFile: options.outputFile,
visibility: options.extensionVisibility
)
print("[\(Self.toolName)] Analytics generated")
@ -79,7 +79,7 @@ struct Analytics: ParsableCommand {
guard GeneratorChecker.shouldGenerate(
force: options.forceGeneration,
inputFilePath: options.inputFile,
extensionFilePath: options.extensionFilePath
extensionFilePath: options.outputFile
) else {
print("[\(Self.toolName)] Analytics are already up to date :) ")
return false

View File

@ -7,45 +7,41 @@
import ArgumentParser
import Foundation
// swiftlint:disable no_grouping_extension
import ToolCore
struct AnalyticsOptions: ParsableArguments {
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
@Flag(
name: [.customShort("f"), .customShort("F")],
help: "Should force generation"
)
var forceGeneration = false
@Argument(help: "Input files where tags ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@Argument(
help: "Input files where tags ared defined.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var inputFile: String
@Option(help: "Target(s) analytics to generate. (\"matomo\" | \"firebase\")")
@Option(
help: "Target(s) analytics to generate. (\"matomo\" | \"firebase\")",
completion: .list(["matotmo", "firebase"])
)
var target: String
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var extensionOutputPath: String
@Option(
help: "Where to generate the analytics manager (path with filename)",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var outputFile: String
@Option(help: "Tell if it will generate static properties or not")
var staticMembers: Bool = false
@Option(help: "Extension name. If not specified, it will generate a Analytics extension.")
var extensionName: String = Analytics.defaultExtensionName
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Analytics{extensionSuffix}.swift")
var extensionSuffix: String?
}
// MARK: - Computed var
extension AnalyticsOptions {
var extensionFileName: String {
if let extensionSuffix {
return "\(extensionName)+\(extensionSuffix).swift"
}
return "\(extensionName).swift"
}
var extensionFilePath: String {
"\(extensionOutputPath)/\(extensionFileName)"
}
@Option(
name: .customLong("visibility"),
help: "Visibility of extension and properties. Possibles values: public, private, package, internal. Default is internal",
completion: .list(["public", "private", "package", "internal"])
)
var extensionVisibility: ExtensionVisibility = .internal
}

View File

@ -5,11 +5,14 @@
// Created by Loris Perret on 08/12/2023.
//
// CPD-OFF
import CoreVideo
import Foundation
import ToolCore
// Disabled cause it's a pain to handle in generated string
// swiftlint:disable type_body_length
enum AnalyticsGenerator {
@ -20,8 +23,8 @@ enum AnalyticsGenerator {
target: String,
tags: [String],
staticVar: Bool,
extensionName: String,
extensionFilePath: String
outputFile: String,
visibility: ExtensionVisibility
) {
// Get target type from enum
let targetsString: [String] = target.components(separatedBy: " ")
@ -41,15 +44,15 @@ enum AnalyticsGenerator {
sections: sections,
tags: tags,
staticVar: staticVar,
extensionName: extensionName
visibility: visibility
)
// Write content
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
let outputFilePathURL = URL(fileURLWithPath: outputFile)
do {
try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
try extensionFileContent.write(to: outputFilePathURL, atomically: false, encoding: .utf8)
} catch {
let error = AnalyticsError.writeFile(extensionFilePath, error.localizedDescription)
let error = AnalyticsError.writeFile(outputFile, error.localizedDescription)
print(error.description)
Analytics.exit(withError: error)
}
@ -62,18 +65,19 @@ enum AnalyticsGenerator {
sections: [AnalyticsCategory],
tags: [String],
staticVar: Bool,
extensionName: String
visibility: ExtensionVisibility
) -> String {
[
getHeader(
targets: targets,
extensionClassname: extensionName,
staticVar: staticVar
staticVar: staticVar,
visibility: visibility
),
getProperties(
sections: sections,
tags: tags,
staticVar: staticVar
staticVar: staticVar,
visibility: visibility
),
getFooter()
]
@ -84,41 +88,87 @@ enum AnalyticsGenerator {
private static func getHeader(
targets: [TrackerType],
extensionClassname: String,
staticVar: Bool
staticVar: Bool,
visibility: ExtensionVisibility
) -> String {
"""
// Generated by ResgenSwift.\(Analytics.toolName) \(ResgenSwiftVersion)
\(getImport(targets: targets))
\(Self.getImport(targets: targets))
\(Self.getAnalyticsProtocol(targets: targets, visibility: visibility))
\(Self.getTrackerTypeEnum(targets: targets, visibility: visibility))
\(getAnalyticsProtocol(targets: targets))
// MARK: - Manager
class AnalyticsManager {
\(visibility) class AnalyticsManager {
static var shared = AnalyticsManager()
\(visibility) static var shared = AnalyticsManager()
private init() {}
// MARK: - Properties
var managers: [AnalyticsManagerProtocol] = []
var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
\(getEnabledContent())
\(Self.getEnabledContent(visibility: visibility))
\(getAnalyticsProperties(targets: targets))
\(Self.getAnalyticsProperties(targets: targets, visibility: visibility))
\(getPrivateLogFunction())
\(Self.getPrivateLogFunction())
"""
}
private static func getEnabledContent() -> String {
private static func getTrackerTypeEnum(
targets: [TrackerType],
visibility: ExtensionVisibility
) -> String {
var result: [String] = []
targets.forEach { type in
result.append(" case \(type)")
}
return """
// MARK: - Traker Type
\(visibility) enum TrackerType: CaseIterable {
\(result.joined(separator: "\n"))
}
"""
private var isEnabled: Bool = true
}
// MARK: - Methods
private static func getEnabledContent(
visibility: ExtensionVisibility
) -> String {
"""
private var isEnabled: Bool {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
func setAnalyticsEnabled(_ enable: Bool) {
isEnabled = enable
// MARK: - Enable Methods
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
managers.forEach { (key, value) in
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
\(visibility) func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
\(visibility) func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
}
"""
}
@ -126,9 +176,12 @@ enum AnalyticsGenerator {
private static func getImport(targets: [TrackerType]) -> String {
var result: [String] = []
result.append("import Foundation")
if targets.contains(TrackerType.matomo) {
result.append("import MatomoTracker")
}
if targets.contains(TrackerType.firebase) {
result.append("import FirebaseAnalytics")
}
@ -138,11 +191,21 @@ enum AnalyticsGenerator {
private static func getPrivateLogFunction() -> String {
"""
private func logScreen(name: String, path: String) {
// MARK: - Private Log Methods
private func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return }
managers.forEach { manager in
manager.logScreen(name: name, path: path)
managers.values.forEach { manager in
manager.logScreen(
name: name,
path: path,
params: params
)
}
}
@ -154,7 +217,7 @@ enum AnalyticsGenerator {
) {
guard isEnabled else { return }
managers.forEach { manager in
managers.values.forEach { manager in
manager.logEvent(
name: name,
action: action,
@ -166,29 +229,31 @@ enum AnalyticsGenerator {
"""
}
private static func getAnalyticsProperties(targets: [TrackerType]) -> String {
private static func getAnalyticsProperties(
targets: [TrackerType],
visibility: ExtensionVisibility
) -> String {
var header = ""
var content: [String] = []
let footer = " }"
if targets.contains(TrackerType.matomo) {
header = "func configure(siteId: String, url: String) {"
header = "\(visibility) func configure(siteId: String, url: String) {"
} else if targets.contains(TrackerType.firebase) {
header = "func configure() {"
header = "\(visibility) func configure() {"
}
if targets.contains(TrackerType.matomo) {
content.append("""
managers.append(
MatomoAnalyticsManager(
siteId: siteId,
url: url
)
managers[TrackerType.matomo] = MatomoAnalyticsManager(
siteId: siteId,
url: url
)
""")
}
if targets.contains(TrackerType.firebase) {
content.append(" managers.append(FirebaseAnalyticsManager())")
content.append(" managers[TrackerType.firebase] = FirebaseAnalyticsManager()")
}
return [
@ -199,21 +264,30 @@ enum AnalyticsGenerator {
.joined(separator: "\n")
}
private static func getAnalyticsProtocol(targets: [TrackerType]) -> String {
private static func getAnalyticsProtocol(
targets: [TrackerType],
visibility: ExtensionVisibility
) -> String {
let proto = """
// MARK: - Protocol
protocol AnalyticsManagerProtocol {
\(visibility) protocol AnalyticsManagerProtocol {
func logScreen(
name: String,
path: String,
params: [String: Any]?
)
func logScreen(name: String, path: String)
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
}
func setEnable(_ enable: Bool)
}
"""
var result: [String] = [proto]
@ -226,13 +300,14 @@ enum AnalyticsGenerator {
result.append(FirebaseGenerator.service)
}
return result.joined(separator: "\n")
return result.joined(separator: "\n\n")
}
private static func getProperties(
sections: [AnalyticsCategory],
tags: [String],
staticVar: Bool
staticVar: Bool,
visibility: ExtensionVisibility
) -> String {
sections
.compactMap { section in
@ -248,9 +323,9 @@ enum AnalyticsGenerator {
}
if staticVar {
res += "\n\n\(definition.getStaticProperty())"
res += "\n\n\(definition.getStaticProperty(visibility: visibility))"
} else {
res += "\n\n\(definition.getProperty())"
res += "\n\n\(definition.getProperty(visibility: visibility))"
}
}
return res
@ -265,3 +340,5 @@ enum AnalyticsGenerator {
"""
}
}
// CPD-ON

View File

@ -16,6 +16,7 @@ enum FirebaseGenerator {
Self.header,
Self.logScreen,
Self.logEvent,
Self.enable,
Self.footer
]
.joined(separator: "\n")
@ -28,16 +29,39 @@ enum FirebaseGenerator {
// MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Methods
"""
}
private static var logScreen: String {
"""
func logScreen(name: String, path: String) {
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
var parameters = [
AnalyticsParameterScreenName: name as NSObject
]
if path.isEmpty == false {
parameters["path"] = path + "/iOS" as NSObject
}
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
AnalyticsEventScreenView,
parameters: parameters
@ -56,10 +80,17 @@ enum FirebaseGenerator {
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
"action": action as NSObject,
"category": category as NSObject,
AnalyticsParameterItemName: name.replacingOccurrences(of: " ", with: "_") as NSObject
]
if category.isEmpty == false {
parameters["AnalyticsParameterItemCategory"] = category as NSObject
}
if action.isEmpty == false {
parameters["action"] = action as NSObject
}
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
@ -73,17 +104,25 @@ enum FirebaseGenerator {
}
Analytics.logEvent(
name.replacingOccurrences(of: [" "], with: "_"),
AnalyticsEventSelectContent,
parameters: parameters
)
}
"""
}
private static var enable: String {
"""
func setEnable(_ enable: Bool) {
Analytics.setAnalyticsCollectionEnabled(enable)
}
"""
}
private static var footer: String {
"""
}
"""
}
}

View File

@ -5,6 +5,8 @@
// Created by Loris Perret on 05/12/2023.
//
// CPD-OFF
import Foundation
enum MatomoGenerator {
@ -15,6 +17,7 @@ enum MatomoGenerator {
Self.setup,
Self.logScreen,
Self.logEvent,
Self.enable,
Self.footer
]
.joined(separator: "\n")
@ -66,8 +69,11 @@ enum MatomoGenerator {
private static var logScreen: String {
"""
func logScreen(name: String, path: String) {
guard !tracker.isOptedOut else { return }
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
@ -88,8 +94,6 @@ enum MatomoGenerator {
category: String,
params: [String: Any]?
) {
guard !tracker.isOptedOut else { return }
tracker.track(
eventWithCategory: category,
action: action,
@ -98,13 +102,23 @@ enum MatomoGenerator {
url: nil
)
}
"""
}
private static var enable: String {
"""
func setEnable(_ enable: Bool) {
tracker.isOptedOut = !enable
}
"""
}
private static var footer: String {
"""
}
"""
}
}
// CPD-ON

View File

@ -51,17 +51,25 @@ class AnalyticsDefinition {
}
private func getParameters() -> String {
var params = parameters
var result: String
if type == .screen {
params = params.filter { param in
!param.replaceIn.isEmpty
}
}
let paramsString = parameters.compactMap { parameter -> String? in
guard parameter.value.isEmpty else { return nil }
let paramsString = params.map { parameter in
"\(parameter.name): \(parameter.type)"
let defaultValue: String
switch parameter.type {
case .bool:
defaultValue = "\(parameter.defaultValue.lowercased())"
case .int, .double:
defaultValue = "\(parameter.defaultValue)"
case .string:
defaultValue = "\"\(parameter.defaultValue)\""
}
let defaultValueString = parameter.defaultValue.isEmpty ? "" : " = \(defaultValue)"
return "\(parameter.name): \(parameter.type.rawValue)\(defaultValueString)"
}
if paramsString.count > 2 {
@ -84,10 +92,17 @@ class AnalyticsDefinition {
for rep in parameter.replaceIn {
switch rep {
case "name": name = name.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
case "path": path = path.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
case "category": category = category.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
case "action": action = action.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
default: break
default:
if let param = parameters.first(where: { $0.name == rep }), param.value.isEmpty == false {
param.value = param.value.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
}
}
}
}
@ -102,7 +117,20 @@ class AnalyticsDefinition {
}
supplementaryParams.forEach { param in
params.append("\"\(param.name)\": \(param.name)")
if param.value.isEmpty {
params.append("\"\(param.name)\": \(param.name)")
} else {
switch param.type {
case .bool:
params.append("\"\(param.name)\": \(param.value.lowercased())")
case .int, .double:
params.append("\"\(param.name)\": \(param.value)")
case .string:
params.append("\"\(param.name)\": \"\(param.value)\"")
}
}
}
if params.count > 1 {
@ -116,14 +144,15 @@ class AnalyticsDefinition {
[\(params.joined(separator: ", "))]
"""
} else {
result = "[:]"
result = "nil"
}
if type == .screen {
return """
logScreen(
name: "\(name)",
path: "\(path)"
path: "\(path)",
params: \(result)
)
"""
} else {
@ -140,20 +169,20 @@ class AnalyticsDefinition {
// MARK: - Raw strings
func getProperty() -> String {
func getProperty(visibility: ExtensionVisibility) -> String {
replaceIn()
return """
func \(getFuncName())\(getParameters()) {
\(visibility) func \(getFuncName())\(getParameters()) {
\(getlogFunction())
}
"""
}
func getStaticProperty() -> String {
func getStaticProperty(visibility: ExtensionVisibility) -> String {
replaceIn()
return """
static func \(getFuncName())\(getParameters()) {
\(getlogFunction())
\(visibility) static func \(getFuncName())\(getParameters()) {
AnalyticsManager.shared.\(getlogFunction())
}
"""
}

View File

@ -46,5 +46,7 @@ struct AnalyticsParameterDTO: Codable {
var name: String
var type: String
var value: String?
var defaultValue: String?
var replaceIn: String?
}

View File

@ -12,13 +12,17 @@ class AnalyticsParameter {
// MARK: - Properties
var name: String
var type: String
var type: ParameterType
var value: String
var defaultValue: String
var replaceIn: [String] = []
// MARK: - Init
init(name: String, type: String) {
init(name: String, type: ParameterType, value: String, defaultValue: String) {
self.name = name
self.type = type
self.value = value
self.defaultValue = defaultValue
}
}

View File

@ -0,0 +1,16 @@
//
// File.swift
//
//
// Created by Loris Perret on 17/07/2024.
//
import Foundation
enum ParameterType: String {
case string = "String"
case int = "Int"
case double = "Double"
case bool = "Bool"
}

View File

@ -67,25 +67,61 @@ class AnalyticsFileParser {
}
}
private func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
parameters.map { dtoParameter in
private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
func verify(value: String?, for type: ParameterType) {
guard let value, value.isEmpty == false else { return }
switch type {
case .int:
if Int(value) == nil {
let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)")
print(error.description)
Analytics.exit(withError: error)
}
case .bool:
if Bool(value.lowercased()) == nil {
let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)")
print(error.description)
Analytics.exit(withError: error)
}
case .double:
if Double(value) == nil {
let error = AnalyticsError.invalidParameter("type of \(value) is not \(type)")
print(error.description)
Analytics.exit(withError: error)
}
case .string:
break
}
}
return parameters.map { dtoParameter in
// Type
let type = dtoParameter.type.uppercasedFirst()
guard
type == "String" ||
type == "Int" ||
type == "Double" ||
type == "Bool"
else {
guard let typeEnum = ParameterType(rawValue: type) else {
let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)")
print(error.description)
Analytics.exit(withError: error)
}
if dtoParameter.value != nil, dtoParameter.replaceIn != nil {
let error = AnalyticsError.invalidParameter("you can't set 'value' and 'replaceIn' for \(dtoParameter.name)")
print(error.description)
Analytics.exit(withError: error)
}
verify(value: dtoParameter.value, for: typeEnum)
verify(value: dtoParameter.defaultValue, for: typeEnum)
let parameter = AnalyticsParameter(
name: dtoParameter.name,
type: type
type: typeEnum,
value: dtoParameter.value ?? "",
defaultValue: dtoParameter.defaultValue ?? ""
)
if let replaceIn = dtoParameter.replaceIn {
@ -114,7 +150,7 @@ class AnalyticsFileParser {
}
if let parameters {
definition.parameters = getParameters(from: parameters)
definition.parameters = Self.getParameters(from: parameters)
}
return definition
@ -140,6 +176,8 @@ class AnalyticsFileParser {
Analytics.exit(withError: error)
}
definition.path = path
} else if let path = screen.path {
definition.path = path
}
@ -176,6 +214,14 @@ class AnalyticsFileParser {
}
definition.action = action
} else {
if let category = event.category {
definition.category = category
}
if let action = event.action {
definition.action = action
}
}
return definition

View File

@ -21,8 +21,6 @@ struct Colors: ParsableCommand {
// MARK: - Static
static let toolName = "Color"
static let defaultExtensionName = "Color"
static let defaultExtensionNameUIKit = "UIColor"
static let assetsColorsFolderName = "Colors"
// MARK: - Command options
@ -57,22 +55,30 @@ struct Colors: ParsableCommand {
// -> Time: 3.4505380392074585 seconds
// Generate extension
ColorExtensionGenerator.writeExtensionFile(
colors: parsedColors,
staticVar: options.staticMembers,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath,
isSwiftUI: true
)
if let extensionName = options.extensionName,
let extensionFilePath = options.extensionFilePath {
ColorExtensionGenerator.writeExtensionFile(
colors: parsedColors,
staticVar: options.staticMembers,
extensionName: extensionName,
extensionFilePath: extensionFilePath,
isSwiftUI: true,
visibility: options.extensionVisibility
)
}
// Generate extension
ColorExtensionGenerator.writeExtensionFile(
colors: parsedColors,
staticVar: options.staticMembers,
extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: false
)
if let extensionNameUIKit = options.extensionNameUIKit,
let extensionFilePathUIKit = options.extensionFilePathUIKit {
ColorExtensionGenerator.writeExtensionFile(
colors: parsedColors,
staticVar: options.staticMembers,
extensionName: extensionNameUIKit,
extensionFilePath: extensionFilePathUIKit,
isSwiftUI: false,
visibility: options.extensionVisibility
)
}
print("[\(Self.toolName)] Colors generated")
}
@ -96,18 +102,40 @@ struct Colors: ParsableCommand {
Self.exit(withError: error)
}
// Extension for UIKit and SwiftUI should have different name
guard options.extensionName != options.extensionNameUIKit else {
let error = ColorsToolError.extensionNamesCollision(options.extensionName)
print(error.description)
Self.exit(withError: error)
// Extension for UIKit and SwiftUI should have different name (if both are defined)
if let extensionName = options.extensionName,
let extensionNameUIKit = options.extensionNameUIKit {
guard extensionName != extensionNameUIKit else {
let error = ColorsToolError.extensionNamesCollision(extensionName)
print(error.description)
Self.exit(withError: error)
}
}
// If an extension need to be generated, ensure extensionOutputPath is defined
if options.extensionName != nil ||
options.extensionNameUIKit != nil {
guard let extensionOutputPath = options.extensionOutputPath,
extensionOutputPath.isEmpty == false else {
let error = ColorsToolError.missingExtensionPath
print(error.description)
Self.exit(withError: error)
}
}
// Check if needed to regenerate
let fileToCompareToInput: String = {
// If there is no extension file to compare
// Then check the xcassets file instead
if let extensionFilePath = options.extensionFilePath {
return extensionFilePath
}
return options.xcassetsPath
}()
guard GeneratorChecker.shouldGenerate(
force: options.forceGeneration,
inputFilePath: options.inputFile,
extensionFilePath: options.extensionFilePath
extensionFilePath: fileToCompareToInput
) else {
print("[\(Self.toolName)] Colors are already up to date :) ")
return false

View File

@ -17,11 +17,12 @@ enum ColorsToolError: Error {
case fileNotExists(String)
case badColorDefinition(String, String)
case deleteExistingColors(String)
case missingExtensionPath
var description: String {
switch self {
case .extensionNamesCollision(let extensionName):
return "error: [\(Fonts.toolName)] Error on extension names, extension name and SwiftUI extension name should be different (\(extensionName) is used on both)"
return "error: [\(Colors.toolName)] Error on extension names, extension name and SwiftUI extension name should be different (\(extensionName) is used on both)"
case .badFormat(let info):
return "error: [\(Colors.toolName)] Bad line format: \(info). Accepted format are: colorName=\"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\" \"#RGB/#ARGB\""
@ -43,6 +44,9 @@ enum ColorsToolError: Error {
case .deleteExistingColors(let assetsFolder):
return "error: [\(Colors.toolName)] An error occured while deleting colors folder `\(assetsFolder)`"
case .missingExtensionPath:
return "error: [\(Colors.toolName)] Extension need to be generated but no `extensionOutputPath` is provided"
}
}
}

View File

@ -7,34 +7,54 @@
import ArgumentParser
import Foundation
import ToolCore
// swiftlint:disable no_grouping_extension
struct ColorsToolOptions: ParsableArguments {
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
@Flag(
name: [.customShort("f"), .customShort("F")],
help: "Should force generation"
)
var forceGeneration = false
@Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@Argument(
help: "Input files where colors ared defined.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var inputFile: String
@Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
var style: ColorStyle
@Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@Option(
help: "Path of xcassets where to generate colors",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var xcassetsPath: String
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var extensionOutputPath: String
@Option(help: "Tell if it will generate static properties or not")
var staticMembers: Bool = false
@Option(help: "Extension name. If not specified, it will generate an Color extension.")
var extensionName: String = Colors.defaultExtensionName
@Option(
name: .customLong("visibility"),
help: "Visibility of extension and properties. Possibles values: public, private, package, internal. Default is internal",
completion: .list(["public", "private", "package", "internal"])
)
var extensionVisibility: ExtensionVisibility = .internal
@Option(help: "SwiftUI Extension name. If not specified, it will generate an UIColor extension.")
var extensionNameUIKit: String = Colors.defaultExtensionNameUIKit
@Option(
help: "Path where to generate the extension.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var extensionOutputPath: String?
@Option(help: "SwiftUI extension name. If not specified, no extension will be generated.")
var extensionName: String?
@Option(help: "UIKit extension name. If not specified, no extension will be generated.")
var extensionNameUIKit: String?
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
var extensionSuffix: String?
@ -46,27 +66,35 @@ extension ColorsToolOptions {
// MARK: - SwiftUI
var extensionFileName: String {
var extensionFileName: String? {
guard let extensionName else { return nil }
if let extensionSuffix {
return "\(extensionName)+\(extensionSuffix).swift"
}
return "\(extensionName).swift"
}
var extensionFilePath: String {
"\(extensionOutputPath)/\(extensionFileName)"
var extensionFilePath: String? {
guard let extensionOutputPath, let extensionFileName else { return nil }
return "\(extensionOutputPath)/\(extensionFileName)"
}
// MARK: - UIKit
var extensionFileNameUIKit: String {
var extensionFileNameUIKit: String? {
guard let extensionNameUIKit else { return nil }
if let extensionSuffix {
return "\(extensionNameUIKit)+\(extensionSuffix).swift"
}
return "\(extensionNameUIKit).swift"
}
var extensionFilePathUIKit: String {
"\(extensionOutputPath)/\(extensionFileNameUIKit)"
var extensionFilePathUIKit: String? {
guard let extensionOutputPath, let extensionFileNameUIKit else { return nil }
return "\(extensionOutputPath)/\(extensionFileNameUIKit)"
}
}

View File

@ -20,14 +20,16 @@ struct ColorExtensionGenerator {
staticVar: Bool,
extensionName: String,
extensionFilePath: String,
isSwiftUI: Bool
isSwiftUI: Bool,
visibility: ExtensionVisibility
) {
// Create extension content
let extensionContent = Self.getExtensionContent(
colors: colors,
staticVar: staticVar,
extensionName: extensionName,
isSwiftUI: isSwiftUI
isSwiftUI: isSwiftUI,
visibility: visibility
)
// Write content
@ -45,11 +47,20 @@ struct ColorExtensionGenerator {
colors: [ParsedColor],
staticVar: Bool,
extensionName: String,
isSwiftUI: Bool
isSwiftUI: Bool,
visibility: ExtensionVisibility
) -> String {
[
Self.getHeader(extensionClassname: extensionName, isSwiftUI: isSwiftUI),
Self.getProperties(for: colors, withStaticVar: staticVar, isSwiftUI: isSwiftUI),
Self.getHeader(
extensionClassname: extensionName,
isSwiftUI: isSwiftUI
),
Self.getProperties(
for: colors,
withStaticVar: staticVar,
isSwiftUI: isSwiftUI,
visibility: visibility
),
Self.getFooter()
]
.joined(separator: "\n")
@ -75,10 +86,15 @@ struct ColorExtensionGenerator {
private static func getProperties(
for colors: [ParsedColor],
withStaticVar staticVar: Bool,
isSwiftUI: Bool
isSwiftUI: Bool,
visibility: ExtensionVisibility
) -> String {
colors.map {
$0.getColorProperty(isStatic: staticVar, isSwiftUI: isSwiftUI)
$0.getColorProperty(
isStatic: staticVar,
isSwiftUI: isSwiftUI,
visibility: visibility
)
}
.joined(separator: "\n\n")
}

View File

@ -6,14 +6,19 @@
//
import Foundation
import ToolCore
struct ParsedColor {
// MARK: - Properties
let name: String
let light: String
let dark: String
// Generate Contents.json content
// MARK: - Contents.json
/// Generate Contents.json content
func contentsJSON() -> String {
let lightARGB = light.colorComponent()
let darkARGB = dark.colorComponent()
@ -73,20 +78,24 @@ struct ParsedColor {
"""
}
// MARK: - UIKit
// MARK: - Property
func getColorProperty(isStatic: Bool, isSwiftUI: Bool) -> String {
func getColorProperty(
isStatic: Bool,
isSwiftUI: Bool,
visibility: ExtensionVisibility
) -> String {
if isSwiftUI {
return """
/// Color \(name) is \(light) (light) or \(dark) (dark)"
\(isStatic ? "static " : "")var \(name): Color {
\(visibility) \(isStatic ? "static " : "")var \(name): Color {
Color("\(name)")
}
"""
}
return """
/// Color \(name) is \(light) (light) or \(dark) (dark)"
\(isStatic ? "static " : "@objc ")var \(name): UIColor {
\(isStatic ? "" : "@objc ")\(visibility) \(isStatic ? "static " : "")var \(name): UIColor {
UIColor(named: "\(name)")!
}
"""

View File

@ -7,6 +7,7 @@
import ArgumentParser
import Foundation
import ToolCore
// swiftlint:disable no_grouping_extension
@ -18,20 +19,27 @@ struct FontsOptions: ParsableArguments {
@Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var inputFile: String
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var extensionOutputPath: String
@Option(help: "Tell if it will generate static properties or methods")
var staticMembers: Bool = false
@Option(
name: .customLong("visibility"),
help: "Visibility of extension and properties. Possibles values: public, private, package, internal. Default is internal",
completion: .list(["public", "private", "package", "internal"])
)
var extensionVisibility: ExtensionVisibility = .internal
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var extensionOutputPath: String
@Option(help: "Extension name. If not specified, it will generate an Font extension.")
var extensionName: String = Fonts.defaultExtensionName
@Option(help: "Extension name. If not specified, it will generate an UIFont extension.")
var extensionNameUIKit: String = Fonts.defaultExtensionNameUIKit
@Option(help: "Extension name. If not specified, no extension will be generated.")
var extensionNameUIKit: String?
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
var extensionSuffix: String = ""
var extensionSuffix: String?
@Option(name: .customLong("info-plist-paths"), help: "Info.plist paths (array). Will be used to update UIAppFonts content")
fileprivate var infoPlistPathsRaw: String = ""
@ -44,7 +52,7 @@ extension FontsOptions {
// MARK: - SwiftUI
var extensionFileName: String {
if extensionSuffix.isEmpty == false {
if let extensionSuffix {
return "\(extensionName)+\(extensionSuffix).swift"
}
return "\(extensionName).swift"
@ -56,15 +64,19 @@ extension FontsOptions {
// MARK: - UIKit
var extensionFileNameUIKit: String {
if extensionSuffix.isEmpty == false {
var extensionFileNameUIKit: String? {
guard let extensionNameUIKit else { return nil }
if let extensionSuffix {
return "\(extensionNameUIKit)+\(extensionSuffix).swift"
}
return "\(extensionNameUIKit).swift"
}
var extensionFilePathUIKit: String {
"\(extensionOutputPath)/\(extensionFileNameUIKit)"
var extensionFilePathUIKit: String? {
guard let extensionFileNameUIKit else { return nil }
return "\(extensionOutputPath)/\(extensionFileNameUIKit)"
}
// MARK: -

View File

@ -22,7 +22,6 @@ struct Fonts: ParsableCommand {
static let toolName = "Fonts"
static let defaultExtensionName = "Font"
static let defaultExtensionNameUIKit = "UIFont"
// MARK: - Command Options
@ -58,19 +57,30 @@ struct Fonts: ParsableCommand {
staticVar: options.staticMembers,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath,
isSwiftUI: true
isSwiftUI: true,
visibility: options.extensionVisibility
)
FontExtensionGenerator.writeExtensionFile(
fontsNames: fontsNames,
staticVar: options.staticMembers,
extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: false
)
if let extensionNameUIKit = options.extensionNameUIKit,
let extensionFilePathUIKit = options.extensionFilePathUIKit {
FontExtensionGenerator.writeExtensionFile(
fontsNames: fontsNames,
staticVar: options.staticMembers,
extensionName: extensionNameUIKit,
extensionFilePath: extensionFilePathUIKit,
isSwiftUI: false,
visibility: options.extensionVisibility
)
}
print("Info.plist has been updated with:")
print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames, infoPlistPaths: options.infoPlistPaths))")
if options.infoPlistPaths.isEmpty == false {
let plistUpdateFontsData = FontPlistGenerator.generatePlistUIAppsFontContent(
for: fontsNames,
infoPlistPaths: options.infoPlistPaths
)
print("Info.plist has been updated with:")
print(plistUpdateFontsData)
}
print("[\(Self.toolName)] Fonts generated")
}

View File

@ -14,6 +14,7 @@ enum FontsToolError: Error {
case inputFolderNotFound(String)
case fileNotExists(String)
case writeExtension(String, String)
case missingExtensionPath
var description: String {
switch self {
@ -31,6 +32,9 @@ enum FontsToolError: Error {
case let .writeExtension(filename, info):
return "error: [\(Fonts.toolName)] An error occured while writing extension in \(filename): \(info)"
case .missingExtensionPath:
return "error: [\(Fonts.toolName)] Extension need to be generated but no `extensionOutputPath` is provided"
}
}
}

View File

@ -10,8 +10,11 @@ import ToolCore
enum FontExtensionGenerator {
private static func getFontNameEnum(fontsNames: [FontName]) -> String {
var enumDefinition = " enum FontName: String {\n"
private static func getFontNameEnum(
fontsNames: [FontName],
visibility: ExtensionVisibility
) -> String {
var enumDefinition = " \(visibility) enum FontName: String {\n"
fontsNames.forEach {
enumDefinition += " case \($0.fontNameSanitize) = \"\($0.postscriptName)\"\n"
@ -26,14 +29,16 @@ enum FontExtensionGenerator {
staticVar: Bool,
extensionName: String,
extensionFilePath: String,
isSwiftUI: Bool
isSwiftUI: Bool,
visibility: ExtensionVisibility
) {
// Create extension content
let extensionContent = Self.getExtensionContent(
fontsNames: fontsNames,
staticVar: staticVar,
extensionName: extensionName,
isSwiftUI: isSwiftUI
isSwiftUI: isSwiftUI,
visibility: visibility
)
// Write content
@ -51,18 +56,33 @@ enum FontExtensionGenerator {
fontsNames: [FontName],
staticVar: Bool,
extensionName: String,
isSwiftUI: Bool
isSwiftUI: Bool,
visibility: ExtensionVisibility
) -> String {
[
Self.getHeader(extensionClassname: extensionName, isSwiftUI: isSwiftUI),
Self.getFontNameEnum(fontsNames: fontsNames),
Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar, isSwiftUI: isSwiftUI),
Self.getHeader(
extensionClassname: extensionName,
isSwiftUI: isSwiftUI
),
Self.getFontNameEnum(
fontsNames: fontsNames,
visibility: visibility
),
Self.getFontMethods(
fontsNames: fontsNames,
staticVar: staticVar,
isSwiftUI: isSwiftUI,
visibility: visibility
),
Self.getFooter()
]
.joined(separator: "\n")
}
private static func getHeader(extensionClassname: String, isSwiftUI: Bool) -> String {
private static func getHeader(
extensionClassname: String,
isSwiftUI: Bool
) -> String {
"""
// Generated by ResgenSwift.\(Fonts.toolName) \(ResgenSwiftVersion)
@ -72,13 +92,22 @@ enum FontExtensionGenerator {
"""
}
private static func getFontMethods(fontsNames: [FontName], staticVar: Bool, isSwiftUI: Bool) -> String {
private static func getFontMethods(
fontsNames: [FontName],
staticVar: Bool,
isSwiftUI: Bool,
visibility: ExtensionVisibility
) -> String {
let pragma = " // MARK: - Getter"
var propertiesOrMethods: [String] = fontsNames
.unique()
.map {
$0.getProperty(isStatic: staticVar, isSwiftUI: isSwiftUI)
$0.getProperty(
isStatic: staticVar,
isSwiftUI: isSwiftUI,
visibility: visibility
)
}
propertiesOrMethods.insert(pragma, at: 0)

View File

@ -6,6 +6,7 @@
//
import Foundation
import ToolCore
// swiftlint:disable no_grouping_extension
@ -22,33 +23,43 @@ extension FontName {
postscriptName.removeCharacters(from: "[]+-_")
}
func getProperty(isStatic: Bool, isSwiftUI: Bool) -> String {
if isSwiftUI {
if isStatic {
return """
static let \(fontNameSanitize): ((_ size: CGFloat) -> Font) = { size in
Font.custom(FontName.\(fontNameSanitize).rawValue, size: size)
}
"""
}
return """
func \(fontNameSanitize)(withSize size: CGFloat) -> Font {
func getProperty(
isStatic: Bool,
isSwiftUI: Bool,
visibility: ExtensionVisibility
) -> String {
switch (isSwiftUI, isStatic) {
case (true, true):
// SwiftUI, Static => let
"""
\(visibility) static let \(fontNameSanitize): ((_ size: CGFloat) -> Font) = { size in
Font.custom(FontName.\(fontNameSanitize).rawValue, size: size)
}
"""
}
// UIKit
if isStatic {
return """
static let \(fontNameSanitize): ((_ size: CGFloat) -> UIFont) = { size in
case (true, false):
// SwiftUI, Not Static => func
"""
\(visibility) func \(fontNameSanitize)(withSize size: CGFloat) -> Font {
Font.custom(FontName.\(fontNameSanitize).rawValue, size: size)
}
"""
case (false, true):
// UIKit, Static => let
"""
\(visibility) static let \(fontNameSanitize): ((_ size: CGFloat) -> UIFont) = { size in
UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
}
"""
}
return """
func \(fontNameSanitize)(withSize size: CGFloat) -> UIFont {
case (false, false):
// UIKit, Not Static => func
"""
\(visibility) func \(fontNameSanitize)(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
}
"""
"""
}
}
}

View File

@ -86,31 +86,21 @@ struct AnalyticsConfiguration: Codable, CustomDebugStringConvertible {
let inputFile: String
let target: String
let extensionOutputPath: String
let extensionName: String?
let extensionSuffix: String?
private let staticMembers: Bool?
var staticMembersOptions: Bool {
if let staticMembers {
return staticMembers
}
return false
}
let outputFile: String
let visibility: String?
let staticMembers: Bool?
internal init(
inputFile: String,
target: String,
extensionOutputPath: String,
extensionName: String?,
extensionSuffix: String?,
outputFile: String,
visibility: String?,
staticMembers: Bool?
) {
self.inputFile = inputFile
self.target = target
self.extensionOutputPath = extensionOutputPath
self.extensionName = extensionName
self.extensionSuffix = extensionSuffix
self.outputFile = outputFile
self.visibility = visibility
self.staticMembers = staticMembers
}
@ -119,9 +109,9 @@ struct AnalyticsConfiguration: Codable, CustomDebugStringConvertible {
Analytics configuration:
- Input file: \(inputFile)
- Target: \(target)
- Extension output path: \(extensionOutputPath)
- Extension name: \(extensionName ?? "-")
- Extension suffix: \(extensionSuffix ?? "-")
- Output file: \(outputFile)
- Visiblity: \(visibility ?? "default")
- Static members: \(staticMembers != nil ? "\(String(describing: staticMembers))" : "default")
"""
}
}
@ -131,27 +121,22 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
let inputFile: String
let style: String
let xcassetsPath: String
let extensionOutputPath: String
let extensionOutputPath: String?
let extensionName: String?
let extensionNameUIKit: String?
let extensionSuffix: String?
private let staticMembers: Bool?
var staticMembersOptions: Bool {
if let staticMembers {
return staticMembers
}
return false
}
let visibility: String?
let staticMembers: Bool?
internal init(
inputFile: String,
style: String,
xcassetsPath: String,
extensionOutputPath: String,
extensionOutputPath: String?,
extensionName: String?,
extensionNameUIKit: String?,
extensionSuffix: String?,
visibility: String?,
staticMembers: Bool?
) {
self.inputFile = inputFile
@ -161,6 +146,7 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
self.extensionName = extensionName
self.extensionNameUIKit = extensionNameUIKit
self.extensionSuffix = extensionSuffix
self.visibility = visibility
self.staticMembers = staticMembers
}
@ -170,10 +156,12 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
- Input file: \(inputFile)
- Style: \(style)
- Xcassets path: \(xcassetsPath)
- Extension output path: \(extensionOutputPath)
- Extension output path: \(extensionOutputPath ?? "-")
- Extension name: \(extensionName ?? "-")
- Extension name UIKit: \(extensionNameUIKit ?? "-")
- Extension suffix: \(extensionSuffix ?? "-")
- Visiblity: \(visibility ?? "default")
- Static members: \(staticMembers != nil ? "\(String(describing: staticMembers))" : "default")
"""
}
}
@ -181,27 +169,22 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
struct FontsConfiguration: Codable, CustomDebugStringConvertible {
let inputFile: String
let extensionOutputPath: String
let extensionOutputPath: String?
let extensionName: String?
let extensionNameUIKit: String?
let extensionSuffix: String?
let infoPlistPaths: String?
private let staticMembers: Bool?
var staticMembersOptions: Bool {
if let staticMembers {
return staticMembers
}
return false
}
let visibility: String?
let staticMembers: Bool?
internal init(
inputFile: String,
extensionOutputPath: String,
extensionOutputPath: String?,
extensionName: String?,
extensionNameUIKit: String?,
extensionSuffix: String?,
infoPlistPaths: String?,
visibility: String?,
staticMembers: Bool?
) {
self.inputFile = inputFile
@ -210,6 +193,7 @@ struct FontsConfiguration: Codable, CustomDebugStringConvertible {
self.extensionNameUIKit = extensionNameUIKit
self.extensionSuffix = extensionSuffix
self.infoPlistPaths = infoPlistPaths
self.visibility = visibility
self.staticMembers = staticMembers
}
@ -217,11 +201,13 @@ struct FontsConfiguration: Codable, CustomDebugStringConvertible {
"""
Fonts configuration:
- Input file: \(inputFile)
- Extension output path: \(extensionOutputPath)
- Extension output path: \(extensionOutputPath ?? "-")
- Extension name: \(extensionName ?? "-")
- Extension name UIKit: \(extensionNameUIKit ?? "-")
- Extension suffix: \(extensionSuffix ?? "-")
- InfoPlistPaths: \(infoPlistPaths ?? "-")
- Visiblity: \(visibility ?? "default")
- Static members: \(staticMembers != nil ? "\(String(describing: staticMembers))" : "default")
"""
}
}
@ -230,26 +216,21 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible {
let inputFile: String
let xcassetsPath: String
let extensionOutputPath: String
let extensionOutputPath: String?
let extensionName: String?
let extensionNameUIKit: String?
let extensionSuffix: String?
private let staticMembers: Bool?
var staticMembersOptions: Bool {
if let staticMembers {
return staticMembers
}
return false
}
let visibility: String?
let staticMembers: Bool?
internal init(
inputFile: String,
xcassetsPath: String,
extensionOutputPath: String,
extensionOutputPath: String?,
extensionName: String?,
extensionNameUIKit: String?,
extensionSuffix: String?,
visibility: String?,
staticMembers: Bool?
) {
self.inputFile = inputFile
@ -258,6 +239,7 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible {
self.extensionName = extensionName
self.extensionNameUIKit = extensionNameUIKit
self.extensionSuffix = extensionSuffix
self.visibility = visibility
self.staticMembers = staticMembers
}
@ -266,10 +248,12 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible {
Images configuration:
- Input file: \(inputFile)
- Xcassets path: \(xcassetsPath)
- Extension output path: \(extensionOutputPath)
- Extension output path: \(extensionOutputPath ?? "-")
- Extension name: \(extensionName ?? "-")
- Extension name UIKit: \(extensionNameUIKit ?? "-")
- Extension suffix: \(extensionSuffix ?? "-")
- Visiblity: \(visibility ?? "default")
- Static members: \(staticMembers != nil ? "\(String(describing: staticMembers))" : "default")
"""
}
}
@ -280,34 +264,22 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible {
let outputPath: String
let langs: String
let defaultLang: String
let extensionOutputPath: String
let extensionOutputPath: String?
let extensionName: String?
let extensionSuffix: String?
private let staticMembers: Bool?
private let xcStrings: Bool?
var staticMembersOptions: Bool {
if let staticMembers {
return staticMembers
}
return false
}
var xcStringsOptions: Bool {
if let xcStrings {
return xcStrings
}
return false
}
let visibility: String?
let staticMembers: Bool?
let xcStrings: Bool?
internal init(
inputFile: String,
outputPath: String,
langs: String,
defaultLang: String,
extensionOutputPath: String,
extensionOutputPath: String?,
extensionName: String?,
extensionSuffix: String?,
visibility: String?,
staticMembers: Bool?,
xcStrings: Bool?
) {
@ -318,6 +290,7 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible {
self.extensionOutputPath = extensionOutputPath
self.extensionName = extensionName
self.extensionSuffix = extensionSuffix
self.visibility = visibility
self.staticMembers = staticMembers
self.xcStrings = xcStrings
}
@ -329,9 +302,12 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible {
- Output path: \(outputPath)
- Langs: \(langs)
- Default lang: \(defaultLang)
- Extension output path: \(extensionOutputPath)
- Extension output path: \(extensionOutputPath ?? "-")
- Extension name: \(extensionName ?? "-")
- Extension suffix: \(extensionSuffix ?? "-")
- Visiblity: \(visibility ?? "default")
- Static members: \(staticMembers != nil ? "\(String(describing: staticMembers))" : "default")
- XC Strings: \(xcStrings != nil ? "\(String(describing: xcStrings))" : "default")
"""
}
}
@ -343,14 +319,8 @@ struct TagsConfiguration: Codable, CustomDebugStringConvertible {
let extensionOutputPath: String
let extensionName: String?
let extensionSuffix: String?
private let staticMembers: Bool?
var staticMembersOptions: Bool {
if let staticMembers {
return staticMembers
}
return false
}
let visibility: String?
let staticMembers: Bool?
internal init(
inputFile: String,
@ -358,6 +328,7 @@ struct TagsConfiguration: Codable, CustomDebugStringConvertible {
extensionOutputPath: String,
extensionName: String?,
extensionSuffix: String?,
visibility: String?,
staticMembers: Bool?
) {
self.inputFile = inputFile
@ -365,6 +336,7 @@ struct TagsConfiguration: Codable, CustomDebugStringConvertible {
self.extensionOutputPath = extensionOutputPath
self.extensionName = extensionName
self.extensionSuffix = extensionSuffix
self.visibility = visibility
self.staticMembers = staticMembers
}
@ -376,6 +348,8 @@ struct TagsConfiguration: Codable, CustomDebugStringConvertible {
- Extension output path: \(extensionOutputPath)
- Extension name: \(extensionName ?? "-")
- Extension suffix: \(extensionSuffix ?? "-")
- Visiblity: \(visibility ?? "default")
- Static members: \(staticMembers != nil ? "\(String(describing: staticMembers))" : "default")
"""
}
}

View File

@ -10,6 +10,11 @@ import Foundation
extension AnalyticsConfiguration: Runnable {
func run(projectDirectory: String, force: Bool) {
let args = getArguments(projectDirectory: projectDirectory, force: force)
Analytics.main(args)
}
func getArguments(projectDirectory: String, force: Bool) -> [String] {
var args = [String]()
if force {
@ -20,25 +25,23 @@ extension AnalyticsConfiguration: Runnable {
inputFile.prependIfRelativePath(projectDirectory),
"--target",
target,
"--extension-output-path",
extensionOutputPath.prependIfRelativePath(projectDirectory),
"--static-members",
"\(staticMembersOptions)"
"--output-file",
outputFile.prependIfRelativePath(projectDirectory)
]
if let extensionName {
args += [
"--extension-name",
extensionName
]
}
if let extensionSuffix {
args += [
"--extension-suffix",
extensionSuffix
]
// Add optional parameters
[
("--visibility", visibility),
("--static-members", staticMembers?.description)
].forEach { argumentName, argumentValue in
if let argumentValue {
args += [
argumentName,
argumentValue
]
}
}
Analytics.main(args)
return args
}
}

View File

@ -26,30 +26,24 @@ extension ColorsConfiguration: Runnable {
"--style",
style,
"--xcassets-path",
xcassetsPath.prependIfRelativePath(projectDirectory),
"--extension-output-path",
extensionOutputPath.prependIfRelativePath(projectDirectory),
"--static-members",
"\(staticMembersOptions)"
xcassetsPath.prependIfRelativePath(projectDirectory)
]
if let extensionName {
args += [
"--extension-name",
extensionName
]
}
if let extensionNameUIKit {
args += [
"--extension-name-ui-kit",
extensionNameUIKit
]
}
if let extensionSuffix {
args += [
"--extension-suffix",
extensionSuffix
]
// Add optional parameters
[
("--extension-output-path", extensionOutputPath?.prependIfRelativePath(projectDirectory)),
("--extension-name", extensionName),
("--extension-name-ui-kit", extensionNameUIKit),
("--extension-suffix", extensionSuffix),
("--visibility", visibility),
("--static-members", staticMembers?.description)
].forEach { argumentName, argumentValue in
if let argumentValue {
args += [
argumentName,
argumentValue
]
}
}
return args

View File

@ -22,33 +22,27 @@ extension FontsConfiguration: Runnable {
}
args += [
inputFile.prependIfRelativePath(projectDirectory),
"--extension-output-path",
extensionOutputPath.prependIfRelativePath(projectDirectory),
"--static-members",
"\(staticMembersOptions)"
inputFile.prependIfRelativePath(projectDirectory)
]
if let extensionName {
args += [
"--extension-name",
extensionName
]
}
if let extensionNameUIKit {
args += [
"--extension-name-ui-kit",
extensionNameUIKit
]
}
if let extensionSuffix {
args += [
"--extension-suffix",
extensionSuffix
]
// Add optional parameters
[
("--extension-output-path", extensionOutputPath?.prependIfRelativePath(projectDirectory)),
("--extension-name", extensionName),
("--extension-name-ui-kit", extensionNameUIKit),
("--extension-suffix", extensionSuffix),
("--visibility", visibility),
("--static-members", staticMembers?.description)
].forEach { argumentName, argumentValue in
if let argumentValue {
args += [
argumentName,
argumentValue
]
}
}
// Add infoPlist paths
if let infoPlistPaths {
let adjustedPlistPaths = infoPlistPaths
.split(separator: " ")

View File

@ -24,32 +24,24 @@ extension ImagesConfiguration: Runnable {
args += [
inputFile.prependIfRelativePath(projectDirectory),
"--xcassets-path",
xcassetsPath.prependIfRelativePath(projectDirectory),
"--extension-output-path",
extensionOutputPath.prependIfRelativePath(projectDirectory),
"--static-members",
"\(staticMembersOptions)"
xcassetsPath.prependIfRelativePath(projectDirectory)
]
if let extensionName {
args += [
"--extension-name",
extensionName
]
}
if let extensionNameUIKit {
args += [
"--extension-name-ui-kit",
extensionNameUIKit
]
}
if let extensionSuffix {
args += [
"--extension-suffix",
extensionSuffix
]
// Add optional parameters
[
("--extension-output-path", extensionOutputPath?.prependIfRelativePath(projectDirectory)),
("--extension-name", extensionName),
("--extension-name-ui-kit", extensionNameUIKit),
("--extension-suffix", extensionSuffix),
("--visibility", visibility),
("--static-members", staticMembers?.description)
].forEach { argumentName, argumentValue in
if let argumentValue {
args += [
argumentName,
argumentValue
]
}
}
return args

View File

@ -10,6 +10,11 @@ import Foundation
extension StringsConfiguration: Runnable {
func run(projectDirectory: String, force: Bool) {
let args = getArguments(projectDirectory: projectDirectory, force: force)
Stringium.main(args)
}
func getArguments(projectDirectory: String, force: Bool) -> [String] {
var args = [String]()
if force {
@ -23,29 +28,26 @@ extension StringsConfiguration: Runnable {
"--langs",
langs,
"--default-lang",
defaultLang,
"--extension-output-path",
extensionOutputPath.prependIfRelativePath(projectDirectory),
"--static-members",
"\(staticMembersOptions)",
"--xc-strings",
"\(xcStringsOptions)"
defaultLang
]
if let extensionName {
args += [
"--extension-name",
extensionName
]
// Add optional parameters
[
("--extension-output-path", extensionOutputPath?.prependIfRelativePath(projectDirectory)),
("--extension-name", extensionName),
("--extension-suffix", extensionSuffix),
("--visibility", visibility),
("--xc-strings", staticMembers?.description),
("--static-members", xcStrings?.description)
].forEach { argumentName, argumentValue in
if let argumentValue {
args += [
argumentName,
argumentValue
]
}
}
if let extensionSuffix {
args += [
"--extension-suffix",
extensionSuffix
]
}
Stringium.main(args)
return args
}
}

View File

@ -10,6 +10,11 @@ import Foundation
extension TagsConfiguration: Runnable {
func run(projectDirectory: String, force: Bool) {
let args = getArguments(projectDirectory: projectDirectory, force: force)
Tags.main(args)
}
func getArguments(projectDirectory: String, force: Bool) -> [String] {
var args = [String]()
if force {
@ -21,24 +26,24 @@ extension TagsConfiguration: Runnable {
"--lang",
lang,
"--extension-output-path",
extensionOutputPath.prependIfRelativePath(projectDirectory),
"--static-members",
"\(staticMembersOptions)"
extensionOutputPath.prependIfRelativePath(projectDirectory)
]
if let extensionName {
args += [
"--extension-name",
extensionName
]
}
if let extensionSuffix {
args += [
"--extension-suffix",
extensionSuffix
]
// Add optional parameters
[
("--extension-name", extensionName),
("--extension-suffix", extensionSuffix),
("--visibility", visibility),
("--static-members", staticMembers?.description)
].forEach { argumentName, argumentValue in
if let argumentValue {
args += [
argumentName,
argumentValue
]
}
}
Tags.main(args)
return args
}
}

View File

@ -18,7 +18,8 @@ enum ImageExtensionGenerator {
inputFilename: String,
extensionName: String,
extensionFilePath: String,
isSwiftUI: Bool
isSwiftUI: Bool,
visibility: ExtensionVisibility
) {
// Create extension conten1t
let extensionContent = Self.getExtensionContent(
@ -26,7 +27,8 @@ enum ImageExtensionGenerator {
staticVar: staticVar,
extensionName: extensionName,
inputFilename: inputFilename,
isSwiftUI: isSwiftUI
isSwiftUI: isSwiftUI,
visibility: visibility
)
// Write content
@ -45,11 +47,21 @@ enum ImageExtensionGenerator {
staticVar: Bool,
extensionName: String,
inputFilename: String,
isSwiftUI: Bool
isSwiftUI: Bool,
visibility: ExtensionVisibility
) -> String {
[
Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName, isSwiftUI: isSwiftUI),
Self.getProperties(images: images, staticVar: staticVar, isSwiftUI: isSwiftUI),
Self.getHeader(
inputFilename: inputFilename,
extensionClassname: extensionName,
isSwiftUI: isSwiftUI
),
Self.getProperties(
images: images,
staticVar: staticVar,
isSwiftUI: isSwiftUI,
visibility: visibility
),
Self.getFooter()
]
.joined(separator: "\n")
@ -73,10 +85,13 @@ enum ImageExtensionGenerator {
private static func getProperties(
images: [ParsedImage],
staticVar: Bool,
isSwiftUI: Bool
isSwiftUI: Bool,
visibility: ExtensionVisibility
) -> String {
images
.map { "\n\($0.getImageProperty(isStatic: staticVar, isSwiftUI: isSwiftUI))" }
.map {
"\n\($0.getImageProperty(isStatic: staticVar, isSwiftUI: isSwiftUI, visibility: visibility))"
}
.joined(separator: "\n")
}

View File

@ -21,8 +21,6 @@ struct Images: ParsableCommand {
// MARK: - Static
static let toolName = "Images"
static let defaultExtensionName = "Image"
static let defaultExtensionNameUIKit = "UIImage"
// MARK: - Command Options
@ -55,23 +53,31 @@ struct Images: ParsableCommand {
)
// Generate extension
ImageExtensionGenerator.generateExtensionFile(
images: imagesToGenerate,
staticVar: options.staticMembers,
inputFilename: options.inputFilenameWithoutExt,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath,
isSwiftUI: true
)
if let extensionName = options.extensionName,
let extensionFilePath = options.extensionFilePath {
ImageExtensionGenerator.generateExtensionFile(
images: imagesToGenerate,
staticVar: options.staticMembers,
inputFilename: options.inputFilenameWithoutExt,
extensionName: extensionName,
extensionFilePath: extensionFilePath,
isSwiftUI: true,
visibility: options.extensionVisibility
)
}
ImageExtensionGenerator.generateExtensionFile(
images: imagesToGenerate,
staticVar: options.staticMembers,
inputFilename: options.inputFilenameWithoutExt,
extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: false
)
if let extensionNameUIKit = options.extensionNameUIKit,
let extensionFilePathUIKit = options.extensionFilePathUIKit {
ImageExtensionGenerator.generateExtensionFile(
images: imagesToGenerate,
staticVar: options.staticMembers,
inputFilename: options.inputFilenameWithoutExt,
extensionName: extensionNameUIKit,
extensionFilePath: extensionFilePathUIKit,
isSwiftUI: false,
visibility: options.extensionVisibility
)
}
print("[\(Self.toolName)] Images generated")
}
@ -96,17 +102,39 @@ struct Images: ParsableCommand {
_ = Self.getSvgConverterPath()
// Extension for UIKit and SwiftUI should have different name
guard options.extensionName != options.extensionNameUIKit else {
let error = ImagesError.extensionNamesCollision(options.extensionName)
print(error.description)
Self.exit(withError: error)
if let extensionName = options.extensionName,
let extensionNameUIKit = options.extensionNameUIKit {
guard extensionName != extensionNameUIKit else {
let error = ImagesError.extensionNamesCollision(extensionName)
print(error.description)
Self.exit(withError: error)
}
}
// If an extension need to be generated, ensure extensionOutputPath is defined
if options.extensionName != nil ||
options.extensionNameUIKit != nil {
guard let extensionOutputPath = options.extensionOutputPath,
extensionOutputPath.isEmpty == false else {
let error = ImagesError.missingExtensionPath
print(error.description)
Self.exit(withError: error)
}
}
// Check if needed to regenerate
let fileToCompareToInput: String = {
// If there is no extension file to compare
// Then check the xcassets file instead
if let extensionFilePath = options.extensionFilePath {
return extensionFilePath
}
return options.xcassetsPath
}()
guard GeneratorChecker.shouldGenerate(
force: options.forceExecution,
inputFilePath: options.inputFile,
extensionFilePath: options.extensionFilePath
extensionFilePath: fileToCompareToInput
) else {
print("[\(Self.toolName)] Images are already up to date :) ")
return false

View File

@ -18,12 +18,13 @@ enum ImagesError: Error {
case magickConvertNotFound
case writeFile(String, String)
case createAssetFolder(String)
case missingExtensionPath
case unknown(String)
var description: String {
switch self {
case .extensionNamesCollision(let extensionName):
return "error: [\(Fonts.toolName)] Error on extension names, extension name and SwiftUI extension name should be different (\(extensionName) is used on both)"
return "error: [\(Images.toolName)] Error on extension names, extension name and SwiftUI extension name should be different (\(extensionName) is used on both)"
case .inputFolderNotFound(let inputFolder):
return "error: [\(Images.toolName)] Input folder not found: \(inputFolder)"
@ -47,7 +48,10 @@ enum ImagesError: Error {
return "error: [\(Images.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
case .createAssetFolder(let folder):
return "error: [\(Colors.toolName)] An error occured while creating folder `\(folder)`"
return "error: [\(Images.toolName)] An error occured while creating folder `\(folder)`"
case .missingExtensionPath:
return "error: [\(Images.toolName)] Extension need to be generated but no `extensionOutputPath` is provided"
case .unknown(let errorDescription):
return "error: [\(Images.toolName)] Unknown error: \(errorDescription)"

View File

@ -7,34 +7,61 @@
import ArgumentParser
import Foundation
import ToolCore
// swiftlint:disable no_grouping_extension
struct ImagesOptions: ParsableArguments {
@Flag(name: .customShort("f"), help: "Should force script execution")
@Flag(
name: .customShort("f"),
help: "Should force script execution"
)
var forceExecution = false
@Flag(name: .customShort("F"), help: "Regenerate all images")
@Flag(
name: .customShort("F"),
help: "Regenerate all images"
)
var forceExecutionAndGeneration = false
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@Argument(
help: "Input files where strings ared defined.",
completion: .file(),
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var inputFile: String
@Option(help: "Xcassets path where to generate images.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@Option(
help: "Xcassets path where to generate images.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var xcassetsPath: String
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var extensionOutputPath: String
@Option(help: "Tell if it will generate static properties or not")
@Option(
help: "Tell if it will generate static properties or not",
completion: .list(["true", "false"])
)
var staticMembers: Bool = false
@Option(help: "Extension name. If not specified, it will generate an Image extension.")
var extensionName: String = Images.defaultExtensionName
@Option(
name: .customLong("visibility"),
help: "Visibility of extension and properties. Possibles values: public, private, package, internal. Default is internal",
completion: .list(["public", "private", "package", "internal"])
)
var extensionVisibility: ExtensionVisibility = .internal
@Option(help: "Extension name. If not specified, it will generate an UIImage extension.")
var extensionNameUIKit: String = Images.defaultExtensionNameUIKit
@Option(
help: "Path where to generate the extension.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var extensionOutputPath: String?
@Option(help: "SwiftUI extension name. If not specified, no extension will be generated.")
var extensionName: String?
@Option(help: "UIKit extension name. If not specified, no extension will be generated.")
var extensionNameUIKit: String?
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift")
var extensionSuffix: String?
@ -46,28 +73,36 @@ extension ImagesOptions {
// MARK: - SwiftUI
var extensionFileName: String {
var extensionFileName: String? {
guard let extensionName else { return nil }
if let extensionSuffix {
return "\(extensionName)+\(extensionSuffix).swift"
}
return "\(extensionName).swift"
}
var extensionFilePath: String {
"\(extensionOutputPath)/\(extensionFileName)"
var extensionFilePath: String? {
guard let extensionOutputPath, let extensionFileName else { return nil }
return "\(extensionOutputPath)/\(extensionFileName)"
}
// MARK: - UIKit
var extensionFileNameUIKit: String {
var extensionFileNameUIKit: String? {
guard let extensionNameUIKit else { return nil }
if let extensionSuffix {
return "\(extensionNameUIKit)+\(extensionSuffix).swift"
}
return "\(extensionNameUIKit).swift"
}
var extensionFilePathUIKit: String {
"\(extensionOutputPath)/\(extensionFileNameUIKit)"
var extensionFilePathUIKit: String? {
guard let extensionOutputPath, let extensionFileNameUIKit else { return nil }
return "\(extensionOutputPath)/\(extensionFileNameUIKit)"
}
// MARK: -

View File

@ -6,6 +6,7 @@
//
import Foundation
import ToolCore
enum ImageExtension: String {
@ -129,16 +130,16 @@ struct ParsedImage {
// MARK: - Extension property
func getImageProperty(isStatic: Bool, isSwiftUI: Bool) -> String {
func getImageProperty(isStatic: Bool, isSwiftUI: Bool, visibility: ExtensionVisibility) -> String {
if isSwiftUI {
return """
\(isStatic ? "static " : "")var \(name): Image {
\(visibility) \(isStatic ? "static " : "")var \(name): Image {
Image("\(name)")
}
"""
}
return """
\(isStatic ? "static " : "")var \(name): UIImage {
\(visibility) \(isStatic ? "static " : "")var \(name): UIImage {
UIImage(named: "\(name)")!
}
"""

View File

@ -19,8 +19,7 @@ enum StringsFileGenerator {
langs: [String],
defaultLang: String,
tags: [String],
outputPath: String,
inputFilenameWithoutExt: String
lprojPathFormat: String
) {
var stringsFilesContent = [String: String]()
@ -37,7 +36,7 @@ enum StringsFileGenerator {
langs.forEach { lang in
guard let fileContent = stringsFilesContent[lang] else { return }
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
let stringsFilePath = String(format: lprojPathFormat, lang)
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
do {
try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8)
@ -54,8 +53,7 @@ enum StringsFileGenerator {
langs: [String],
defaultLang: String,
tags: [String],
outputPath: String,
inputFilenameWithoutExt: String
xcStringsFilePath: String
) {
let fileContent: String = Self.generateXcStringsFileContent(
@ -65,12 +63,11 @@ enum StringsFileGenerator {
sections: sections
)
let stringsFilePath = "\(outputPath)/\(inputFilenameWithoutExt).xcstrings"
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
let stringsFilePathURL = URL(fileURLWithPath: xcStringsFilePath)
do {
try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8)
} catch {
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
let error = StringiumError.writeFile(error.localizedDescription, xcStringsFilePath)
print(error.description)
Stringium.exit(withError: error)
}
@ -254,7 +251,8 @@ enum StringsFileGenerator {
inputFilename: String,
extensionName: String,
extensionFilePath: String,
extensionSuffix: String
extensionSuffix: String,
visibility: ExtensionVisibility
) {
// Get extension content
let extensionFileContent = Self.getExtensionContent(
@ -264,7 +262,8 @@ enum StringsFileGenerator {
staticVar: staticVar,
inputFilename: inputFilename,
extensionName: extensionName,
extensionSuffix: extensionSuffix
extensionSuffix: extensionSuffix,
visibility: visibility
)
// Write content
@ -287,7 +286,8 @@ enum StringsFileGenerator {
staticVar: Bool,
inputFilename: String,
extensionName: String,
extensionSuffix: String
extensionSuffix: String,
visibility: ExtensionVisibility
) -> String {
[
Self.getHeader(
@ -298,13 +298,15 @@ enum StringsFileGenerator {
sections: sections,
tags: tags,
extensionClassname: extensionName,
extensionSuffix: extensionSuffix
extensionSuffix: extensionSuffix,
visibility: visibility
),
Self.getProperties(
sections: sections,
defaultLang: lang,
tags: tags,
staticVar: staticVar
staticVar: staticVar,
visibility: visibility
),
Self.getFooter()
]
@ -329,9 +331,10 @@ enum StringsFileGenerator {
sections: [Section],
tags: [String],
extensionClassname: String,
extensionSuffix: String
extensionSuffix: String,
visibility: ExtensionVisibility
) -> String {
var enumDefinition = "\n enum Key\(extensionSuffix.uppercasedFirst()): String {\n"
var enumDefinition = "\n \(visibility) enum Key\(extensionSuffix.uppercasedFirst()): String {\n"
// Enum
sections.forEach { section in
@ -350,7 +353,7 @@ enum StringsFileGenerator {
// KeyPath accessors
enumDefinition += "\n"
enumDefinition += " var keyPath: KeyPath<\(extensionClassname), String> {\n"
enumDefinition += " \(visibility) var keyPath: KeyPath<\(extensionClassname), String> {\n"
enumDefinition += " switch self {\n"
sections.forEach { section in
// Check that at least one string will be generated
@ -372,7 +375,13 @@ enum StringsFileGenerator {
return enumDefinition
}
private static func getProperties(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool) -> String {
private static func getProperties(
sections: [Section],
defaultLang lang: String,
tags: [String],
staticVar: Bool,
visibility: ExtensionVisibility
) -> String {
sections.compactMap { section in
// Check that at least one string will be generated
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
@ -385,10 +394,21 @@ enum StringsFileGenerator {
return nil // Go to next definition
}
if staticVar {
return "\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
}
return "\n\(definition.getNSLocalizedStringProperty(forLang: lang))"
let property: String = {
if staticVar {
definition.getNSLocalizedStringStaticProperty(
forLang: lang,
visibility: visibility
)
} else {
definition.getNSLocalizedStringProperty(
forLang: lang,
visibility: visibility
)
}
}()
return "\n\(property)"
}
.joined(separator: "\n")
return res

View File

@ -17,7 +17,8 @@ enum TagsGenerator {
tags: [String],
staticVar: Bool,
extensionName: String,
extensionFilePath: String
extensionFilePath: String,
visibility: ExtensionVisibility
) {
// Get extension content
let extensionFileContent = Self.getExtensionContent(
@ -25,7 +26,8 @@ enum TagsGenerator {
lang: lang,
tags: tags,
staticVar: staticVar,
extensionName: extensionName
extensionName: extensionName,
visibility: visibility
)
// Write content
@ -46,7 +48,8 @@ enum TagsGenerator {
lang: String,
tags: [String],
staticVar: Bool,
extensionName: String
extensionName: String,
visibility: ExtensionVisibility
) -> String {
[
Self.getHeader(
@ -57,7 +60,8 @@ enum TagsGenerator {
sections: sections,
lang: lang,
tags: tags,
staticVar: staticVar
staticVar: staticVar,
visibility: visibility
),
Self.getFooter()
]
@ -80,7 +84,8 @@ enum TagsGenerator {
sections: [Section],
lang: String,
tags: [String],
staticVar: Bool
staticVar: Bool,
visibility: ExtensionVisibility
) -> String {
sections
.compactMap { section in
@ -96,9 +101,9 @@ enum TagsGenerator {
}
if staticVar {
res += "\n\n\(definition.getStaticProperty(forLang: lang))"
res += "\n\n\(definition.getStaticProperty(forLang: lang, visibility: visibility))"
} else {
res += "\n\n\(definition.getProperty(forLang: lang))"
res += "\n\n\(definition.getProperty(forLang: lang, visibility: visibility))"
}
}
return res

View File

@ -6,6 +6,7 @@
//
import Foundation
import ToolCore
// swiftlint:disable force_unwrapping
@ -99,14 +100,20 @@ class Definition {
return (inputParameters: inputParameters, translationArguments: translationArguments)
}
private func getBaseProperty(lang: String, translation: String, isStatic: Bool, comment: String?) -> String {
private func getBaseProperty(
lang: String,
translation: String,
isStatic: Bool,
comment: String?,
visibility: ExtensionVisibility
) -> String {
"""
/// Translation in \(lang) :
/// \(translation)
///
/// Comment :
/// \(comment?.isEmpty == false ? comment! : "No comment")
\(isStatic ? "static " : "")var \(name): String {
\(visibility) \(isStatic ? "static " : "")var \(name): String {
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "\(comment ?? "")")
}
"""
@ -118,7 +125,8 @@ class Definition {
isStatic: Bool,
inputParameters: [String],
translationArguments: [String],
comment: String?
comment: String?,
visibility: ExtensionVisibility
) -> String {
"""
@ -127,13 +135,13 @@ class Definition {
///
/// Comment :
/// \(comment?.isEmpty == false ? comment! : "No comment")
\(isStatic ? "static " : "")func \(name)(\(inputParameters.joined(separator: ", "))) -> String {
\(visibility) \(isStatic ? "static " : "")func \(name)(\(inputParameters.joined(separator: ", "))) -> String {
String(format: \(isStatic ? "Self" : "self").\(name), \(translationArguments.joined(separator: ", ")))
}
"""
}
func getNSLocalizedStringProperty(forLang lang: String) -> String {
func getNSLocalizedStringProperty(forLang lang: String, visibility: ExtensionVisibility) -> String {
guard let translation = translations[lang] else {
let error = StringiumError.langNotDefined(lang, name, reference != nil)
print(error.description)
@ -145,7 +153,8 @@ class Definition {
lang: lang,
translation: translation,
isStatic: false,
comment: self.comment
comment: self.comment,
visibility: visibility
)
// Generate method
@ -157,14 +166,15 @@ class Definition {
isStatic: false,
inputParameters: parameters.inputParameters,
translationArguments: parameters.translationArguments,
comment: self.comment
comment: self.comment,
visibility: visibility
)
}
return property + method
}
func getNSLocalizedStringStaticProperty(forLang lang: String) -> String {
func getNSLocalizedStringStaticProperty(forLang lang: String, visibility: ExtensionVisibility) -> String {
guard let translation = translations[lang] else {
let error = StringiumError.langNotDefined(lang, name, reference != nil)
print(error.description)
@ -176,7 +186,8 @@ class Definition {
lang: lang,
translation: translation,
isStatic: true,
comment: self.comment
comment: self.comment,
visibility: visibility
)
// Generate method
@ -188,7 +199,8 @@ class Definition {
isStatic: true,
inputParameters: parameters.inputParameters,
translationArguments: parameters.translationArguments,
comment: self.comment
comment: self.comment,
visibility: visibility
)
}
@ -197,7 +209,7 @@ class Definition {
// MARK: - Raw strings
func getProperty(forLang lang: String) -> String {
func getProperty(forLang lang: String, visibility: ExtensionVisibility) -> String {
guard let translation = translations[lang] else {
let error = StringiumError.langNotDefined(lang, name, reference != nil)
print(error.description)
@ -210,14 +222,13 @@ class Definition {
///
/// Comment :
/// \(comment?.isEmpty == false ? comment! : "No comment")
var \(name): String {
\(visibility) var \(name): String {
"\(translation)"
}
"""
}
func getStaticProperty(forLang lang: String) -> String {
func getStaticProperty(forLang lang: String, visibility: ExtensionVisibility) -> String {
guard let translation = translations[lang] else {
let error = StringiumError.langNotDefined(lang, name, reference != nil)
print(error.description)
@ -230,7 +241,7 @@ class Definition {
///
/// Comment :
/// \(comment?.isEmpty == false ? comment! : "No comment")
static var \(name): String {
\(visibility) static var \(name): String {
"\(translation)"
}
"""

View File

@ -21,7 +21,6 @@ struct Stringium: ParsableCommand {
// MARK: - Static
static let toolName = "Stringium"
static let defaultExtensionName = "String"
static let noTranslationTag: String = "notranslation"
// MARK: - Command options
@ -49,8 +48,7 @@ struct Stringium: ParsableCommand {
langs: options.langs,
defaultLang: options.defaultLang,
tags: options.tags,
outputPath: options.stringsFileOutputPath,
inputFilenameWithoutExt: options.inputFilenameWithoutExt
lprojPathFormat: options.lprojPathFormat
)
} else {
StringsFileGenerator.writeXcStringsFiles(
@ -58,22 +56,26 @@ struct Stringium: ParsableCommand {
langs: options.langs,
defaultLang: options.defaultLang,
tags: options.tags,
outputPath: options.stringsFileOutputPath,
inputFilenameWithoutExt: options.inputFilenameWithoutExt
xcStringsFilePath: options.xcStringsFilePath
)
}
// Generate extension
StringsFileGenerator.writeExtensionFiles(
sections: sections,
defaultLang: options.defaultLang,
tags: options.tags,
staticVar: options.staticMembers,
inputFilename: options.inputFilenameWithoutExt,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath,
extensionSuffix: options.extensionSuffix
)
if let extensionName = options.extensionName,
let extensionFilePath = options.extensionFilePath {
print("Will generate extensions")
StringsFileGenerator.writeExtensionFiles(
sections: sections,
defaultLang: options.defaultLang,
tags: options.tags,
staticVar: options.staticMembers,
inputFilename: options.inputFilenameWithoutExt,
extensionName: extensionName,
extensionFilePath: extensionFilePath,
extensionSuffix: options.extensionSuffix ?? "",
visibility: options.extensionVisibility
)
}
print("[\(Self.toolName)] Strings generated")
}
@ -104,10 +106,18 @@ struct Stringium: ParsableCommand {
}
// Check if needed to regenerate
let fileToCompareToInput: String = {
// If there is no extension file to compare
// Then check the xcassets file instead
if options.xcStrings {
return options.xcStringsFilePath
}
return String(format: options.lprojPathFormat, options.defaultLang)
}()
guard GeneratorChecker.shouldGenerate(
force: options.forceGeneration,
inputFilePath: options.inputFile,
extensionFilePath: options.extensionFilePath
extensionFilePath: fileToCompareToInput
) else {
print("[\(Self.toolName)] Strings are already up to date :) ")
return false

View File

@ -7,6 +7,7 @@
import ArgumentParser
import Foundation
import ToolCore
// swiftlint:disable no_grouping_extension
@ -15,35 +16,58 @@ struct StringiumOptions: ParsableArguments {
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
var forceGeneration = false
@Argument(help: "Input files where strings are defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@Argument(
help: "Input files where strings are defined.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var inputFile: String
@Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@Option(
name: .customLong("output-path"),
help: "Path where to find the .xcStrings file or the lproj folders.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
fileprivate var outputPathRaw: String
@Option(name: .customLong("langs"), help: "Langs to generate.")
@Option(
name: .customLong("langs"),
help: "Langs to generate."
)
fileprivate var langsRaw: String
@Option(help: "Default langs.")
var defaultLang: String
@Option(name: .customLong("tags"), help: "Tags to generate.")
@Option(
name: .customLong("tags"),
help: "Tags to generate."
)
fileprivate var tagsRaw: String = "ios iosonly iosOnly notranslation"
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var extensionOutputPath: String
@Option(help: "Tell if it will generate static properties or not")
@Option(help: "Generate static properties. False by default")
var staticMembers: Bool = false
@Option(help: "Tell if it will generate xcStrings file or not")
var xcStrings: Bool = false
@Option(help: "Tell if it will generate xcStrings file or lproj file. True by default")
var xcStrings: Bool = true
@Option(help: "Extension name. If not specified, it will generate an String extension.")
var extensionName: String = Stringium.defaultExtensionName
@Option(
name: .customLong("visibility"),
help: "Visibility of extension and properties. Possibles values: public, private, package, internal. Default is internal",
completion: .list(["public", "private", "package", "internal"])
)
var extensionVisibility: ExtensionVisibility = .internal
@Option(
help: "Path where to generate the extension.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var extensionOutputPath: String?
@Option(help: "Extension name. If not specified, no extension will be generated.")
var extensionName: String?
@Option(help: "Extension suffix: {extensionName}+{extensionSuffix}.swift")
var extensionSuffix: String
var extensionSuffix: String?
}
// MARK: - Private var getter
@ -75,12 +99,21 @@ extension StringiumOptions {
extension StringiumOptions {
var extensionFileName: String {
"\(extensionName)+\(extensionSuffix).swift"
private var extensionFileName: String? {
if let extensionName {
if let extensionSuffix {
return "\(extensionName)+\(extensionSuffix).swift"
}
return "\(extensionName).swift"
}
return nil
}
var extensionFilePath: String {
"\(extensionOutputPath)/\(extensionFileName)"
var extensionFilePath: String? {
if let extensionOutputPath, let extensionFileName {
return "\(extensionOutputPath)/\(extensionFileName)"
}
return nil
}
var inputFilenameWithoutExt: String {
@ -88,4 +121,12 @@ extension StringiumOptions {
.deletingPathExtension()
.lastPathComponent
}
var xcStringsFilePath: String {
"\(stringsFileOutputPath)/\(inputFilenameWithoutExt).xcstrings"
}
var lprojPathFormat: String {
"\(stringsFileOutputPath)/%@.lproj/\(inputFilenameWithoutExt).strings"
}
}

View File

@ -49,7 +49,8 @@ struct Tags: ParsableCommand {
tags: ["ios", "iosonly", Self.noTranslationTag],
staticVar: options.staticMembers,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath
extensionFilePath: options.extensionFilePath,
visibility: options.extensionVisibility
)
print("[\(Self.toolName)] Tags generated")

View File

@ -7,6 +7,7 @@
import ArgumentParser
import Foundation
import ToolCore
// swiftlint:disable no_grouping_extension
@ -21,7 +22,17 @@ struct TagsOptions: ParsableArguments {
@Option(help: "Lang to generate. (\"ium\" by default)")
var lang: String = "ium"
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@Option(
name: .customLong("visibility"),
help: "Visibility of extension and properties. Possibles values: public, private, package, internal. Default is internal",
completion: .list(["public", "private", "package", "internal"])
)
var extensionVisibility: ExtensionVisibility = .internal
@Option(
help: "Path where to generate the extension.",
transform: { $0.replaceTiltWithHomeDirectoryPath() }
)
var extensionOutputPath: String
@Option(help: "Tell if it will generate static properties or not")

View File

@ -0,0 +1,34 @@
//
// ExtensionVisibility.swift
// ResgenSwift
//
// Created by Thibaut Schmitt on 17/07/2025.
//
import ArgumentParser
package enum ExtensionVisibility: String, CustomStringConvertible, ExpressibleByArgument {
case `public`
case `private`
case `internal`
case `package`
// MARK: - CustomStringConvertible
package var description: String {
switch self {
case .public:
"public"
case .private:
"private"
case .internal:
"internal"
case .package:
"package"
}
}
}

View File

@ -9,4 +9,4 @@ import Foundation
// swiftlint:disable prefixed_toplevel_constant identifier_name
public let ResgenSwiftVersion = "2.1.0"
public let ResgenSwiftVersion = "2.2.0"

View File

@ -55,14 +55,15 @@ final class AnalyticsDefinitionTests: XCTestCase {
definition.path = "ecran_un/"
// When
let propertyScreen = definition.getProperty()
let propertyScreen = definition.getProperty(visibility: .internal)
// Expect
let expectScreen = """
func logScreenDefinitionName() {
internal func logScreenDefinitionName() {
logScreen(
name: "Ecran un",
path: "ecran_un/"
path: "ecran_un/",
params: nil
)
}
"""
@ -75,16 +76,16 @@ final class AnalyticsDefinitionTests: XCTestCase {
let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .event)
// When
let propertyEvent = definition.getProperty()
let propertyEvent = definition.getProperty(visibility: .public)
// Expect
let expectEvent = """
func logEventDefinitionName() {
public func logEventDefinitionName() {
logEvent(
name: "Ecran un",
action: "",
category: "",
params: [:]
params: nil
)
}
"""
@ -98,14 +99,15 @@ final class AnalyticsDefinitionTests: XCTestCase {
definition.path = "ecran_un/"
// When
let propertyScreen = definition.getStaticProperty()
let propertyScreen = definition.getStaticProperty(visibility: .private)
// Expect
let expectScreen = """
static func logScreenDefinitionName() {
logScreen(
private static func logScreenDefinitionName() {
AnalyticsManager.shared.logScreen(
name: "Ecran un",
path: "ecran_un/"
path: "ecran_un/",
params: nil
)
}
"""
@ -118,16 +120,16 @@ final class AnalyticsDefinitionTests: XCTestCase {
let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .event)
// When
let propertyEvent = definition.getStaticProperty()
let propertyEvent = definition.getStaticProperty(visibility: .package)
// Expect
let expectEvent = """
static func logEventDefinitionName() {
logEvent(
package static func logEventDefinitionName() {
AnalyticsManager.shared.logEvent(
name: "Ecran un",
action: "",
category: "",
params: [:]
params: nil
)
}
"""

View File

@ -1,6 +1,6 @@
//
// AnalyticsGeneratorTests.swift
//
//
//
// Created by Thibaut Schmitt on 06/09/2022.
//
@ -14,7 +14,7 @@ import ToolCore
@testable import ResgenSwift
final class AnalyticsGeneratorTests: XCTestCase {
private func getAnalyticsDefinition(
id: String,
path: String = "",
@ -31,7 +31,183 @@ final class AnalyticsGeneratorTests: XCTestCase {
definition.category = category
return definition
}
private func protocolString(visibility: ExtensionVisibility) -> String {
"""
// MARK: - Protocol
\(visibility) protocol AnalyticsManagerProtocol {
func logScreen(
name: String,
path: String,
params: [String: Any]?
)
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
func setEnable(_ enable: Bool)
}
"""
}
private func firebaseString() -> String {
"""
// MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Methods
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
var parameters = [
AnalyticsParameterScreenName: name as NSObject
]
if path.isEmpty == false {
parameters["path"] = path + "/iOS" as NSObject
}
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
AnalyticsEventScreenView,
parameters: parameters
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
AnalyticsParameterItemName: name.replacingOccurrences(of: " ", with: "_") as NSObject
]
if category.isEmpty == false {
parameters["AnalyticsParameterItemCategory"] = category as NSObject
}
if action.isEmpty == false {
parameters["action"] = action as NSObject
}
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
AnalyticsEventSelectContent,
parameters: parameters
)
}
func setEnable(_ enable: Bool) {
Analytics.setAnalyticsCollectionEnabled(enable)
}
}
"""
}
private func matomoString() -> String {
"""
// MARK: - Matomo
class MatomoAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Properties
private var tracker: MatomoTracker
// MARK: - Init
init(siteId: String, url: String) {
debugPrint("[Matomo service] Server URL: \\(url)")
debugPrint("[Matomo service] Site ID: \\(siteId)")
tracker = MatomoTracker(
siteId: siteId,
baseURL: URL(string: url)!
)
#if DEBUG
tracker.dispatchInterval = 5
#endif
#if DEBUG
tracker.logger = DefaultLogger(minLevel: .verbose)
#endif
debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")")
debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)")
}
// MARK: - Methods
func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
tracker.track(
view: [name],
url: urlString
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
tracker.track(
eventWithCategory: category,
action: action,
name: name,
number: nil,
url: nil
)
}
func setEnable(_ enable: Bool) {
tracker.isOptedOut = !enable
}
}
"""
}
func testGeneratedExtensionContentFirebase() {
// Given
let sectionOne = AnalyticsCategory(id: "section_one")
@ -39,119 +215,105 @@ final class AnalyticsGeneratorTests: XCTestCase {
getAnalyticsDefinition(id: "s1_def_one", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]),
getAnalyticsDefinition(id: "s1_def_two", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]),
]
let sectionTwo = AnalyticsCategory(id: "section_two")
sectionTwo.definitions = [
getAnalyticsDefinition(id: "s2_def_one", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]),
getAnalyticsDefinition(id: "s2_def_two", name: "s2 def two", type: .event, tags: ["droid","droidonly"]),
]
let sectionThree = AnalyticsCategory(id: "section_three")
sectionThree.definitions = [
getAnalyticsDefinition(id: "s3_def_one", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]),
getAnalyticsDefinition(id: "s3_def_two", name: "s3 def two", type: .event, tags: ["droid","droidonly"]),
]
// When
let extensionContent = AnalyticsGenerator.getExtensionContent(
targets: [TrackerType.firebase],
sections: [sectionOne, sectionTwo, sectionThree],
tags: ["ios", "iosonly"],
staticVar: false,
extensionName: "GenAnalytics"
visibility: .public
)
// Expect Analytics
let expect = """
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
import Foundation
import FirebaseAnalytics
// MARK: - Protocol
\(protocolString(visibility: .public))
protocol AnalyticsManagerProtocol {
\(firebaseString())
func logScreen(name: String, path: String)
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
}
// MARK: - Traker Type
// MARK: - Firebase
public enum TrackerType: CaseIterable {
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
func logScreen(name: String, path: String) {
var parameters = [
AnalyticsParameterScreenName: name as NSObject
]
Analytics.logEvent(
AnalyticsEventScreenView,
parameters: parameters
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
"action": action as NSObject,
"category": category as NSObject,
]
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
name.replacingOccurrences(of: [" "], with: "_"),
parameters: parameters
)
}
case firebase
}
// MARK: - Manager
class AnalyticsManager {
public class AnalyticsManager {
static var shared = AnalyticsManager()
public static var shared = AnalyticsManager()
private init() {}
// MARK: - Properties
var managers: [AnalyticsManagerProtocol] = []
var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
private var isEnabled: Bool = true
// MARK: - Methods
func setAnalyticsEnabled(_ enable: Bool) {
isEnabled = enable
private var isEnabled: Bool {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
func configure() {
managers.append(FirebaseAnalyticsManager())
// MARK: - Enable Methods
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
managers.forEach { (key, value) in
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
private func logScreen(name: String, path: String) {
public func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
public func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
}
public func configure() {
managers[TrackerType.firebase] = FirebaseAnalyticsManager()
}
// MARK: - Private Log Methods
private func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return }
managers.forEach { manager in
manager.logScreen(name: name, path: path)
managers.values.forEach { manager in
manager.logScreen(
name: name,
path: path,
params: params
)
}
}
@ -163,7 +325,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
) {
guard isEnabled else { return }
managers.forEach { manager in
managers.values.forEach { manager in
manager.logEvent(
name: name,
action: action,
@ -175,40 +337,42 @@ final class AnalyticsGeneratorTests: XCTestCase {
// MARK: - section_one
func logScreenS1DefOne() {
public func logScreenS1DefOne() {
logScreen(
name: "s1 def one",
path: ""
path: "",
params: nil
)
}
func logEventS1DefTwo() {
public func logEventS1DefTwo() {
logEvent(
name: "s1 def two",
action: "",
category: "",
params: [:]
params: nil
)
}
// MARK: - section_two
func logScreenS2DefOne() {
public func logScreenS2DefOne() {
logScreen(
name: "s2 def one",
path: ""
path: "",
params: nil
)
}
}
"""
if extensionContent != expect {
print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect))
}
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
}
func testGeneratedExtensionContentMatomo() {
// Given
let sectionOne = AnalyticsCategory(id: "section_one")
@ -216,139 +380,107 @@ final class AnalyticsGeneratorTests: XCTestCase {
getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]),
getAnalyticsDefinition(id: "s1_def_two", action: "test", category: "test", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]),
]
let sectionTwo = AnalyticsCategory(id: "section_two")
sectionTwo.definitions = [
getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]),
getAnalyticsDefinition(id: "s2_def_two", action: "test", category: "test", name: "s2 def two", type: .event, tags: ["droid","droidonly"]),
]
let sectionThree = AnalyticsCategory(id: "section_three")
sectionThree.definitions = [
getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]),
getAnalyticsDefinition(id: "s3_def_two", action: "test", category: "test", name: "s3 def two", type: .event, tags: ["droid","droidonly"]),
]
// When
let extensionContent = AnalyticsGenerator.getExtensionContent(
targets: [TrackerType.matomo],
sections: [sectionOne, sectionTwo, sectionThree],
tags: ["ios", "iosonly"],
staticVar: false,
extensionName: "GenAnalytics"
visibility: .package
)
// Expect Analytics
let expect = """
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
import Foundation
import MatomoTracker
// MARK: - Protocol
\(protocolString(visibility: .package))
protocol AnalyticsManagerProtocol {
\(matomoString())
func logScreen(name: String, path: String)
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
}
// MARK: - Traker Type
// MARK: - Matomo
package enum TrackerType: CaseIterable {
class MatomoAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Properties
private var tracker: MatomoTracker
// MARK: - Init
init(siteId: String, url: String) {
debugPrint("[Matomo service] Server URL: \\(url)")
debugPrint("[Matomo service] Site ID: \\(siteId)")
tracker = MatomoTracker(
siteId: siteId,
baseURL: URL(string: url)!
)
#if DEBUG
tracker.dispatchInterval = 5
#endif
#if DEBUG
tracker.logger = DefaultLogger(minLevel: .verbose)
#endif
debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")")
debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)")
}
// MARK: - Methods
func logScreen(name: String, path: String) {
guard !tracker.isOptedOut else { return }
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
tracker.track(
view: [name],
url: urlString
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
guard !tracker.isOptedOut else { return }
tracker.track(
eventWithCategory: category,
action: action,
name: name,
number: nil,
url: nil
)
}
case matomo
}
// MARK: - Manager
class AnalyticsManager {
package class AnalyticsManager {
static var shared = AnalyticsManager()
package static var shared = AnalyticsManager()
private init() {}
// MARK: - Properties
var managers: [AnalyticsManagerProtocol] = []
var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
private var isEnabled: Bool = true
// MARK: - Methods
func setAnalyticsEnabled(_ enable: Bool) {
isEnabled = enable
private var isEnabled: Bool {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
func configure(siteId: String, url: String) {
managers.append(
MatomoAnalyticsManager(
siteId: siteId,
url: url
)
// MARK: - Enable Methods
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
managers.forEach { (key, value) in
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
package func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
package func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
}
package func configure(siteId: String, url: String) {
managers[TrackerType.matomo] = MatomoAnalyticsManager(
siteId: siteId,
url: url
)
}
private func logScreen(name: String, path: String) {
// MARK: - Private Log Methods
private func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return }
managers.forEach { manager in
manager.logScreen(name: name, path: path)
managers.values.forEach { manager in
manager.logScreen(
name: name,
path: path,
params: params
)
}
}
@ -360,7 +492,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
) {
guard isEnabled else { return }
managers.forEach { manager in
managers.values.forEach { manager in
manager.logEvent(
name: name,
action: action,
@ -372,40 +504,42 @@ final class AnalyticsGeneratorTests: XCTestCase {
// MARK: - section_one
func logScreenS1DefOne() {
package func logScreenS1DefOne() {
logScreen(
name: "s1 def one",
path: "s1_def_one/"
path: "s1_def_one/",
params: nil
)
}
func logEventS1DefTwo() {
package func logEventS1DefTwo() {
logEvent(
name: "s1 def two",
action: "test",
category: "test",
params: [:]
params: nil
)
}
// MARK: - section_two
func logScreenS2DefOne() {
package func logScreenS2DefOne() {
logScreen(
name: "s2 def one",
path: "s2_def_one/"
path: "s2_def_one/",
params: nil
)
}
}
"""
if extensionContent != expect {
print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect))
}
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
}
func testGeneratedExtensionContentMatomoAndFirebase() {
// Given
let sectionOne = AnalyticsCategory(id: "section_one")
@ -413,186 +547,113 @@ final class AnalyticsGeneratorTests: XCTestCase {
getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: .screen, tags: ["ios", "iosonly"]),
getAnalyticsDefinition(id: "s1_def_two", action: "test", category: "test", name: "s1 def two", type: .event, tags: ["ios", "iosonly"]),
]
let sectionTwo = AnalyticsCategory(id: "section_two")
sectionTwo.definitions = [
getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: .screen, tags: ["ios","iosonly"]),
getAnalyticsDefinition(id: "s2_def_two", action: "test", category: "test", name: "s2 def two", type: .event, tags: ["droid","droidonly"]),
]
let sectionThree = AnalyticsCategory(id: "section_three")
sectionThree.definitions = [
getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: .screen, tags: ["droid","droidonly"]),
getAnalyticsDefinition(id: "s3_def_two", action: "test", category: "test", name: "s3 def two", type: .event, tags: ["droid","droidonly"]),
]
// When
let extensionContent = AnalyticsGenerator.getExtensionContent(
targets: [TrackerType.matomo, TrackerType.firebase],
sections: [sectionOne, sectionTwo, sectionThree],
tags: ["ios", "iosonly"],
staticVar: false,
extensionName: "GenAnalytics"
visibility: .internal
)
// Expect Analytics
let expect = """
// Generated by ResgenSwift.Analytics \(ResgenSwiftVersion)
import Foundation
import MatomoTracker
import FirebaseAnalytics
// MARK: - Protocol
\(protocolString(visibility: .internal))
protocol AnalyticsManagerProtocol {
\(matomoString())
func logScreen(name: String, path: String)
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
)
}
\(firebaseString())
// MARK: - Matomo
// MARK: - Traker Type
class MatomoAnalyticsManager: AnalyticsManagerProtocol {
internal enum TrackerType: CaseIterable {
// MARK: - Properties
private var tracker: MatomoTracker
// MARK: - Init
init(siteId: String, url: String) {
debugPrint("[Matomo service] Server URL: \\(url)")
debugPrint("[Matomo service] Site ID: \\(siteId)")
tracker = MatomoTracker(
siteId: siteId,
baseURL: URL(string: url)!
)
#if DEBUG
tracker.dispatchInterval = 5
#endif
#if DEBUG
tracker.logger = DefaultLogger(minLevel: .verbose)
#endif
debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")")
debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)")
}
// MARK: - Methods
func logScreen(name: String, path: String) {
guard !tracker.isOptedOut else { return }
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
tracker.track(
view: [name],
url: urlString
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
guard !tracker.isOptedOut else { return }
tracker.track(
eventWithCategory: category,
action: action,
name: name,
number: nil,
url: nil
)
}
}
// MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
func logScreen(name: String, path: String) {
var parameters = [
AnalyticsParameterScreenName: name as NSObject
]
Analytics.logEvent(
AnalyticsEventScreenView,
parameters: parameters
)
}
func logEvent(
name: String,
action: String,
category: String,
params: [String: Any]?
) {
var parameters: [String:NSObject] = [
"action": action as NSObject,
"category": category as NSObject,
]
if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters {
if parameters.contains(where: { (key: String, value: NSObject) in
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
}
}
Analytics.logEvent(
name.replacingOccurrences(of: [" "], with: "_"),
parameters: parameters
)
}
case matomo
case firebase
}
// MARK: - Manager
class AnalyticsManager {
internal class AnalyticsManager {
static var shared = AnalyticsManager()
internal static var shared = AnalyticsManager()
private init() {}
// MARK: - Properties
var managers: [AnalyticsManagerProtocol] = []
var managers: [TrackerType: AnalyticsManagerProtocol] = [:]
private var isEnabled: Bool = true
// MARK: - Methods
func setAnalyticsEnabled(_ enable: Bool) {
isEnabled = enable
private var isEnabled: Bool {
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
func configure(siteId: String, url: String) {
managers.append(
MatomoAnalyticsManager(
siteId: siteId,
url: url
)
// MARK: - Enable Methods
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
managers.forEach { (key, value) in
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
internal func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
internal func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
}
internal func configure(siteId: String, url: String) {
managers[TrackerType.matomo] = MatomoAnalyticsManager(
siteId: siteId,
url: url
)
managers.append(FirebaseAnalyticsManager())
managers[TrackerType.firebase] = FirebaseAnalyticsManager()
}
private func logScreen(name: String, path: String) {
// MARK: - Private Log Methods
private func logScreen(
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return }
managers.forEach { manager in
manager.logScreen(name: name, path: path)
managers.values.forEach { manager in
manager.logScreen(
name: name,
path: path,
params: params
)
}
}
@ -604,7 +665,7 @@ final class AnalyticsGeneratorTests: XCTestCase {
) {
guard isEnabled else { return }
managers.forEach { manager in
managers.values.forEach { manager in
manager.logEvent(
name: name,
action: action,
@ -616,34 +677,36 @@ final class AnalyticsGeneratorTests: XCTestCase {
// MARK: - section_one
func logScreenS1DefOne() {
internal func logScreenS1DefOne() {
logScreen(
name: "s1 def one",
path: "s1_def_one/"
path: "s1_def_one/",
params: nil
)
}
func logEventS1DefTwo() {
internal func logEventS1DefTwo() {
logEvent(
name: "s1 def two",
action: "test",
category: "test",
params: [:]
params: nil
)
}
// MARK: - section_two
func logScreenS2DefOne() {
internal func logScreenS2DefOne() {
logScreen(
name: "s2 def one",
path: "s2_def_one/"
path: "s2_def_one/",
params: nil
)
}
}
"""
if extensionContent != expect {
print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect))
}

View File

@ -21,10 +21,13 @@ final class ColorExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = ColorExtensionGenerator.getExtensionContent(colors: colors,
staticVar: false,
extensionName: "GenColors",
isSwiftUI: false)
let extensionContent = ColorExtensionGenerator.getExtensionContent(
colors: colors,
staticVar: false,
extensionName: "GenColors",
isSwiftUI: false,
visibility: .public
)
// Expect
let expect = """
@ -35,12 +38,12 @@ final class ColorExtensionGeneratorTests: XCTestCase {
extension GenColors {
/// Color colorOne is #FF00FF (light) or #00FF00 (dark)"
@objc var colorOne: UIColor {
@objc public var colorOne: UIColor {
UIColor(named: "colorOne")!
}
/// Color colorTwo is #F0F0F0 (light) or #0F0F0F (dark)"
@objc var colorTwo: UIColor {
@objc public var colorTwo: UIColor {
UIColor(named: "colorTwo")!
}
}
@ -57,10 +60,13 @@ final class ColorExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = ColorExtensionGenerator.getExtensionContent(colors: colors,
staticVar: true,
extensionName: "GenColor",
isSwiftUI: false)
let extensionContent = ColorExtensionGenerator.getExtensionContent(
colors: colors,
staticVar: true,
extensionName: "GenColor",
isSwiftUI: false,
visibility: .public
)
// Expect
let expect = """
@ -71,12 +77,12 @@ final class ColorExtensionGeneratorTests: XCTestCase {
extension GenColor {
/// Color colorOne is #FF00FF (light) or #00FF00 (dark)"
static var colorOne: UIColor {
public static var colorOne: UIColor {
UIColor(named: "colorOne")!
}
/// Color colorTwo is #F0F0F0 (light) or #0F0F0F (dark)"
static var colorTwo: UIColor {
public static var colorTwo: UIColor {
UIColor(named: "colorTwo")!
}
}
@ -93,10 +99,13 @@ final class ColorExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = ColorExtensionGenerator.getExtensionContent(colors: colors,
staticVar: false,
extensionName: "GenColors",
isSwiftUI: true)
let extensionContent = ColorExtensionGenerator.getExtensionContent(
colors: colors,
staticVar: false,
extensionName: "GenColors",
isSwiftUI: true,
visibility: .package
)
// Expect
let expect = """
@ -107,12 +116,12 @@ final class ColorExtensionGeneratorTests: XCTestCase {
extension GenColors {
/// Color colorOne is #FF00FF (light) or #00FF00 (dark)"
var colorOne: Color {
package var colorOne: Color {
Color("colorOne")
}
/// Color colorTwo is #F0F0F0 (light) or #0F0F0F (dark)"
var colorTwo: Color {
package var colorTwo: Color {
Color("colorTwo")
}
}
@ -129,10 +138,13 @@ final class ColorExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = ColorExtensionGenerator.getExtensionContent(colors: colors,
staticVar: true,
extensionName: "GenColor",
isSwiftUI: true)
let extensionContent = ColorExtensionGenerator.getExtensionContent(
colors: colors,
staticVar: true,
extensionName: "GenColor",
isSwiftUI: true,
visibility: .internal
)
// Expect
let expect = """
@ -143,12 +155,12 @@ final class ColorExtensionGeneratorTests: XCTestCase {
extension GenColor {
/// Color colorOne is #FF00FF (light) or #00FF00 (dark)"
static var colorOne: Color {
internal static var colorOne: Color {
Color("colorOne")
}
/// Color colorTwo is #F0F0F0 (light) or #0F0F0F (dark)"
static var colorTwo: Color {
internal static var colorTwo: Color {
Color("colorTwo")
}
}

View File

@ -17,12 +17,16 @@ final class ParsedColorTests: XCTestCase {
let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF")
// When
let property = color.getColorProperty(isStatic: false, isSwiftUI: false)
let property = color.getColorProperty(
isStatic: false,
isSwiftUI: false,
visibility: .public
)
// Expect
let expect = """
/// Color red is #FF0000 (light) or #0000FF (dark)"
@objc var red: UIColor {
@objc public var red: UIColor {
UIColor(named: "red")!
}
"""
@ -35,12 +39,16 @@ final class ParsedColorTests: XCTestCase {
let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF")
// When
let property = color.getColorProperty(isStatic: true, isSwiftUI: false)
let property = color.getColorProperty(
isStatic: true,
isSwiftUI: false,
visibility: .private
)
// Expect
let expect = """
/// Color red is #FF0000 (light) or #0000FF (dark)"
static var red: UIColor {
private static var red: UIColor {
UIColor(named: "red")!
}
"""
@ -53,12 +61,16 @@ final class ParsedColorTests: XCTestCase {
let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF")
// When
let property = color.getColorProperty(isStatic: false, isSwiftUI: true)
let property = color.getColorProperty(
isStatic: false,
isSwiftUI: true,
visibility: .package
)
// Expect
let expect = """
/// Color red is #FF0000 (light) or #0000FF (dark)"
var red: Color {
package var red: Color {
Color("red")
}
"""
@ -71,12 +83,16 @@ final class ParsedColorTests: XCTestCase {
let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF")
// When
let property = color.getColorProperty(isStatic: true, isSwiftUI: true)
let property = color.getColorProperty(
isStatic: true,
isSwiftUI: true,
visibility: .internal
)
// Expect
let expect = """
/// Color red is #FF0000 (light) or #0000FF (dark)"
static var red: Color {
internal static var red: Color {
Color("red")
}
"""

View File

@ -21,10 +21,13 @@ final class FontExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = FontExtensionGenerator.getExtensionContent(fontsNames: fontNames,
staticVar: false,
extensionName: "GenFonts",
isSwiftUI: false)
let extensionContent = FontExtensionGenerator.getExtensionContent(
fontsNames: fontNames,
staticVar: false,
extensionName: "GenFonts",
isSwiftUI: false,
visibility: .public
)
// Expect
let expect = """
@ -34,18 +37,18 @@ final class FontExtensionGeneratorTests: XCTestCase {
extension GenFonts {
enum FontName: String {
public enum FontName: String {
case CircularStdRegular = "CircularStd-Regular"
case CircularStdBold = "CircularStd-Bold"
}
// MARK: - Getter
func CircularStdRegular(withSize size: CGFloat) -> UIFont {
public func CircularStdRegular(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.CircularStdRegular.rawValue, size: size)!
}
func CircularStdBold(withSize size: CGFloat) -> UIFont {
public func CircularStdBold(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.CircularStdBold.rawValue, size: size)!
}
@ -64,10 +67,13 @@ final class FontExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = FontExtensionGenerator.getExtensionContent(fontsNames: fontNames,
staticVar: false,
extensionName: "GenFonts",
isSwiftUI: true)
let extensionContent = FontExtensionGenerator.getExtensionContent(
fontsNames: fontNames,
staticVar: false,
extensionName: "GenFonts",
isSwiftUI: true,
visibility: .package
)
// Expect
let expect = """
@ -77,18 +83,18 @@ final class FontExtensionGeneratorTests: XCTestCase {
extension GenFonts {
enum FontName: String {
package enum FontName: String {
case CircularStdRegular = "CircularStd-Regular"
case CircularStdBold = "CircularStd-Bold"
}
// MARK: - Getter
func CircularStdRegular(withSize size: CGFloat) -> Font {
package func CircularStdRegular(withSize size: CGFloat) -> Font {
Font.custom(FontName.CircularStdRegular.rawValue, size: size)
}
func CircularStdBold(withSize size: CGFloat) -> Font {
package func CircularStdBold(withSize size: CGFloat) -> Font {
Font.custom(FontName.CircularStdBold.rawValue, size: size)
}

View File

@ -21,11 +21,15 @@ final class FontNameTests: XCTestCase {
)
// When
let property = fontName.getProperty(isStatic: true, isSwiftUI: false)
let property = fontName.getProperty(
isStatic: true,
isSwiftUI: false,
visibility: .internal
)
// Expect
let expect = """
static let CircularStdBold: ((_ size: CGFloat) -> UIFont) = { size in
internal static let CircularStdBold: ((_ size: CGFloat) -> UIFont) = { size in
UIFont(name: FontName.CircularStdBold.rawValue, size: size)!
}
"""
@ -42,11 +46,15 @@ final class FontNameTests: XCTestCase {
)
// When
let property = fontName.getProperty(isStatic: true, isSwiftUI: false)
let property = fontName.getProperty(
isStatic: true,
isSwiftUI: false,
visibility: .package
)
// Expect
let expect = """
static let CircularStdBoldUnderline: ((_ size: CGFloat) -> UIFont) = { size in
package static let CircularStdBoldUnderline: ((_ size: CGFloat) -> UIFont) = { size in
UIFont(name: FontName.CircularStdBoldUnderline.rawValue, size: size)!
}
"""
@ -63,11 +71,15 @@ final class FontNameTests: XCTestCase {
)
// When
let property = fontName.getProperty(isStatic: false, isSwiftUI: false)
let property = fontName.getProperty(
isStatic: false,
isSwiftUI: false,
visibility: .private
)
// Expect
let expect = """
func CircularStdBold(withSize size: CGFloat) -> UIFont {
private func CircularStdBold(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.CircularStdBold.rawValue, size: size)!
}
"""
@ -84,11 +96,15 @@ final class FontNameTests: XCTestCase {
)
// When
let property = fontName.getProperty(isStatic: false, isSwiftUI: false)
let property = fontName.getProperty(
isStatic: false,
isSwiftUI: false,
visibility: .public
)
// Expect
let expect = """
func CircularStdBoldUnderline(withSize size: CGFloat) -> UIFont {
public func CircularStdBoldUnderline(withSize size: CGFloat) -> UIFont {
UIFont(name: FontName.CircularStdBoldUnderline.rawValue, size: size)!
}
"""
@ -105,11 +121,15 @@ final class FontNameTests: XCTestCase {
)
// When
let property = fontName.getProperty(isStatic: true, isSwiftUI: true)
let property = fontName.getProperty(
isStatic: true,
isSwiftUI: true,
visibility: .public
)
// Expect
let expect = """
static let CircularStdBold: ((_ size: CGFloat) -> Font) = { size in
public static let CircularStdBold: ((_ size: CGFloat) -> Font) = { size in
Font.custom(FontName.CircularStdBold.rawValue, size: size)
}
"""
@ -126,11 +146,15 @@ final class FontNameTests: XCTestCase {
)
// When
let property = fontName.getProperty(isStatic: true, isSwiftUI: true)
let property = fontName.getProperty(
isStatic: true,
isSwiftUI: true,
visibility: .package
)
// Expect
let expect = """
static let CircularStdBoldUnderline: ((_ size: CGFloat) -> Font) = { size in
package static let CircularStdBoldUnderline: ((_ size: CGFloat) -> Font) = { size in
Font.custom(FontName.CircularStdBoldUnderline.rawValue, size: size)
}
"""
@ -147,11 +171,15 @@ final class FontNameTests: XCTestCase {
)
// When
let property = fontName.getProperty(isStatic: false, isSwiftUI: true)
let property = fontName.getProperty(
isStatic: false,
isSwiftUI: true,
visibility: .package
)
// Expect
let expect = """
func CircularStdBold(withSize size: CGFloat) -> Font {
package func CircularStdBold(withSize size: CGFloat) -> Font {
Font.custom(FontName.CircularStdBold.rawValue, size: size)
}
"""
@ -168,11 +196,15 @@ final class FontNameTests: XCTestCase {
)
// When
let property = fontName.getProperty(isStatic: false, isSwiftUI: true)
let property = fontName.getProperty(
isStatic: false,
isSwiftUI: true,
visibility: .internal
)
// Expect
let expect = """
func CircularStdBoldUnderline(withSize size: CGFloat) -> Font {
internal func CircularStdBoldUnderline(withSize size: CGFloat) -> Font {
Font.custom(FontName.CircularStdBoldUnderline.rawValue, size: size)
}
"""

View File

@ -0,0 +1,76 @@
//
// AnalyticsConfigurationTests.swift
// ResgenSwift
//
// Created by Thibaut Schmitt on 18/07/2025.
//
import Foundation
@testable import ResgenSwift
import XCTest
final class AnalyticsConfigurationTests: XCTestCase {
let projectDirectory = "projectDirectory/"
func test_argsGeneration_requiredArgs() {
// Given
let testingConfiguration = AnalyticsConfiguration(
inputFile: "path/to/tags.yml",
target: "matomo firebase",
outputFile: "Analytics/Generated/AnalyticsManager.swift",
visibility: nil,
staticMembers: nil
)
// When
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: false
)
// Expect
let expectedArguments = [
"projectDirectory/path/to/tags.yml",
"--target",
"matomo firebase",
"--output-file",
"projectDirectory/Analytics/Generated/AnalyticsManager.swift",
]
XCTAssertEqual(arguments, expectedArguments)
}
func test_argsGeneration_allArguments() {
// Given
let testingConfiguration = AnalyticsConfiguration(
inputFile: "path/to/tags.yml",
target: "matomo firebase",
outputFile: "Analytics/Generated/AnalyticsManager.swift",
visibility: "public",
staticMembers: false
)
// When
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: true
)
// Expect
let expectedArguments = [
"-f",
"projectDirectory/path/to/tags.yml",
"--target",
"matomo firebase",
"--output-file",
"projectDirectory/Analytics/Generated/AnalyticsManager.swift",
"--visibility",
"public",
"--static-members",
"false"
]
XCTAssertEqual(arguments, expectedArguments)
}
}

View File

@ -16,16 +16,22 @@ final class ColorsConfigurationTests: XCTestCase {
func test_argsGeneration_requiredArgs() {
// Given
let testingConfiguration = ColorsConfiguration(inputFile: "path/to/colors.txt",
style: ColorStyle.all.rawValue,
xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Colors/Generated",
extensionName: nil,
extensionNameUIKit: nil,
extensionSuffix: nil,
staticMembers: false)
let testingConfiguration = ColorsConfiguration(
inputFile: "path/to/colors.txt",
style: ColorStyle.all.rawValue,
xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: nil,
extensionName: nil,
extensionNameUIKit: nil,
extensionSuffix: nil,
visibility: nil,
staticMembers: false
)
// When
let arguments = testingConfiguration.getArguments(projectDirectory: projectDirectory, force: false)
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: false
)
// Expect
let expectedArguments = [
@ -34,8 +40,6 @@ final class ColorsConfigurationTests: XCTestCase {
"all",
"--xcassets-path",
"projectDirectory/path/to/assets.xcassets",
"--extension-output-path",
"projectDirectory/Colors/Generated",
"--static-members",
"false"
]
@ -45,16 +49,22 @@ final class ColorsConfigurationTests: XCTestCase {
func test_argsGeneration_allArguments() {
// Given
let testingConfiguration = ColorsConfiguration(inputFile: "path/to/colors.txt",
style: ColorStyle.all.rawValue,
xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Colors/Generated",
extensionName: "AppUIColor",
extensionNameUIKit: "AppColor",
extensionSuffix: "Testing",
staticMembers: false)
let testingConfiguration = ColorsConfiguration(
inputFile: "path/to/colors.txt",
style: ColorStyle.all.rawValue,
xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Colors/Generated",
extensionName: "AppUIColor",
extensionNameUIKit: "AppColor",
extensionSuffix: "Testing",
visibility: "public",
staticMembers: false
)
// When
let arguments = testingConfiguration.getArguments(projectDirectory: projectDirectory, force: true)
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: true
)
// Expect
let expectedArguments = [
@ -66,14 +76,16 @@ final class ColorsConfigurationTests: XCTestCase {
"projectDirectory/path/to/assets.xcassets",
"--extension-output-path",
"projectDirectory/Colors/Generated",
"--static-members",
"false",
"--extension-name",
"AppUIColor",
"--extension-name-ui-kit",
"AppColor",
"--extension-suffix",
"Testing",
"--visibility",
"public",
"--static-members",
"false"
]
XCTAssertEqual(arguments, expectedArguments)

View File

@ -16,23 +16,25 @@ final class FontsConfigurationTests: XCTestCase {
func test_argsGeneration_requiredArgs() {
// Given
let testingConfiguration = FontsConfiguration(inputFile: "path/to/fonts.txt",
extensionOutputPath: "Fonts/Generated",
extensionName: nil,
extensionNameUIKit: nil,
extensionSuffix: nil,
infoPlistPaths: nil,
staticMembers: nil)
let testingConfiguration = FontsConfiguration(
inputFile: "path/to/fonts.txt",
extensionOutputPath: nil,
extensionName: nil,
extensionNameUIKit: nil,
extensionSuffix: nil,
infoPlistPaths: nil,
visibility: nil,
staticMembers: nil
)
// When
let arguments = testingConfiguration.getArguments(projectDirectory: projectDirectory, force: false)
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: false
)
// Expect
let expectedArguments = [
"projectDirectory/path/to/fonts.txt",
"--extension-output-path",
"projectDirectory/Fonts/Generated",
"--static-members",
"false"
"projectDirectory/path/to/fonts.txt"
]
XCTAssertEqual(arguments, expectedArguments)
@ -40,15 +42,21 @@ final class FontsConfigurationTests: XCTestCase {
func test_argsGeneration_allArguments() {
// Given
let testingConfiguration = FontsConfiguration(inputFile: "path/to/fonts.txt",
extensionOutputPath: "Fonts/Generated",
extensionName: "AppUIFont",
extensionNameUIKit: "AppFont",
extensionSuffix: "Testing",
infoPlistPaths: "path/to/plist1.plist path/to/plist2.plist",
staticMembers: true)
let testingConfiguration = FontsConfiguration(
inputFile: "path/to/fonts.txt",
extensionOutputPath: "Fonts/Generated",
extensionName: "AppUIFont",
extensionNameUIKit: "AppFont",
extensionSuffix: "Testing",
infoPlistPaths: "path/to/plist1.plist path/to/plist2.plist",
visibility: "package",
staticMembers: true
)
// When
let arguments = testingConfiguration.getArguments(projectDirectory: projectDirectory, force: true)
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: true
)
// Expect
let expectedArguments = [
@ -56,14 +64,16 @@ final class FontsConfigurationTests: XCTestCase {
"projectDirectory/path/to/fonts.txt",
"--extension-output-path",
"projectDirectory/Fonts/Generated",
"--static-members",
"true",
"--extension-name",
"AppUIFont",
"--extension-name-ui-kit",
"AppFont",
"--extension-suffix",
"Testing",
"--visibility",
"package",
"--static-members",
"true",
"--info-plist-paths",
"projectDirectory/path/to/plist1.plist projectDirectory/path/to/plist2.plist"
]

View File

@ -16,26 +16,28 @@ final class ImagesConfigurationTests: XCTestCase {
func test_argsGeneration_requiredArgs() {
// Given
let testingConfiguration = ImagesConfiguration(inputFile: "path/to/images.txt",
xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Images/Generated",
extensionName: nil,
extensionNameUIKit: nil,
extensionSuffix: nil,
staticMembers: nil)
let testingConfiguration = ImagesConfiguration(
inputFile: "path/to/images.txt",
xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: nil,
extensionName: nil,
extensionNameUIKit: nil,
extensionSuffix: nil,
visibility: nil,
staticMembers: nil
)
// When
let arguments = testingConfiguration.getArguments(projectDirectory: projectDirectory, force: false)
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: false
)
// Expect
let expectedArguments = [
"projectDirectory/path/to/images.txt",
"--xcassets-path",
"projectDirectory/path/to/assets.xcassets",
"--extension-output-path",
"projectDirectory/Images/Generated",
"--static-members",
"false"
"projectDirectory/path/to/assets.xcassets"
]
XCTAssertEqual(arguments, expectedArguments)
@ -43,16 +45,22 @@ final class ImagesConfigurationTests: XCTestCase {
func test_argsGeneration_allArguments() {
// Given
let testingConfiguration = ImagesConfiguration(inputFile: "path/to/images.txt",
xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Images/Generated",
extensionName: "AppUIImage",
extensionNameUIKit: "AppImage",
extensionSuffix: "Testing",
staticMembers: true)
let testingConfiguration = ImagesConfiguration(
inputFile: "path/to/images.txt",
xcassetsPath: "path/to/assets.xcassets",
extensionOutputPath: "Images/Generated",
extensionName: "AppUIImage",
extensionNameUIKit: "AppImage",
extensionSuffix: "Testing",
visibility: "private",
staticMembers: true
)
// When
let arguments = testingConfiguration.getArguments(projectDirectory: projectDirectory, force: true)
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: true
)
// Expect
let expectedArguments = [
@ -62,14 +70,16 @@ final class ImagesConfigurationTests: XCTestCase {
"projectDirectory/path/to/assets.xcassets",
"--extension-output-path",
"projectDirectory/Images/Generated",
"--static-members",
"true",
"--extension-name",
"AppUIImage",
"--extension-name-ui-kit",
"AppImage",
"--extension-suffix",
"Testing",
"--visibility",
"private",
"--static-members",
"true"
]
XCTAssertEqual(arguments, expectedArguments)

View File

@ -0,0 +1,98 @@
//
// StringsConfigurationTests.swift
// ResgenSwift
//
// Created by Thibaut Schmitt on 18/07/2025.
//
import Foundation
@testable import ResgenSwift
import XCTest
final class StringsConfigurationTests: XCTestCase {
let projectDirectory = "projectDirectory/"
func test_argsGeneration_requiredArgs() {
// Given
let testingConfiguration = StringsConfiguration(
inputFile: "path/to/strings.txt",
outputPath: "Strings/Generated",
langs: "fr en en-us",
defaultLang: "en",
extensionOutputPath: nil, // Strings/Generated
extensionName: nil, // String
extensionSuffix: nil, // Testing
visibility: nil, // "internal"
staticMembers: nil, // true
xcStrings: nil // true
)
// When
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: false
)
// Expect
let expectedArguments = [
"projectDirectory/path/to/strings.txt",
"--output-path",
"projectDirectory/Strings/Generated",
"--langs",
"fr en en-us",
"--default-lang",
"en"
]
XCTAssertEqual(arguments, expectedArguments)
}
func test_argsGeneration_allArguments() {
// Given
let testingConfiguration = StringsConfiguration(
inputFile: "path/to/strings.txt",
outputPath: "Strings/Generated",
langs: "fr en en-us",
defaultLang: "en",
extensionOutputPath: "Strings/Generated",
extensionName: "AppString",
extensionSuffix: "Testing",
visibility: "internal",
staticMembers: true,
xcStrings: true
)
// When
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: true
)
// Expect
let expectedArguments = [
"-f",
"projectDirectory/path/to/strings.txt",
"--output-path",
"projectDirectory/Strings/Generated",
"--langs",
"fr en en-us",
"--default-lang",
"en",
"--extension-output-path",
"projectDirectory/Strings/Generated",
"--extension-name",
"AppString",
"--extension-suffix",
"Testing",
"--visibility",
"internal",
"--xc-strings",
"true",
"--static-members",
"true"
]
XCTAssertEqual(arguments, expectedArguments)
}
}

View File

@ -0,0 +1,84 @@
//
// TagsConfigurationTests.swift
// ResgenSwift
//
// Created by Thibaut Schmitt on 18/07/2025.
//
import Foundation
@testable import ResgenSwift
import XCTest
final class TagsConfigurationTests: XCTestCase {
let projectDirectory = "projectDirectory/"
func test_argsGeneration_requiredArgs() {
// Given
let testingConfiguration = TagsConfiguration(
inputFile: "path/to/tags.txt",
lang: "ium",
extensionOutputPath: "Tags/Generated",
extensionName: nil,
extensionSuffix: nil,
visibility: nil,
staticMembers: nil
)
// When
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: false
)
// Expect
let expectedArguments = [
"projectDirectory/path/to/tags.txt",
"--lang",
"ium",
"--extension-output-path",
"projectDirectory/Tags/Generated"
]
XCTAssertEqual(arguments, expectedArguments)
}
func test_argsGeneration_allArguments() {
// Given
let testingConfiguration = TagsConfiguration(
inputFile: "path/to/tags.txt",
lang: "ium",
extensionOutputPath: "Tags/Generated",
extensionName: "AppTag",
extensionSuffix: "Testing",
visibility: "private",
staticMembers: true
)
// When
let arguments = testingConfiguration.getArguments(
projectDirectory: projectDirectory,
force: true
)
// Expect
let expectedArguments = [
"-f",
"projectDirectory/path/to/tags.txt",
"--lang",
"ium",
"--extension-output-path",
"projectDirectory/Tags/Generated",
"--extension-name",
"AppTag",
"--extension-suffix",
"Testing",
"--visibility",
"private",
"--static-members",
"true"
]
XCTAssertEqual(arguments, expectedArguments)
}
}

View File

@ -21,11 +21,14 @@ final class ImageExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = ImageExtensionGenerator.getExtensionContent(images: images,
staticVar: false,
extensionName: "GenImages",
inputFilename: "myInputFilename",
isSwiftUI: false)
let extensionContent = ImageExtensionGenerator.getExtensionContent(
images: images,
staticVar: false,
extensionName: "GenImages",
inputFilename: "myInputFilename",
isSwiftUI: false,
visibility: .public
)
// Expect
let expect = """
@ -36,11 +39,11 @@ final class ImageExtensionGeneratorTests: XCTestCase {
extension GenImages {
var image_one: UIImage {
public var image_one: UIImage {
UIImage(named: "image_one")!
}
var image_two: UIImage {
public var image_two: UIImage {
UIImage(named: "image_two")!
}
}
@ -57,11 +60,14 @@ final class ImageExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = ImageExtensionGenerator.getExtensionContent(images: images,
staticVar: true,
extensionName: "GenImages",
inputFilename: "myInputFilename",
isSwiftUI: false)
let extensionContent = ImageExtensionGenerator.getExtensionContent(
images: images,
staticVar: true,
extensionName: "GenImages",
inputFilename: "myInputFilename",
isSwiftUI: false,
visibility: .internal
)
// Expect
let expect = """
@ -72,11 +78,11 @@ final class ImageExtensionGeneratorTests: XCTestCase {
extension GenImages {
static var image_one: UIImage {
internal static var image_one: UIImage {
UIImage(named: "image_one")!
}
static var image_two: UIImage {
internal static var image_two: UIImage {
UIImage(named: "image_two")!
}
}
@ -93,11 +99,14 @@ final class ImageExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = ImageExtensionGenerator.getExtensionContent(images: images,
staticVar: false,
extensionName: "GenImages",
inputFilename: "myInputFilename",
isSwiftUI: true)
let extensionContent = ImageExtensionGenerator.getExtensionContent(
images: images,
staticVar: false,
extensionName: "GenImages",
inputFilename: "myInputFilename",
isSwiftUI: true,
visibility: .public
)
// Expect
let expect = """
@ -108,11 +117,11 @@ final class ImageExtensionGeneratorTests: XCTestCase {
extension GenImages {
var image_one: Image {
public var image_one: Image {
Image("image_one")
}
var image_two: Image {
public var image_two: Image {
Image("image_two")
}
}
@ -129,11 +138,14 @@ final class ImageExtensionGeneratorTests: XCTestCase {
]
// When
let extensionContent = ImageExtensionGenerator.getExtensionContent(images: images,
staticVar: true,
extensionName: "GenImages",
inputFilename: "myInputFilename",
isSwiftUI: true)
let extensionContent = ImageExtensionGenerator.getExtensionContent(
images: images,
staticVar: true,
extensionName: "GenImages",
inputFilename: "myInputFilename",
isSwiftUI: true,
visibility: .package
)
// Expect
let expect = """
@ -144,11 +156,11 @@ final class ImageExtensionGeneratorTests: XCTestCase {
extension GenImages {
static var image_one: Image {
package static var image_one: Image {
Image("image_one")
}
static var image_two: Image {
package static var image_two: Image {
Image("image_two")
}
}

View File

@ -37,17 +37,23 @@ final class ParsedImageTests: XCTestCase {
func test_uiKit_GeneratedProperty() {
// Given
let imageName = "the_name"
let parsedImage = ParsedImage(name: imageName,
tags: "id",
width: 10,
height: 10)
let parsedImage = ParsedImage(
name: imageName,
tags: "id",
width: 10,
height: 10
)
// When
let property = parsedImage.getImageProperty(isStatic: false, isSwiftUI: false)
let property = parsedImage.getImageProperty(
isStatic: false,
isSwiftUI: false,
visibility: .public
)
// Expect
let expect = """
var \(imageName): UIImage {
public var \(imageName): UIImage {
UIImage(named: "\(imageName)")!
}
"""
@ -58,17 +64,23 @@ final class ParsedImageTests: XCTestCase {
func test_uiKit_GeneratedStaticProperty() {
// Given
let imageName = "the_name"
let parsedImage = ParsedImage(name: imageName,
tags: "id",
width: 10,
height: 10)
let parsedImage = ParsedImage(
name: imageName,
tags: "id",
width: 10,
height: 10
)
// When
let property = parsedImage.getImageProperty(isStatic: true, isSwiftUI: false)
let property = parsedImage.getImageProperty(
isStatic: true,
isSwiftUI: false,
visibility: .internal
)
// Expect
let expect = """
static var \(imageName): UIImage {
internal static var \(imageName): UIImage {
UIImage(named: "\(imageName)")!
}
"""
@ -79,17 +91,23 @@ final class ParsedImageTests: XCTestCase {
func test_swiftUI_GeneratedProperty() {
// Given
let imageName = "the_name"
let parsedImage = ParsedImage(name: imageName,
tags: "id",
width: 10,
height: 10)
let parsedImage = ParsedImage(
name: imageName,
tags: "id",
width: 10,
height: 10
)
// When
let property = parsedImage.getImageProperty(isStatic: false, isSwiftUI: true)
let property = parsedImage.getImageProperty(
isStatic: false,
isSwiftUI: true,
visibility: .private
)
// Expect
let expect = """
var \(imageName): Image {
private var \(imageName): Image {
Image("\(imageName)")
}
"""
@ -100,17 +118,23 @@ final class ParsedImageTests: XCTestCase {
func test_swiftUI_GeneratedStaticProperty() {
// Given
let imageName = "the_name"
let parsedImage = ParsedImage(name: imageName,
tags: "id",
width: 10,
height: 10)
let parsedImage = ParsedImage(
name: imageName,
tags: "id",
width: 10,
height: 10
)
// When
let property = parsedImage.getImageProperty(isStatic: true, isSwiftUI: true)
let property = parsedImage.getImageProperty(
isStatic: true,
isSwiftUI: true,
visibility: .package
)
// Expect
let expect = """
static var \(imageName): Image {
package static var \(imageName): Image {
Image("\(imageName)")
}
"""
@ -121,10 +145,12 @@ final class ParsedImageTests: XCTestCase {
func testAssetContentJson() {
// Given
let imageName = "the_name"
let parsedImage = ParsedImage(name: imageName,
tags: "id",
width: 10,
height: 10)
let parsedImage = ParsedImage(
name: imageName,
tags: "id",
width: 10,
height: 10
)
// When
let property = parsedImage.generateImageContent(isVector: false)

View File

@ -8,9 +8,9 @@
// CPD-OFF
import Foundation
import XCTest
@testable import ResgenSwift
import ToolCore
import XCTest
final class DefinitionTests: XCTestCase {
@ -94,9 +94,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us")
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr", visibility: .public)
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en", visibility: .public)
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us", visibility: .public)
// Expect
let expectFr = """
@ -105,7 +105,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
public var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "This is a comment")
}
"""
@ -116,7 +116,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
public var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "This is a comment")
}
"""
@ -127,7 +127,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
public var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "This is a comment")
}
"""
@ -149,9 +149,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us")
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr", visibility: .private)
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en", visibility: .private)
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us", visibility: .private)
// Expect
let expectFr = """
@ -160,7 +160,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
private var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
}
"""
@ -171,7 +171,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
private var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
}
"""
@ -182,7 +182,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
private var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
}
"""
@ -203,9 +203,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us")
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr", visibility: .public)
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en", visibility: .public)
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us", visibility: .public)
// Expect
let expectFr = """
@ -214,7 +214,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
public var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
}
"""
@ -225,7 +225,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
public var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
}
"""
@ -236,7 +236,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
public var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
}
"""
@ -260,10 +260,10 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us")
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr", visibility: .public)
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en", visibility: .public)
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us", visibility: .public)
// Expect
let expectFr = """
/// Translation in fr :
@ -271,7 +271,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
static var definition_name: String {
public static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "This is a comment")
}
"""
@ -282,7 +282,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
static var definition_name: String {
public static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "This is a comment")
}
"""
@ -293,7 +293,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
static var definition_name: String {
public static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "This is a comment")
}
"""
@ -315,9 +315,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us")
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr", visibility: .internal)
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en", visibility: .internal)
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us", visibility: .internal)
// Expect
let expectFr = """
@ -326,7 +326,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
}
"""
@ -337,7 +337,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
}
"""
@ -348,7 +348,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
}
"""
@ -369,9 +369,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us")
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr", visibility: .internal)
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en", visibility: .internal)
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us", visibility: .internal)
// Expect
let expectFr = """
@ -380,7 +380,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
}
"""
@ -391,7 +391,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
}
"""
@ -402,7 +402,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
}
"""
@ -422,8 +422,8 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr", visibility: .internal)
// Expect
let expectFr = """
/// Translation in fr :
@ -431,7 +431,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
internal var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "This is a comment")
}
@ -440,7 +440,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
func definition_name(arg0: String) -> String {
internal func definition_name(arg0: String) -> String {
String(format: self.definition_name, arg0)
}
"""
@ -458,8 +458,8 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr", visibility: .private)
// Expect
let expectFr = """
/// Translation in fr :
@ -467,7 +467,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
private var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" ! Your age is %d :) Your weight is %f ;-)", comment: "This is a comment")
}
@ -476,7 +476,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
func definition_name(arg0: String, arg1: Int, arg2: Double) -> String {
private func definition_name(arg0: String, arg1: Int, arg2: Double) -> String {
String(format: self.definition_name, arg0, arg1, arg2)
}
"""
@ -495,16 +495,16 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en")
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr", visibility: .public)
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en", visibility: .public)
let expectFr = """
/// Translation in fr :
/// Vous %%: %1$@ %2$@ Age: %3$d
///
/// Comment :
/// This is a comment
var definition_name: String {
public var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Vous %%: %1$@ %2$@ Age: %3$d", comment: "This is a comment")
}
@ -513,7 +513,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
func definition_name(arg0: String, arg1: String, arg2: Int) -> String {
public func definition_name(arg0: String, arg1: String, arg2: Int) -> String {
String(format: self.definition_name, arg0, arg1, arg2)
}
"""
@ -524,7 +524,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
public var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "This is a comment")
}
@ -533,7 +533,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
func definition_name(arg0: String, arg1: String, arg2: Int) -> String {
public func definition_name(arg0: String, arg1: String, arg2: Int) -> String {
String(format: self.definition_name, arg0, arg1, arg2)
}
"""
@ -556,10 +556,10 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getProperty(forLang: "fr")
let propertyEn = definition.getProperty(forLang: "en")
let propertyEnUs = definition.getProperty(forLang: "en-us")
let propertyFr = definition.getProperty(forLang: "fr", visibility: .public)
let propertyEn = definition.getProperty(forLang: "en", visibility: .public)
let propertyEnUs = definition.getProperty(forLang: "en-us", visibility: .public)
// Expect
let expectFr = """
/// Translation in fr :
@ -567,7 +567,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
public var definition_name: String {
"C'est la traduction francaise"
}
"""
@ -578,7 +578,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
public var definition_name: String {
"This is the english translation"
}
"""
@ -589,7 +589,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
var definition_name: String {
public var definition_name: String {
"This is the english us translation"
}
"""
@ -611,9 +611,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getProperty(forLang: "fr")
let propertyEn = definition.getProperty(forLang: "en")
let propertyEnUs = definition.getProperty(forLang: "en-us")
let propertyFr = definition.getProperty(forLang: "fr", visibility: .package)
let propertyEn = definition.getProperty(forLang: "en", visibility: .package)
let propertyEnUs = definition.getProperty(forLang: "en-us", visibility: .package)
// Expect
let expectFr = """
@ -622,7 +622,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
package var definition_name: String {
"C'est la traduction francaise"
}
"""
@ -633,7 +633,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
package var definition_name: String {
"This is the english translation"
}
"""
@ -644,7 +644,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
package var definition_name: String {
"This is the english us translation"
}
"""
@ -665,9 +665,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getProperty(forLang: "fr")
let propertyEn = definition.getProperty(forLang: "en")
let propertyEnUs = definition.getProperty(forLang: "en-us")
let propertyFr = definition.getProperty(forLang: "fr", visibility: .private)
let propertyEn = definition.getProperty(forLang: "en", visibility: .private)
let propertyEnUs = definition.getProperty(forLang: "en-us", visibility: .private)
// Expect
let expectFr = """
@ -676,7 +676,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
private var definition_name: String {
"C'est la traduction francaise"
}
"""
@ -687,7 +687,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
private var definition_name: String {
"This is the english translation"
}
"""
@ -698,7 +698,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
var definition_name: String {
private var definition_name: String {
"This is the english us translation"
}
"""
@ -722,10 +722,10 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getStaticProperty(forLang: "fr")
let propertyEn = definition.getStaticProperty(forLang: "en")
let propertyEnUs = definition.getStaticProperty(forLang: "en-us")
let propertyFr = definition.getStaticProperty(forLang: "fr", visibility: .internal)
let propertyEn = definition.getStaticProperty(forLang: "en", visibility: .internal)
let propertyEnUs = definition.getStaticProperty(forLang: "en-us", visibility: .internal)
// Expect
let expectFr = """
/// Translation in fr :
@ -733,7 +733,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
static var definition_name: String {
internal static var definition_name: String {
"C'est la traduction francaise"
}
"""
@ -744,7 +744,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
static var definition_name: String {
internal static var definition_name: String {
"This is the english translation"
}
"""
@ -755,7 +755,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// This is a comment
static var definition_name: String {
internal static var definition_name: String {
"This is the english us translation"
}
"""
@ -777,9 +777,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getStaticProperty(forLang: "fr")
let propertyEn = definition.getStaticProperty(forLang: "en")
let propertyEnUs = definition.getStaticProperty(forLang: "en-us")
let propertyFr = definition.getStaticProperty(forLang: "fr", visibility: .internal)
let propertyEn = definition.getStaticProperty(forLang: "en", visibility: .internal)
let propertyEnUs = definition.getStaticProperty(forLang: "en-us", visibility: .internal)
// Expect
let expectFr = """
@ -788,7 +788,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
"C'est la traduction francaise"
}
"""
@ -799,7 +799,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
"This is the english translation"
}
"""
@ -810,7 +810,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
"This is the english us translation"
}
"""
@ -831,9 +831,9 @@ final class DefinitionTests: XCTestCase {
]
// When
let propertyFr = definition.getStaticProperty(forLang: "fr")
let propertyEn = definition.getStaticProperty(forLang: "en")
let propertyEnUs = definition.getStaticProperty(forLang: "en-us")
let propertyFr = definition.getStaticProperty(forLang: "fr", visibility: .internal)
let propertyEn = definition.getStaticProperty(forLang: "en", visibility: .internal)
let propertyEnUs = definition.getStaticProperty(forLang: "en-us", visibility: .internal)
// Expect
let expectFr = """
@ -842,7 +842,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
"C'est la traduction francaise"
}
"""
@ -853,7 +853,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
"This is the english translation"
}
"""
@ -864,7 +864,7 @@ final class DefinitionTests: XCTestCase {
///
/// Comment :
/// No comment
static var definition_name: String {
internal static var definition_name: String {
"This is the english us translation"
}
"""

View File

@ -20,6 +20,7 @@ extension StringsFileGeneratorTests {
s2DefOneComment: String = "",
s2DefTwoFr: String = "Section Deux - Definition Deux",
s2DefTwoComment: String = "",
visibility: ExtensionVisibility = .internal
) -> String {
"""
// Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion)
@ -30,13 +31,13 @@ extension StringsFileGeneratorTests {
extension GenStrings {
enum KeyStrings: String {
\(visibility) enum KeyStrings: String {
case s1_def_one = "s1_def_one"
case s1_def_two = "s1_def_two"
case s2_def_one = "s2_def_one"
case s2_def_two = "s2_def_two"
var keyPath: KeyPath<GenStrings, String> {
\(visibility) var keyPath: KeyPath<GenStrings, String> {
switch self {
case .s1_def_one: return \\GenStrings.s1_def_one
case .s1_def_two: return \\GenStrings.s1_def_two
@ -53,7 +54,7 @@ extension StringsFileGeneratorTests {
///
/// Comment :
/// \(s1DefOneComment.isEmpty ? "No comment" : s1DefOneComment)
\(staticVar ? "static " : "")var s1_def_one: String {
\(visibility) \(staticVar ? "static " : "")var s1_def_one: String {
NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "\(s1DefOneComment)")
}
@ -62,7 +63,7 @@ extension StringsFileGeneratorTests {
///
/// Comment :
/// \(s1DefTwoComment.isEmpty ? "No comment" : s1DefTwoComment)
\(staticVar ? "static " : "")var s1_def_two: String {
\(visibility) \(staticVar ? "static " : "")var s1_def_two: String {
NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "\(s1DefTwoComment)")
}
@ -73,7 +74,7 @@ extension StringsFileGeneratorTests {
///
/// Comment :
/// \(s2DefOneComment.isEmpty ? "No comment" : s2DefOneComment)
\(staticVar ? "static " : "")var s2_def_one: String {
\(visibility) \(staticVar ? "static " : "")var s2_def_one: String {
NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "\(s2DefOneComment)")
}
@ -82,7 +83,7 @@ extension StringsFileGeneratorTests {
///
/// Comment :
/// \(s2DefTwoComment.isEmpty ? "No comment" : s2DefTwoComment)
\(staticVar ? "static " : "")var s2_def_two: String {
\(visibility) \(staticVar ? "static " : "")var s2_def_two: String {
NSLocalizedString("s2_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Deux", comment: "\(s2DefTwoComment)")
}
}

View File

@ -373,13 +373,16 @@ final class StringsFileGeneratorTests: XCTestCase {
let sectionTwo = Section.Mock.getSectionTwo()
// When
let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo],
defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"],
staticVar: false,
inputFilename: "myInputFilename",
extensionName: "GenStrings",
extensionSuffix: "strings")
let extensionContent = StringsFileGenerator.getExtensionContent(
sections: [sectionOne, sectionTwo],
defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"],
staticVar: false,
inputFilename: "myInputFilename",
extensionName: "GenStrings",
extensionSuffix: "strings",
visibility: .internal
)
// Expect
let expect = Self.getExtensionContentExpectation(
@ -404,13 +407,16 @@ final class StringsFileGeneratorTests: XCTestCase {
)
// When
let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo],
defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"],
staticVar: false,
inputFilename: "myInputFilename",
extensionName: "GenStrings",
extensionSuffix: "strings")
let extensionContent = StringsFileGenerator.getExtensionContent(
sections: [sectionOne, sectionTwo],
defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"],
staticVar: false,
inputFilename: "myInputFilename",
extensionName: "GenStrings",
extensionSuffix: "strings",
visibility: .public
)
// Expect
let expect = Self.getExtensionContentExpectation(
@ -419,6 +425,7 @@ final class StringsFileGeneratorTests: XCTestCase {
s1DefTwoComment: "This is a comment",
s2DefOneComment: "This is a comment",
s2DefTwoComment: "This is a comment",
visibility: .public
)
if extensionContent != expect {
@ -434,17 +441,21 @@ final class StringsFileGeneratorTests: XCTestCase {
let sectionTwo = Section.Mock.getSectionTwo()
// When
let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo],
defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"],
staticVar: true,
inputFilename: "myInputFilename",
extensionName: "GenStrings",
extensionSuffix: "strings")
let extensionContent = StringsFileGenerator.getExtensionContent(
sections: [sectionOne, sectionTwo],
defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"],
staticVar: true,
inputFilename: "myInputFilename",
extensionName: "GenStrings",
extensionSuffix: "strings",
visibility: .package
)
// Expect
let expect = Self.getExtensionContentExpectation(
staticVar: true
staticVar: true,
visibility: .package
)
if extensionContent != expect {
@ -465,13 +476,16 @@ final class StringsFileGeneratorTests: XCTestCase {
)
// When
let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo],
defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"],
staticVar: true,
inputFilename: "myInputFilename",
extensionName: "GenStrings",
extensionSuffix: "strings")
let extensionContent = StringsFileGenerator.getExtensionContent(
sections: [sectionOne, sectionTwo],
defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"],
staticVar: true,
inputFilename: "myInputFilename",
extensionName: "GenStrings",
extensionSuffix: "strings",
visibility: .internal
)
// Expect
let expect = Self.getExtensionContentExpectation(
@ -480,6 +494,7 @@ final class StringsFileGeneratorTests: XCTestCase {
s1DefTwoComment: "This is a comment",
s2DefOneComment: "This is a comment",
s2DefTwoComment: "This is a comment",
visibility: .internal
)
if extensionContent != expect {

View File

@ -13,7 +13,11 @@ import ToolCore
final class TagsGeneratorTests: XCTestCase {
private func getDefinition(name: String, lang: String, tags: [String]) -> Definition {
private func getDefinition(
name: String,
lang: String,
tags: [String]
) -> Definition {
let definition = Definition(name: name)
definition.tags = tags
definition.translations = [lang: "Some translation"]
@ -41,11 +45,15 @@ final class TagsGeneratorTests: XCTestCase {
]
// When
let extensionContent = TagsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree],
lang: "ium",
tags: ["ios", "iosonly"],
staticVar: false,
extensionName: "GenTags")
let extensionContent = TagsGenerator.getExtensionContent(
sections: [sectionOne, sectionTwo, sectionThree],
lang: "ium",
tags: ["ios", "iosonly"],
staticVar: false,
extensionName: "GenTags",
visibility: .public
)
// Expect Tags
let expect = """
// Generated by ResgenSwift.Strings.Tags \(ResgenSwiftVersion)
@ -60,7 +68,7 @@ final class TagsGeneratorTests: XCTestCase {
///
/// Comment :
/// No comment
var s1_def_one: String {
public var s1_def_one: String {
"Some translation"
}
@ -69,7 +77,7 @@ final class TagsGeneratorTests: XCTestCase {
///
/// Comment :
/// No comment
var s1_def_two: String {
public var s1_def_two: String {
"Some translation"
}
@ -80,7 +88,7 @@ final class TagsGeneratorTests: XCTestCase {
///
/// Comment :
/// No comment
var s2_def_one: String {
public var s2_def_one: String {
"Some translation"
}
}