10 Commits

Author SHA1 Message Date
082a95f244 Merge branch 'master' into edit-for-swiftui-default
Some checks failed
gitea-openium/resgen.swift/pipeline/pr-master There was a failure building this commit
2023-12-13 10:46:25 +01:00
443d8d0727 Edit sample file
Some checks failed
gitea-openium/resgen.swift/pipeline/pr-master There was a failure building this commit
2023-12-13 09:29:19 +01:00
f0d9ac3337 Add parse error 2023-12-13 09:29:19 +01:00
31d20bfe2e Add missing print error 2023-12-13 09:29:19 +01:00
dfe31b5c80 Retours sur la structure du code 2023-12-13 09:29:19 +01:00
5427ff6786 Actualiser Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-11 10:19:17 +01:00
abb7c8f8c8 Add Swiftlint
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
2023-12-11 10:13:18 +01:00
92626b76ad Fix Image 2023-12-11 10:12:11 +01:00
449f16499b Fix Font 2023-12-11 10:12:11 +01:00
cd873ca5d9 Fix Color 2023-12-11 10:12:11 +01:00
33 changed files with 396 additions and 2775 deletions

2
Jenkinsfile vendored
View File

@ -1,6 +1,6 @@
library "openiumpipeline" library "openiumpipeline"
env.DEVELOPER_DIR="/Applications/Xcode-15.4.0.app/Contents/Developer" env.DEVELOPER_DIR="/Applications/Xcode-15.0.1.app/Contents/Developer"
//env.SIMULATOR_DEVICE_TYPES="iPad--7th-generation-" //env.SIMULATOR_DEVICE_TYPES="iPad--7th-generation-"
env.IS_PACKAGE_SWIFT=1 env.IS_PACKAGE_SWIFT=1
env.TARGETS_MACOS=1 env.TARGETS_MACOS=1

View File

@ -1,5 +1,32 @@
{ {
"pins" : [ "pins" : [
{
"identity" : "collectionconcurrencykit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git",
"state" : {
"revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95",
"version" : "0.2.0"
}
},
{
"identity" : "cryptoswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
"state" : {
"revision" : "db51c407d3be4a051484a141bf0bff36c43d3b1e",
"version" : "1.8.0"
}
},
{
"identity" : "sourcekitten",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/SourceKitten.git",
"state" : {
"revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56",
"version" : "0.34.1"
}
},
{ {
"identity" : "swift-argument-parser", "identity" : "swift-argument-parser",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
@ -10,12 +37,39 @@
} }
}, },
{ {
"identity" : "swiftlintplugin", "identity" : "swift-syntax",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/lukepistrol/SwiftLintPlugin", "location" : "https://github.com/apple/swift-syntax.git",
"state" : { "state" : {
"revision" : "5a65f4074975f811da666dfe31a19850950b1ea4", "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036",
"version" : "0.56.2" "version" : "509.0.2"
}
},
{
"identity" : "swiftlint",
"kind" : "remoteSourceControl",
"location" : "https://github.com/realm/SwiftLint.git",
"state" : {
"revision" : "f17a4f9dfb6a6afb0408426354e4180daaf49cee",
"version" : "0.54.0"
}
},
{
"identity" : "swiftytexttable",
"kind" : "remoteSourceControl",
"location" : "https://github.com/scottrhoyt/SwiftyTextTable.git",
"state" : {
"revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3",
"version" : "0.9.0"
}
},
{
"identity" : "swxmlhash",
"kind" : "remoteSourceControl",
"location" : "https://github.com/drmohundro/SWXMLHash.git",
"state" : {
"revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f",
"version" : "7.0.2"
} }
}, },
{ {

View File

@ -10,7 +10,7 @@ let package = Package(
// Dependencies declare other packages that this package depends on. // Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"), .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.1"), .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.1"),
.package(url: "https://github.com/lukepistrol/SwiftLintPlugin", exact: "0.56.2"), .package(url: "https://github.com/realm/SwiftLint.git", .upToNextMajor(from: "0.54.0")),
], ],
targets: [ targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets are the basic building blocks of a package. A target can define a module or a test suite.
@ -22,9 +22,7 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ArgumentParser", package: "swift-argument-parser"),
"Yams" "Yams"
], ],
plugins: [ plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")]
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
]
), ),
// Helper targets // Helper targets

View File

@ -81,7 +81,7 @@ swift run -c release ResgenSwift strings twine $FORCE_FLAG "./Twine/strings.txt"
2. Input translations file (must be Twine formatted) 2. Input translations file (must be Twine formatted)
3. `--langs`: langs to generate (string with space between each lang) 3. `--langs`: langs to generate (string with space between each lang)
4. `--default-lang`: default lang that will be in `Base.lproj`. It must be in `langs` as well 4. `--default-lang`: default lang that will be in `Base.lproj`. It must be in `langs` as well
5. `--extension-output-path`: path where to generate generated extension 4. `--extension-output-path`: path where to generate generated extension
### Stringium (recommended) ### Stringium (recommended)
@ -93,7 +93,6 @@ swift run -c release ResgenSwift strings stringium $FORCE_FLAG "./Strings/string
--extension-output-path "./Strings/Generated" \ --extension-output-path "./Strings/Generated" \
--extension-name "AppString" \ --extension-name "AppString" \
--extension-suffix "GreatApp" \ --extension-suffix "GreatApp" \
--xcStrings true
--static-members true --static-members true
``` ```
@ -106,7 +105,6 @@ swift run -c release ResgenSwift strings stringium $FORCE_FLAG "./Strings/string
4. `--extension-output-path`: path where to generate generated extension 4. `--extension-output-path`: path where to generate generated extension
5. `--extension-name` *(optional)* : name of class to add the extension 5. `--extension-name` *(optional)* : name of class to add the extension
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppString+GreatApp.swift`) 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppString+GreatApp.swift`)
6. `--xcStrings`*(optional)* : generate string catalog
7. `--static-members` *(optional)*: generate static properties or not 7. `--static-members` *(optional)*: generate static properties or not
@ -133,7 +131,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`) 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppTags+GreatApp.swift`)
7. `--static-members` *(optional)*: generate static properties or not 7. `--static-members` *(optional)*: generate static properties or not
> ⚠️ If extension name is not set or is `Tags`, it will generate the following typealias `typealias Tags = String`. > ⚠️ If extension name is not set or is `Tags`, it will generate the following typaloas `typealias Tags = String`.
## Analytics ## Analytics
@ -141,7 +139,7 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
Analytics will generate all you need to analyze UX with Matomo or Firebase Analytics. Input files are formatted in YAML. This command will generate a manager for each target and an AnalyticsManager. This is this one you will need to use. And it will generate a method for all tags you have declared in the YAML file. Next, you will need to use the `configure()` method of AnalyticsManager and if you want to use matomo to set up the `siteId` and the `url` of the site. 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 ```sh
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/analytics.yml" \ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
--target "matomo firebase" \ --target "matomo firebase" \
--extension-output-path "./Analytics/Generated" \ --extension-output-path "./Analytics/Generated" \
--extension-name "AppAnalytics" \ --extension-name "AppAnalytics" \
@ -159,7 +157,7 @@ swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/analytics.yml" \
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppAnalytics+GreatApp.swift`) 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppAnalytics+GreatApp.swift`)
7. `--static-members` *(optional)*: generate static properties or not 7. `--static-members` *(optional)*: generate static properties or not
> ⚠️ If extension name is not set or is `Analytics`, it will generate the following typealias `typealias Analytics = String`. > ⚠️ If extension name is not set or is `Analytics`, it will generate the following typaloas `typealias Analytics = String`.
### YAML ### YAML
@ -186,83 +184,19 @@ swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/analytics.yml" \
7. `comments` *(optional)* 7. `comments` *(optional)*
8. `parameters` *(optional)* 8. `parameters` *(optional)*
**Parameters** **Parameters**
You can use parameters in generate methods. You can use parameters in generate methods.
1. `name`: name of the parameter 1. `name`: name of the parameter
2. `type`: type of the parameter (Int, String, Bool, Double) 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)* 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** **Replace in**
This is section is equivalent of `%s | %d | %f | %@`. You can put the content of the parameter in *name*, *path*, *action*, *category*. 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
@ -289,7 +223,6 @@ swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`) 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`)
7. `--static-members` *(optional)*: generate static properties or not 7. `--static-members` *(optional)*: generate static properties or not
> ⚠️ 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 ## All at once
@ -351,15 +284,6 @@ tags:
extensionName: String? extensionName: String?
extensionSuffix: String? extensionSuffix: String?
staticMembers: Bool? staticMembers: Bool?
analytics:
-
inputFile: String
target: String
extensionOutputPath: String
extensionName: String?
extensionSuffix: String?
staticMembers: Bool?
``` ```
### Multiple configurations ### Multiple configurations

View File

@ -6,20 +6,13 @@ import FirebaseAnalytics
// MARK: - Protocol // MARK: - Protocol
protocol AnalyticsManagerProtocol { protocol AnalyticsManagerProtocol {
func logScreen( func logScreen(name: String, path: String)
name: String,
path: String,
params: [String: Any]?
)
func logEvent( func logEvent(
name: String, name: String,
action: String, action: String,
category: String, category: String,
params: [String: Any]? params: [String: Any]?
) )
func setEnable(_ enable: Bool)
} }
// MARK: - Matomo // MARK: - Matomo
@ -54,11 +47,8 @@ class MatomoAnalyticsManager: AnalyticsManagerProtocol {
// MARK: - Methods // MARK: - Methods
func logScreen( func logScreen(name: String, path: String) {
name: String, guard !tracker.isOptedOut else { return }
path: String,
params: [String: Any]?
) {
guard let trackerUrl = tracker.contentBase?.absoluteString else { return } guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\(trackerUrl)" + "/" + "\(path)" + "iOS") let urlString = URL(string: "\(trackerUrl)" + "/" + "\(path)" + "iOS")
@ -74,6 +64,8 @@ class MatomoAnalyticsManager: AnalyticsManagerProtocol {
category: String, category: String,
params: [String: Any]? params: [String: Any]?
) { ) {
guard !tracker.isOptedOut else { return }
tracker.track( tracker.track(
eventWithCategory: category, eventWithCategory: category,
action: action, action: action,
@ -82,40 +74,16 @@ class MatomoAnalyticsManager: AnalyticsManagerProtocol {
url: nil url: nil
) )
} }
func setEnable(_ enable: Bool) {
tracker.isOptedOut = !enable
}
} }
// MARK: - Firebase // MARK: - Firebase
class FirebaseAnalyticsManager: AnalyticsManagerProtocol { class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
func logScreen( func logScreen(name: String, path: String) {
name: String,
path: String,
params: [String: Any]?
) {
var parameters = [ var parameters = [
AnalyticsParameterScreenName: name as NSObject AnalyticsParameterScreenName: name
] ]
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( Analytics.logEvent(
AnalyticsEventScreenView, AnalyticsEventScreenView,
parameters: parameters parameters: parameters
@ -128,38 +96,22 @@ class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
category: String, category: String,
params: [String: Any]? params: [String: Any]?
) { ) {
var parameters: [String:NSObject] = [ var parameters: [String:Any] = [
AnalyticsParameterItemName: name.replacingOccurrences(of: " ", with: "_") as NSObject "action": action,
"category": category,
] ]
if category.isEmpty == false {
parameters["AnalyticsParameterItemCategory"] = category as NSObject
}
if action.isEmpty == false {
parameters["action"] = action as NSObject
}
if let supplementaryParameters = params { if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters { parameters.merge(supplementaryParameters) { (origin, new) -> Any in
if parameters.contains(where: { (key: String, value: NSObject) in return origin
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
} }
} }
Analytics.logEvent( Analytics.logEvent(
AnalyticsEventSelectContent, name,
parameters: parameters parameters: parameters
) )
} }
func setEnable(_ enable: Bool) {
Analytics.setAnalyticsCollectionEnabled(enable)
}
} }
// MARK: - Manager // MARK: - Manager
@ -169,57 +121,31 @@ class AnalyticsManager {
// MARK: - Properties // MARK: - Properties
var managers: [TargetType: AnalyticsManagerProtocol] = [] var managers: [AnalyticsManagerProtocol] = []
private var isEnabled: Bool { private var isEnabled: Bool = true
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
// MARK: - Methods // MARK: - Methods
private func setAnalytics(enable: Bool) { func setAnalyticsEnabled(_ enable: Bool) {
managers.forEach { (key, value) in isEnabled = enable
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
func enableAnalytics(_ analytics: [TargetType] = TargetType.allCases) {
setAnalytics(enable: true)
}
func disableAnalytics(_ analytics: [TargetType] = TargetType.allCases) {
setAnalytics(enable: false)
} }
func configure(siteId: String, url: String) { func configure(siteId: String, url: String) {
managers[TrackerType.matomo] = MatomoAnalyticsManager( managers.append(
siteId: siteId, MatomoAnalyticsManager(
url: url siteId: siteId,
url: url
)
) )
managers[TrackerType.firebase] = FirebaseAnalyticsManager() managers.append(FirebaseAnalyticsManager())
} }
private func logScreen( private func logScreen(name: String, path: String) {
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return } guard isEnabled else { return }
managers.values.forEach { manager in managers.forEach { manager in
manager.logScreen( manager.logScreen(name: name, path: path)
name: name,
path: path,
params: params
)
} }
} }
@ -231,7 +157,7 @@ class AnalyticsManager {
) { ) {
guard isEnabled else { return } guard isEnabled else { return }
managers.values.forEach { manager in managers.forEach { manager in
manager.logEvent( manager.logEvent(
name: name, name: name,
action: action, action: action,
@ -243,39 +169,31 @@ class AnalyticsManager {
// MARK: - section_one // MARK: - section_one
static func logScreenS1DefOne(title: String) { func logScreenS1DefOne(title: String) {
AnalyticsManager.shared.logScreen( logScreen(
name: "s1 def one \(title)", name: "s1 def one \(title)",
path: "s1_def_one/\(title)", path: "s1_def_one/\(title)"
params: nil
) )
} }
static func logEventS1DefTwo( func logEventS1DefTwo(title: String, count: String) {
title: String, logEvent(
count: String,
test2: String = "test"
) {
AnalyticsManager.shared.logEvent(
name: "s1 def two", name: "s1 def two",
action: "test", action: "test",
category: "test", category: "test",
params: [ params: [
"title": title, "title": title,
"count": count, "count": count
"test": "test",
"test2": test2
] ]
) )
} }
// MARK: - section_two // MARK: - section_two
static func logScreenS2DefOne() { func logScreenS2DefOne() {
AnalyticsManager.shared.logScreen( logScreen(
name: "s2 def one", name: "s2 def one",
path: "s2_def_one/", path: "s2_def_one/"
params: nil
) )
} }
} }

View File

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

View File

@ -52,20 +52,19 @@ FORCE_FLAG="$1"
#echo "\n-------------------------\n" #echo "\n-------------------------\n"
# Analytics ## Analytics
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \ #swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \
--target "firebase matomo" \ # --target "matomo firebase" \
--extension-output-path "./Tags/Generated" \ # --extension-output-path "./Tags/Generated" \
--extension-name "Analytics" \ # --extension-name "Analytics" \
--extension-suffix "GenAllScript" \ # --extension-suffix "GenAllScript"
--static-members true
#echo "\n-------------------------\n" #echo "\n-------------------------\n"
# #
## Images # Images
#swift run -c release ResgenSwift images $FORCE_FLAG "./Images/sampleImages.txt" \ swift run -c release ResgenSwift images $FORCE_FLAG "./Images/sampleImages.txt" \
# --xcassets-path "./Images/imagium.xcassets" \ --xcassets-path "./Images/imagium.xcassets" \
# --extension-output-path "./Images/Generated" \ --extension-output-path "./Images/Generated" \
# --extension-name "ImageYolo" \ --extension-name "ImageYolo" \
# --extension-name-ui-kit "UIImageYolo" \ --extension-name-ui-kit "UIImageYolo" \
# --extension-suffix "GenAllScript" --extension-suffix "GenAllScript"

View File

@ -59,18 +59,14 @@ class AnalyticsGenerator {
\(Self.getImport()) \(Self.getImport())
\(Self.getAnalyticsProtocol()) \(Self.getAnalyticsProtocol())
\(Self.getTrackerTypeEnum())
// MARK: - Manager // MARK: - Manager
class AnalyticsManager { class AnalyticsManager {
static var shared = AnalyticsManager() static var shared = AnalyticsManager()
// MARK: - Properties // MARK: - Properties
var managers: [TrackerType: AnalyticsManagerProtocol] = [:] var managers: [AnalyticsManagerProtocol] = []
\(Self.getEnabledContent()) \(Self.getEnabledContent())
@ -80,49 +76,14 @@ class AnalyticsGenerator {
""" """
} }
private static func getTrackerTypeEnum() -> String {
var result: [String] = []
TrackerType.allCases.forEach { type in
result.append(" case \(type)")
}
return """
// MARK: - Traker Type
enum TrackerType: CaseIterable {
\(result.joined(separator: "\n"))
}
"""
}
private static func getEnabledContent() -> String { private static func getEnabledContent() -> String {
""" """
private var isEnabled: Bool { private var isEnabled: Bool = true
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
false
} else {
true
}
}
// MARK: - Methods // MARK: - Methods
private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) { func setAnalyticsEnabled(_ enable: Bool) {
managers.forEach { (key, value) in isEnabled = enable
if analytics.contains(where: { type in
type == key
}) {
value.setEnable(enable)
}
}
}
func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: true, analytics)
}
func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
setAnalytics(enable: false, analytics)
} }
""" """
} }
@ -142,19 +103,11 @@ class AnalyticsGenerator {
private static func getPrivateLogFunction() -> String { private static func getPrivateLogFunction() -> String {
""" """
private func logScreen( private func logScreen(name: String, path: String) {
name: String,
path: String,
params: [String: Any]?
) {
guard isEnabled else { return } guard isEnabled else { return }
managers.values.forEach { manager in managers.forEach { manager in
manager.logScreen( manager.logScreen(name: name, path: path)
name: name,
path: path,
params: params
)
} }
} }
@ -166,7 +119,7 @@ class AnalyticsGenerator {
) { ) {
guard isEnabled else { return } guard isEnabled else { return }
managers.values.forEach { manager in managers.forEach { manager in
manager.logEvent( manager.logEvent(
name: name, name: name,
action: action, action: action,
@ -191,14 +144,16 @@ class AnalyticsGenerator {
if targets.contains(TrackerType.matomo) { if targets.contains(TrackerType.matomo) {
content.append(""" content.append("""
managers[TrackerType.matomo] = MatomoAnalyticsManager( managers.append(
siteId: siteId, MatomoAnalyticsManager(
url: url siteId: siteId,
url: url
)
) )
""") """)
} }
if targets.contains(TrackerType.firebase) { if targets.contains(TrackerType.firebase) {
content.append(" managers[TrackerType.firebase] = FirebaseAnalyticsManager()") content.append(" managers.append(FirebaseAnalyticsManager())")
} }
return [ return [
@ -214,20 +169,13 @@ class AnalyticsGenerator {
// MARK: - Protocol // MARK: - Protocol
protocol AnalyticsManagerProtocol { protocol AnalyticsManagerProtocol {
func logScreen( func logScreen(name: String, path: String)
name: String,
path: String,
params: [String: Any]?
)
func logEvent( func logEvent(
name: String, name: String,
action: String, action: String,
category: String, category: String,
params: [String: Any]? params: [String: Any]?
) )
func setEnable(_ enable: Bool)
} }
""" """

View File

@ -14,7 +14,6 @@ enum FirebaseGenerator {
FirebaseGenerator.header, FirebaseGenerator.header,
FirebaseGenerator.logScreen, FirebaseGenerator.logScreen,
FirebaseGenerator.logEvent, FirebaseGenerator.logEvent,
FirebaseGenerator.enable,
FirebaseGenerator.footer FirebaseGenerator.footer
] ]
.joined(separator: "\n") .joined(separator: "\n")
@ -32,31 +31,11 @@ enum FirebaseGenerator {
private static var logScreen: String { private static var logScreen: String {
""" """
func logScreen( func logScreen(name: String, path: String) {
name: String,
path: String,
params: [String: Any]?
) {
var parameters = [ var parameters = [
AnalyticsParameterScreenName: name as NSObject AnalyticsParameterScreenName: name
] ]
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( Analytics.logEvent(
AnalyticsEventScreenView, AnalyticsEventScreenView,
parameters: parameters parameters: parameters
@ -74,46 +53,25 @@ enum FirebaseGenerator {
category: String, category: String,
params: [String: Any]? params: [String: Any]?
) { ) {
var parameters: [String:NSObject] = [ var parameters: [String:Any] = [
AnalyticsParameterItemName: name.replacingOccurrences(of: " ", with: "_") as NSObject "action": action,
"category": category,
] ]
if category.isEmpty == false {
parameters["AnalyticsParameterItemCategory"] = category as NSObject
}
if action.isEmpty == false {
parameters["action"] = action as NSObject
}
if let supplementaryParameters = params { if let supplementaryParameters = params {
for (newKey, newValue) in supplementaryParameters { parameters.merge(supplementaryParameters) { (origin, new) -> Any in
if parameters.contains(where: { (key: String, value: NSObject) in return origin
key == newKey
}) {
continue
}
parameters[newKey] = newValue as? NSObject
} }
} }
Analytics.logEvent( Analytics.logEvent(
AnalyticsEventSelectContent, name,
parameters: parameters parameters: parameters
) )
} }
""" """
} }
private static var enable: String {
"""
func setEnable(_ enable: Bool) {
Analytics.setAnalyticsCollectionEnabled(enable)
}
"""
}
private static var footer: String { private static var footer: String {
""" """
} }

View File

@ -15,7 +15,6 @@ enum MatomoGenerator {
MatomoGenerator.setup, MatomoGenerator.setup,
MatomoGenerator.logScreen, MatomoGenerator.logScreen,
MatomoGenerator.logEvent, MatomoGenerator.logEvent,
MatomoGenerator.enable,
MatomoGenerator.footer MatomoGenerator.footer
] ]
.joined(separator: "\n") .joined(separator: "\n")
@ -67,11 +66,8 @@ enum MatomoGenerator {
private static var logScreen: String { private static var logScreen: String {
""" """
func logScreen( func logScreen(name: String, path: String) {
name: String, guard !tracker.isOptedOut else { return }
path: String,
params: [String: Any]?
) {
guard let trackerUrl = tracker.contentBase?.absoluteString else { return } guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS") let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
@ -92,6 +88,8 @@ enum MatomoGenerator {
category: String, category: String,
params: [String: Any]? params: [String: Any]?
) { ) {
guard !tracker.isOptedOut else { return }
tracker.track( tracker.track(
eventWithCategory: category, eventWithCategory: category,
action: action, action: action,
@ -100,15 +98,6 @@ enum MatomoGenerator {
url: nil url: nil
) )
} }
"""
}
private static var enable: String {
"""
func setEnable(_ enable: Bool) {
tracker.isOptedOut = !enable
}
""" """
} }

View File

@ -48,23 +48,17 @@ class AnalyticsDefinition {
} }
private func getParameters() -> String { private func getParameters() -> String {
var params = parameters
var result: String var result: String
let paramsString = parameters.compactMap { parameter -> String? in if type == .screen {
guard parameter.value.isEmpty else { return nil } params = params.filter { param in
!param.replaceIn.isEmpty
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)" let paramsString = params.map { parameter in
return "\(parameter.name): \(parameter.type.rawValue)\(defaultValueString)" "\(parameter.name): \(parameter.type)"
} }
if paramsString.count > 2 { if paramsString.count > 2 {
@ -90,10 +84,7 @@ class AnalyticsDefinition {
case "path": path = path.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 "category": category = category.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
case "action": action = action.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))") case "action": action = action.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
default: default: break
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))")
}
} }
} }
} }
@ -108,18 +99,7 @@ class AnalyticsDefinition {
} }
supplementaryParams.forEach { param in supplementaryParams.forEach { param in
if param.value.isEmpty { params.append("\"\(param.name)\": \(param.name)")
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 { if params.count > 1 {
@ -133,15 +113,14 @@ class AnalyticsDefinition {
[\(params.joined(separator: ", "))] [\(params.joined(separator: ", "))]
""" """
} else { } else {
result = "nil" result = "[:]"
} }
if type == .screen { if type == .screen {
return """ return """
logScreen( logScreen(
name: "\(name)", name: "\(name)",
path: "\(path)", path: "\(path)"
params: \(result)
) )
""" """
} else { } else {
@ -171,7 +150,7 @@ class AnalyticsDefinition {
replaceIn() replaceIn()
return """ return """
static func \(getFuncName())\(getParameters()) { static func \(getFuncName())\(getParameters()) {
AnalyticsManager.shared.\(getlogFunction()) \(getlogFunction())
} }
""" """
} }

View File

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

View File

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

View File

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

@ -30,58 +30,25 @@ class AnalyticsFileParser {
} }
private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] { private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
func verify(value: String?, for type: ParameterType) { parameters.map { dtoParameter in
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 // Type
let type = dtoParameter.type.uppercasedFirst() let type = dtoParameter.type.uppercasedFirst()
guard let typeEnum = ParameterType(rawValue: type) else { guard
type == "String" ||
type == "Int" ||
type == "Double" ||
type == "Bool"
else {
let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)") let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)")
print(error.description) print(error.description)
Analytics.exit(withError: error) 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( let parameter = AnalyticsParameter(
name: dtoParameter.name, name: dtoParameter.name,
type: typeEnum, type: type
value: dtoParameter.value ?? "",
defaultValue: dtoParameter.defaultValue ?? ""
) )
if let replaceIn = dtoParameter.replaceIn { if let replaceIn = dtoParameter.replaceIn {
@ -136,8 +103,6 @@ class AnalyticsFileParser {
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
definition.path = path
} else if let path = screen.path {
definition.path = path definition.path = path
} }
@ -174,14 +139,6 @@ class AnalyticsFileParser {
} }
definition.action = action definition.action = action
} else {
if let category = event.category {
definition.category = category
}
if let action = event.action {
definition.action = action
}
} }
return definition return definition

View File

@ -9,7 +9,7 @@ import Foundation
enum GenerateError: Error { enum GenerateError: Error {
case fileNotExists(String) case fileNotExists(String)
case invalidConfigurationFile(String, String) case invalidConfigurationFile(String)
case commandError([String], String) case commandError([String], String)
case writeFile(String, String) case writeFile(String, String)
@ -18,8 +18,8 @@ enum GenerateError: Error {
case .fileNotExists(let filename): case .fileNotExists(let filename):
return "error: [\(Generate.toolName)] File \(filename) does not exists" return "error: [\(Generate.toolName)] File \(filename) does not exists"
case .invalidConfigurationFile(let filename, let underneathErrorDescription): case .invalidConfigurationFile(let filename):
return "error: [\(Generate.toolName)] File \(filename) is not a valid configuration file. Underneath error: \(underneathErrorDescription)" return "error: [\(Generate.toolName)] File \(filename) is not a valid configuration file"
case .commandError(let command, let terminationStatus): case .commandError(let command, let terminationStatus):
let readableCommand = command let readableCommand = command

View File

@ -269,7 +269,6 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible {
let extensionName: String? let extensionName: String?
let extensionSuffix: String? let extensionSuffix: String?
private let staticMembers: Bool? private let staticMembers: Bool?
private let xcStrings: Bool?
var staticMembersOptions: Bool { var staticMembersOptions: Bool {
if let staticMembers = staticMembers { if let staticMembers = staticMembers {
@ -278,13 +277,6 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible {
return false return false
} }
var xcStringsOptions: Bool {
if let xcStrings = xcStrings {
return xcStrings
}
return false
}
internal init(inputFile: String, internal init(inputFile: String,
outputPath: String, outputPath: String,
langs: String, langs: String,
@ -292,8 +284,7 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible {
extensionOutputPath: String, extensionOutputPath: String,
extensionName: String?, extensionName: String?,
extensionSuffix: String?, extensionSuffix: String?,
staticMembers: Bool?, staticMembers: Bool?) {
xcStrings: Bool?) {
self.inputFile = inputFile self.inputFile = inputFile
self.outputPath = outputPath self.outputPath = outputPath
self.langs = langs self.langs = langs
@ -302,7 +293,6 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible {
self.extensionName = extensionName self.extensionName = extensionName
self.extensionSuffix = extensionSuffix self.extensionSuffix = extensionSuffix
self.staticMembers = staticMembers self.staticMembers = staticMembers
self.xcStrings = xcStrings
} }
var debugDescription: String { var debugDescription: String {

View File

@ -16,15 +16,12 @@ class ConfigurationFileParser {
Generate.exit(withError: error) Generate.exit(withError: error)
} }
do { guard let configuration = try? YAMLDecoder().decode(ConfigurationFile.self, from: data) else {
return try YAMLDecoder().decode(ConfigurationFile.self, from: data) let error = GenerateError.invalidConfigurationFile(configurationFile)
} catch {
let error = GenerateError.invalidConfigurationFile(
configurationFile,
error.localizedDescription.description
)
print(error.description) print(error.description)
Generate.exit(withError: error) Generate.exit(withError: error)
} }
return configuration
} }
} }

View File

@ -26,9 +26,7 @@ extension StringsConfiguration: Runnable {
"--extension-output-path", "--extension-output-path",
extensionOutputPath.prependIfRelativePath(projectDirectory), extensionOutputPath.prependIfRelativePath(projectDirectory),
"--static-members", "--static-members",
"\(staticMembersOptions)", "\(staticMembersOptions)"
"--xc-strings",
"\(xcStringsOptions)"
] ]
if let extensionName = extensionName { if let extensionName = extensionName {

View File

@ -8,13 +8,10 @@
import Foundation import Foundation
import ToolCore import ToolCore
enum OutputImageExtension: String {
case png
case svg
}
class XcassetsGenerator { class XcassetsGenerator {
static let outputImageExtension = "png"
let forceGeneration: Bool let forceGeneration: Bool
// MARK: - Init // MARK: - Init
@ -63,20 +60,13 @@ class XcassetsGenerator {
generatedAssetsPaths.append(imagesetName) generatedAssetsPaths.append(imagesetName)
// Generate output images path // Generate output images path
let output1x = "\(imagesetPath)/\(parsedImage.name).\(OutputImageExtension.png.rawValue)" let output1x = "\(imagesetPath)/\(parsedImage.name).\(XcassetsGenerator.outputImageExtension)"
let output2x = "\(imagesetPath)/\(parsedImage.name)@2x.\(OutputImageExtension.png.rawValue)" let output2x = "\(imagesetPath)/\(parsedImage.name)@2x.\(XcassetsGenerator.outputImageExtension)"
let output3x = "\(imagesetPath)/\(parsedImage.name)@3x.\(OutputImageExtension.png.rawValue)" let output3x = "\(imagesetPath)/\(parsedImage.name)@3x.\(XcassetsGenerator.outputImageExtension)"
// Check if we need to convert image // Check if we need to convert image
guard self.shouldGenerate(inputImagePath: imageData.path, xcassetImagePath: output1x) else {
var needToGenerateForSvg = false //print("\(parsedImage.name) -> Not regenerating")
if imageData.ext == "svg" && !parsedImage.imageExtensions.contains(.png) {
needToGenerateForSvg = true
}
guard self.shouldGenerate(inputImagePath: imageData.path, xcassetImagePath: output1x, needToGenerateForSvg: needToGenerateForSvg) else {
print("\(parsedImage.name) -> Not regenerating")
return return
} }
@ -90,55 +80,29 @@ class XcassetsGenerator {
print(error.description) print(error.description)
Images.exit(withError: error) Images.exit(withError: error)
} }
} else {
do {
let documentsDirectory = try fileManager.contentsOfDirectory(atPath: imagesetPath)
for filePath in documentsDirectory {
try fileManager.removeItem(atPath: "\(imagesetPath)/\(filePath)")
}
} catch {
print("Error deleting previous assets")
}
} }
// Convert image
let convertArguments = parsedImage.convertArguments let convertArguments = parsedImage.convertArguments
if imageData.ext == "svg" { if imageData.ext == "svg" {
if parsedImage.imageExtensions.contains(.png) { // /usr/local/bin/rsvg-convert path/to/image.png -w 200 -h 300 -o path/to/output.png
// /usr/local/bin/rsvg-convert path/to/image.png -w 200 -o path/to/output.png
// /usr/local/bin/rsvg-convert path/to/image.png -h 300 -o path/to/output.png
var command1x = ["\(svgConverter)", "\(imageData.path)"]
var command2x = ["\(svgConverter)", "\(imageData.path)"]
var command3x = ["\(svgConverter)", "\(imageData.path)"]
// /usr/local/bin/rsvg-convert path/to/image.png -w 200 -h 300 -o path/to/output.png self.addConvertArgument(command: &command1x, convertArgument: convertArguments.x1)
// /usr/local/bin/rsvg-convert path/to/image.png -w 200 -o path/to/output.png self.addConvertArgument(command: &command2x, convertArgument: convertArguments.x2)
// /usr/local/bin/rsvg-convert path/to/image.png -h 300 -o path/to/output.png self.addConvertArgument(command: &command3x, convertArgument: convertArguments.x3)
var command1x = ["\(svgConverter)", "\(imageData.path)"]
var command2x = ["\(svgConverter)", "\(imageData.path)"]
var command3x = ["\(svgConverter)", "\(imageData.path)"]
self.addConvertArgument(command: &command1x, convertArgument: convertArguments.x1) command1x.append(contentsOf: ["-o", output1x])
self.addConvertArgument(command: &command2x, convertArgument: convertArguments.x2) command2x.append(contentsOf: ["-o", output2x])
self.addConvertArgument(command: &command3x, convertArgument: convertArguments.x3) command3x.append(contentsOf: ["-o", output3x])
command1x.append(contentsOf: ["-o", output1x]) Shell.shell(command1x)
command2x.append(contentsOf: ["-o", output2x]) Shell.shell(command2x)
command3x.append(contentsOf: ["-o", output3x]) Shell.shell(command3x)
Shell.shell(command1x)
Shell.shell(command2x)
Shell.shell(command3x)
} else {
let output = "\(imagesetPath)/\(parsedImage.name).\(OutputImageExtension.svg.rawValue)"
let tempURL = URL(fileURLWithPath: output)
do {
if FileManager.default.fileExists(atPath: tempURL.path) {
try FileManager.default.removeItem(atPath: tempURL.path)
}
try FileManager.default.copyItem(atPath: imageData.path, toPath: tempURL.path)
} catch {
print(error.localizedDescription)
}
}
} else { } else {
// convert path/to/image.png -resize 200x300 path/to/output.png // convert path/to/image.png -resize 200x300 path/to/output.png
// convert path/to/image.png -resize 200x path/to/output.png // convert path/to/image.png -resize 200x path/to/output.png
@ -155,7 +119,7 @@ class XcassetsGenerator {
} }
// Write Content.json // Write Content.json
guard let imagesetContentJson = parsedImage.generateContentJson(isVector: imageData.ext == "svg") else { return } let imagesetContentJson = parsedImage.contentJson
let contentJsonFilePath = "\(imagesetPath)/Contents.json" let contentJsonFilePath = "\(imagesetPath)/Contents.json"
let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath) let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
@ -197,8 +161,8 @@ class XcassetsGenerator {
// MARK: - Helpers: bypass generation // MARK: - Helpers: bypass generation
private func shouldGenerate(inputImagePath: String, xcassetImagePath: String, needToGenerateForSvg: Bool) -> Bool { private func shouldGenerate(inputImagePath: String, xcassetImagePath: String) -> Bool {
if forceGeneration || needToGenerateForSvg { if forceGeneration {
return true return true
} }

View File

@ -1,76 +0,0 @@
//
// ImageContent.swift
//
//
// Created by Quentin Bandera on 19/04/2024.
//
import Foundation
enum TemplateRenderingIntent: String, Codable {
case template
case original
}
struct AssetContent: Codable, Equatable {
let images: [AssetImageDescription]
let info: AssetInfo
let properties: AssetProperties?
init(
images: [AssetImageDescription],
info: AssetInfo,
properties: AssetProperties? = nil
) {
self.images = images
self.info = info
self.properties = properties
}
static func == (lhs: AssetContent, rhs: AssetContent) -> Bool {
guard lhs.images.count == rhs.images.count else { return false }
let lhsImages = lhs.images.sorted(by: { $0.filename < $1.filename })
let rhsImages = rhs.images.sorted(by: { $0.filename < $1.filename })
return lhsImages == rhsImages
}
}
struct AssetImageDescription: Codable, Equatable {
let idiom: String
let scale: String?
let filename: String
init(
idiom: String,
scale: String? = nil,
filename: String
) {
self.idiom = idiom
self.scale = scale
self.filename = filename
}
}
struct AssetInfo: Codable, Equatable {
let version: Int
let author: String
}
struct AssetProperties: Codable, Equatable {
let preservesVectorRepresentation: Bool
let templateRenderingIntent: TemplateRenderingIntent?
init(
preservesVectorRepresentation: Bool,
templateRenderingIntent: TemplateRenderingIntent? = nil
) {
self.preservesVectorRepresentation = preservesVectorRepresentation
self.templateRenderingIntent = templateRenderingIntent
}
enum CodingKeys: String, CodingKey {
case preservesVectorRepresentation = "preserves-vector-representation"
case templateRenderingIntent = "template-rendering-intent"
}
}

View File

@ -7,30 +7,11 @@
import Foundation import Foundation
enum ImageExtension: String {
case png
}
struct ParsedImage { struct ParsedImage {
let name: String let name: String
let tags: String let tags: String
let width: Int let width: Int
let height: Int let height: Int
let imageExtensions: [ImageExtension]
init(
name: String,
tags: String,
width: Int,
height: Int,
imageExtensions: [ImageExtension] = []
) {
self.name = name
self.tags = tags
self.width = width
self.height = height
self.imageExtensions = imageExtensions
}
// MARK: - Convert // MARK: - Convert
@ -61,67 +42,32 @@ struct ParsedImage {
// MARK: - Assets // MARK: - Assets
func generateContentJson(isVector: Bool) -> String? { var contentJson: String {
let encoder = JSONEncoder() """
encoder.outputFormatting = .prettyPrinted {
"images" : [
let imageContent = generateImageContent(isVector: isVector) {
"idiom" : "universal",
guard let data = try? encoder.encode(imageContent) else { "scale" : "1x",
let error = ImagesError.writeFile("Contents.json", "Error encoding json file") "filename" : "\(name).\(XcassetsGenerator.outputImageExtension)"
print(error.description) },
Images.exit(withError: error) {
} "idiom" : "universal",
"scale" : "2x",
return String(data: data, encoding: .utf8) "filename" : "\(name)@2x.\(XcassetsGenerator.outputImageExtension)"
} },
{
func generateImageContent(isVector: Bool) -> AssetContent { "idiom" : "universal",
"scale" : "3x",
if !imageExtensions.contains(.png) && isVector { "filename" : "\(name)@3x.\(XcassetsGenerator.outputImageExtension)"
//Generate svg }
return AssetContent( ],
images: [ "info" : {
AssetImageDescription( "version" : 1,
idiom: "universal", "author" : "ResgenSwift-Imagium"
filename: "\(name).\(OutputImageExtension.svg.rawValue)" }
)
],
info: AssetInfo(
version: 1,
author: "ResgenSwift-Imagium"
),
properties: AssetProperties(
preservesVectorRepresentation: true,
templateRenderingIntent: .original
)
)
} else {
//Generate png
return AssetContent(
images: [
AssetImageDescription(
idiom: "universal",
scale: "1x",
filename: "\(name).\(OutputImageExtension.png.rawValue)"
),
AssetImageDescription(
idiom: "universal",
scale: "2x",
filename: "\(name)@2x.\(OutputImageExtension.png.rawValue)"
),
AssetImageDescription(
idiom: "universal",
scale: "3x",
filename: "\(name)@3x.\(OutputImageExtension.png.rawValue)"
)
],
info: AssetInfo(
version: 1,
author: "ResgenSwift-Imagium"
)
)
} }
"""
} }
// MARK: - Extension property // MARK: - Extension property

View File

@ -39,20 +39,10 @@ class ImageFileParser {
return Int(splittedLine[3])! return Int(splittedLine[3])!
}() }()
var imageExtensions: [ImageExtension] = [] let image = ParsedImage(name: String(splittedLine[1]), tags: String(splittedLine[0]), width: width, height: height)
splittedLine.forEach { stringExtension in
if let imageExtension = ImageExtension(rawValue: String(stringExtension)) {
imageExtensions.append(imageExtension)
}
}
let image = ParsedImage(name: String(splittedLine[1]), tags: String(splittedLine[0]), width: width, height: height, imageExtensions: imageExtensions)
imagesToGenerate.append(image) imagesToGenerate.append(image)
} }
print(imagesToGenerate)
return imagesToGenerate.filter { return imagesToGenerate.filter {
$0.tags.contains(platform.rawValue) $0.tags.contains(platform.rawValue)
} }

View File

@ -18,7 +18,6 @@ class StringsFileGenerator {
tags: [String], tags: [String],
outputPath: String, outputPath: String,
inputFilenameWithoutExt: String) { inputFilenameWithoutExt: String) {
var stringsFilesContent = [String: String]() var stringsFilesContent = [String: String]()
for lang in langs { for lang in langs {
stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang, stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang,
@ -43,31 +42,6 @@ class StringsFileGenerator {
} }
} }
static func writeXcStringsFiles(sections: [Section],
langs: [String],
defaultLang: String,
tags: [String],
outputPath: String,
inputFilenameWithoutExt: String) {
let fileContent: String = Self.generateXcStringsFileContent(
langs: langs,
defaultLang: defaultLang,
tags: tags,
sections: sections
)
let stringsFilePath = "\(outputPath)/\(inputFilenameWithoutExt).xcstrings"
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
do {
try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8)
} catch let error {
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
print(error.description)
Stringium.exit(withError: error)
}
}
static func generateStringsFileContent(lang: String, static func generateStringsFileContent(lang: String,
defaultLang: String, defaultLang: String,
tags inputTags: [String], tags inputTags: [String],
@ -118,117 +92,6 @@ class StringsFileGenerator {
return stringsFileContent return stringsFileContent
} }
// MARK: - XcStrings Generation
static func generateXcStringsFileContent(langs: [String],
defaultLang: String,
tags inputTags: [String],
sections: [Section]) -> String {
let rootObject = generateRootObject(langs: langs, defaultLang: defaultLang, tags: inputTags, sections: sections)
let file = generateXcStringsFileContentFromRootObject(rootObject: rootObject)
return file
}
static func generateXcStringsFileContentFromRootObject(rootObject: Root) -> String {
do {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted]
let json = try encoder.encode(rootObject)
if let jsonString = String(data: json, encoding: .utf8) {
return jsonString
}
} catch {
debugPrint("Failed to encode: \(error)")
}
return ""
}
static func generateRootObject(langs: [String],
defaultLang: String,
tags inputTags: [String],
sections: [Section]) -> Root {
var xcStringDefinitionTab: [XCStringDefinition] = []
sections.forEach { section in
// Check that at least one string will be generated
guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
return // Go to next section
}
section.definitions.forEach { definition in
var skipDefinition = false
var isNoTranslation = false
var localizationTab: [XCStringLocalization] = []
if definition.hasOneOrMoreMatchingTags(inputTags: inputTags) == false {
skipDefinition = true
}
if definition.tags.contains(Stringium.noTranslationTag) {
isNoTranslation = true
}
if !skipDefinition {
if isNoTranslation {
// Search for langs in yaml
for lang in langs {
if let value = definition.translations[defaultLang], !value.isEmpty {
let localization = XCStringLocalization(
lang: lang,
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(state: "translated", value: value)
)
)
localizationTab.append(localization)
}
}
} else {
// Search for langs in twine
for (lang, value) in definition.translations where !value.isEmpty {
let localization = XCStringLocalization(
lang: lang,
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(state: "translated", value: value)
)
)
localizationTab.append(localization)
}
}
let xcStringDefinition = XCStringDefinition(
title: definition.name,
content: XCStringDefinitionContent(
comment: definition.comment,
extractionState: "manual",
localizations: XCStringLocalizationContainer(
localizations: localizationTab
)
)
)
xcStringDefinitionTab.append(xcStringDefinition)
}
}
}
let xcStringContainer = XCStringDefinitionContainer(strings: xcStringDefinitionTab)
return Root(
sourceLanguage: defaultLang,
strings: xcStringContainer,
version: "1.0"
)
}
// MARK: - Extension file // MARK: - Extension file
static func writeExtensionFiles(sections: [Section], static func writeExtensionFiles(sections: [Section],

View File

@ -84,29 +84,22 @@ class Definition {
return (inputParameters: inputParameters, translationArguments: translationArguments) 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) -> String {
""" """
/// Translation in \(lang) : /// Translation in \(lang) :
/// \(translation) /// \(translation)
///
/// Comment :
/// \(comment?.isEmpty == false ? comment! : "No comment")
\(isStatic ? "static ": "")var \(name): String { \(isStatic ? "static ": "")var \(name): String {
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "\(comment ?? "")") NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
} }
""" """
} }
private func getBaseMethod(lang: String, translation: String, isStatic: Bool, inputParameters: [String], translationArguments: [String], comment: String?) -> String { private func getBaseMethod(lang: String, translation: String, isStatic: Bool, inputParameters: [String], translationArguments: [String]) -> String {
""" """
/// Translation in \(lang) : /// Translation in \(lang) :
/// \(translation) /// \(translation)
///
/// Comment :
/// \(comment?.isEmpty == false ? comment! : "No comment")
\(isStatic ? "static ": "")func \(name)(\(inputParameters.joined(separator: ", "))) -> String { \(isStatic ? "static ": "")func \(name)(\(inputParameters.joined(separator: ", "))) -> String {
String(format: \(isStatic ? "Self" : "self").\(name), \(translationArguments.joined(separator: ", "))) String(format: \(isStatic ? "Self" : "self").\(name), \(translationArguments.joined(separator: ", ")))
} }
@ -121,12 +114,7 @@ class Definition {
} }
// Generate property // Generate property
let property = getBaseProperty( let property = getBaseProperty(lang: lang, translation: translation, isStatic: false)
lang: lang,
translation: translation,
isStatic: false,
comment: self.comment
)
// Generate method // Generate method
var method = "" var method = ""
@ -135,8 +123,7 @@ class Definition {
translation: translation, translation: translation,
isStatic: false, isStatic: false,
inputParameters: parameters.inputParameters, inputParameters: parameters.inputParameters,
translationArguments: parameters.translationArguments, translationArguments: parameters.translationArguments)
comment: self.comment)
} }
return property + method return property + method
@ -150,12 +137,7 @@ class Definition {
} }
// Generate property // Generate property
let property = getBaseProperty( let property = getBaseProperty(lang: lang, translation: translation, isStatic: true)
lang: lang,
translation: translation,
isStatic: true,
comment: self.comment
)
// Generate method // Generate method
var method = "" var method = ""
@ -164,8 +146,7 @@ class Definition {
translation: translation, translation: translation,
isStatic: true, isStatic: true,
inputParameters: parameters.inputParameters, inputParameters: parameters.inputParameters,
translationArguments: parameters.translationArguments, translationArguments: parameters.translationArguments)
comment: self.comment)
} }
return property + method return property + method
@ -183,10 +164,6 @@ class Definition {
return """ return """
/// Translation in \(lang) : /// Translation in \(lang) :
/// \(translation) /// \(translation)
///
/// Comment :
/// \(comment?.isEmpty == false ? comment! : "No comment")
var \(name): String { var \(name): String {
"\(translation)" "\(translation)"
} }
@ -203,9 +180,6 @@ class Definition {
return """ return """
/// Translation in \(lang) : /// Translation in \(lang) :
/// \(translation) /// \(translation)
///
/// Comment :
/// \(comment?.isEmpty == false ? comment! : "No comment")
static var \(name): String { static var \(name): String {
"\(translation)" "\(translation)"
} }

View File

@ -1,109 +0,0 @@
//
// XcString.swift
//
//
// Created by Quentin Bandera on 12/04/2024.
//
import SwiftUI
struct DynamicKey: CodingKey {
var intValue: Int?
init?(intValue: Int) {
self.intValue = intValue
self.stringValue = "\(intValue)"
}
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
}
struct Root: Codable, Equatable {
let sourceLanguage: String
let strings: XCStringDefinitionContainer
let version: String
}
struct XCStringDefinitionContainer: Codable, Equatable {
let strings: [XCStringDefinition]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: DynamicKey.self)
for str in strings {
if let codingKey = DynamicKey(stringValue: str.title) {
try container.encode(str.content, forKey: codingKey)
}
}
}
static func == (lhs: XCStringDefinitionContainer, rhs: XCStringDefinitionContainer) -> Bool {
return lhs.strings.sorted(by: {
$0.title < $1.title
}) == rhs.strings.sorted(by: { $0.title < $1.title })
}
}
struct XCStringDefinition: Codable, Equatable {
let title: String // json key -> custom encoding methods
let content: XCStringDefinitionContent
}
struct XCStringDefinitionContent: Codable, Equatable {
let comment: String?
let extractionState: String
var localizations: XCStringLocalizationContainer
init(comment: String? = nil, extractionState: String, localizations: XCStringLocalizationContainer) {
self.comment = comment
self.extractionState = extractionState
self.localizations = localizations
}
}
struct XCStringLocalizationContainer: Codable, Equatable {
let localizations: [XCStringLocalization]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: DynamicKey.self)
for loca in localizations {
if let codingKey = DynamicKey(stringValue: loca.lang) {
try container.encode(loca.content, forKey: codingKey)
}
}
}
static func == (lhs: XCStringLocalizationContainer, rhs: XCStringLocalizationContainer) -> Bool {
return lhs.localizations.count == rhs.localizations.count && lhs.localizations.sorted(by: { $0.lang < $1.lang }) == rhs.localizations.sorted(by: { $0.lang < $1.lang })
}
}
struct XCStringLocalization: Codable, Equatable {
let lang: String // json key -> custom encoding method
let content: XCStringLocalizationLangContent
}
struct XCStringLocalizationLangContent: Codable, Equatable {
let stringUnit: DefaultStringUnit
}
//enum VarationOrStringUnit: Encodable {
// case variations([Varation])
// case stringUnit: (DefaultStringUnit)
//
// func encode(to encoder: any Encoder) throws {
// if let varations {
//
// } else if let {
//
// }
// }
//}
struct DefaultStringUnit: Codable, Equatable {
let state: String
let value: String
}

View File

@ -43,25 +43,12 @@ struct Stringium: ParsableCommand {
let sections = TwineFileParser.parse(options.inputFile) let sections = TwineFileParser.parse(options.inputFile)
// Generate strings files // Generate strings files
print(options.xcStrings) StringsFileGenerator.writeStringsFiles(sections: sections,
if !options.xcStrings { langs: options.langs,
print("[\(Self.toolName)] Will generate strings") defaultLang: options.defaultLang,
tags: options.tags,
StringsFileGenerator.writeStringsFiles(sections: sections, outputPath: options.stringsFileOutputPath,
langs: options.langs, inputFilenameWithoutExt: options.inputFilenameWithoutExt)
defaultLang: options.defaultLang,
tags: options.tags,
outputPath: options.stringsFileOutputPath,
inputFilenameWithoutExt: options.inputFilenameWithoutExt)
} else {
print("[\(Self.toolName)] Will generate xcStrings")
StringsFileGenerator.writeXcStringsFiles(sections: sections,
langs: options.langs,
defaultLang: options.defaultLang,
tags: options.tags,
outputPath: options.stringsFileOutputPath,
inputFilenameWithoutExt: options.inputFilenameWithoutExt)
}
// Generate extension // Generate extension
StringsFileGenerator.writeExtensionFiles(sections: sections, StringsFileGenerator.writeExtensionFiles(sections: sections,

View File

@ -12,7 +12,7 @@ struct StringiumOptions: ParsableArguments {
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation") @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
var forceGeneration = false var forceGeneration = false
@Argument(help: "Input files where strings are defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) @Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var inputFile: String 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 strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
@ -33,9 +33,6 @@ struct StringiumOptions: ParsableArguments {
@Option(help: "Tell if it will generate static properties or not") @Option(help: "Tell if it will generate static properties or not")
var staticMembers: Bool = false var staticMembers: Bool = false
@Option(help: "Tell if it will generate xcStrings file or not")
var xcStrings: Bool = false
@Option(help: "Extension name. If not specified, it will generate an String extension.") @Option(help: "Extension name. If not specified, it will generate an String extension.")
var extensionName: String = Stringium.defaultExtensionName var extensionName: String = Stringium.defaultExtensionName

View File

@ -91,7 +91,7 @@ final class ParsedColorTests: XCTestCase {
// When // When
let contentJson = color.contentsJSON() let contentJson = color.contentsJSON()
guard let data = contentJson.data(using: .utf8), guard let data = contentJson.data(using: .utf8),
let parsedJson = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { let parsedJson = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
XCTFail("Cannot convert `contentJSON` string to Data") XCTFail("Cannot convert `contentJSON` string to Data")
return return
} }

View File

@ -17,8 +17,8 @@ class ImageFileParserTests: XCTestCase {
# #
# SMAAS Support # SMAAS Support
# #
id image_one 25 ? png id image_one 25 ?
di image_two ? 50 webp png di image_two ? 50
d image_three 25 ? d image_three 25 ?
d image_four 75 ? d image_four 75 ?
""" """
@ -38,7 +38,6 @@ class ImageFileParserTests: XCTestCase {
XCTAssertEqual(firstImage!.tags, "id") XCTAssertEqual(firstImage!.tags, "id")
XCTAssertEqual(firstImage!.width, 25) XCTAssertEqual(firstImage!.width, 25)
XCTAssertEqual(firstImage!.height, -1) XCTAssertEqual(firstImage!.height, -1)
XCTAssertEqual(firstImage!.imageExtensions, [.png])
let secondImage = parsedImages.first { let secondImage = parsedImages.first {
$0.name == "image_two" $0.name == "image_two"
@ -47,6 +46,5 @@ class ImageFileParserTests: XCTestCase {
XCTAssertEqual(secondImage!.tags, "di") XCTAssertEqual(secondImage!.tags, "di")
XCTAssertEqual(secondImage!.width, -1) XCTAssertEqual(secondImage!.width, -1)
XCTAssertEqual(secondImage!.height, 50) XCTAssertEqual(secondImage!.height, 50)
XCTAssertEqual(firstImage!.imageExtensions, [.png])
} }
} }

View File

@ -127,77 +127,35 @@ final class ParsedImageTests: XCTestCase {
height: 10) height: 10)
// When // When
let property = parsedImage.generateImageContent(isVector: false) let property = parsedImage.contentJson
// Expect // Expect
let expect = AssetContent( let expect = """
images: [ {
AssetImageDescription( "images" : [
idiom: "universal", {
scale: "1x", "idiom" : "universal",
filename: "\(parsedImage.name).\(OutputImageExtension.png.rawValue)" "scale" : "1x",
), "filename" : "\(imageName).\(XcassetsGenerator.outputImageExtension)"
AssetImageDescription( },
idiom: "universal", {
scale: "2x", "idiom" : "universal",
filename: "\(parsedImage.name)@2x.\(OutputImageExtension.png.rawValue)" "scale" : "2x",
), "filename" : "\(imageName)@2x.\(XcassetsGenerator.outputImageExtension)"
AssetImageDescription( },
idiom: "universal", {
scale: "3x", "idiom" : "universal",
filename: "\(parsedImage.name)@3x.\(OutputImageExtension.png.rawValue)" "scale" : "3x",
) "filename" : "\(imageName)@3x.\(XcassetsGenerator.outputImageExtension)"
], }
info: AssetInfo( ],
version: 1, "info" : {
author: "ResgenSwift-Imagium" "version" : 1,
) "author" : "ResgenSwift-Imagium"
) }
}
"""
XCTAssertEqual(property, expect) XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
}
func testAssetPng() {
// Given
let imageName = "the_name"
let parsedImage = ParsedImage(name: imageName,
tags: "id",
width: 10,
height: 10,
imageExtensions: [.png])
// When
let property = parsedImage.generateImageContent(isVector: false)
// Expect
let expect = AssetContent(
images: [
AssetImageDescription(
idiom: "universal",
scale: "1x",
filename: "\(parsedImage.name).\(OutputImageExtension.png.rawValue)"
),
AssetImageDescription(
idiom: "universal",
scale: "2x",
filename: "\(parsedImage.name)@2x.\(OutputImageExtension.png.rawValue)"
),
AssetImageDescription(
idiom: "universal",
scale: "3x",
filename: "\(parsedImage.name)@3x.\(OutputImageExtension.png.rawValue)"
)
],
info: AssetInfo(
version: 1,
author: "ResgenSwift-Imagium"
)
)
debugPrint(property)
debugPrint(expect)
XCTAssertEqual(property, expect)
} }
} }

View File

@ -100,64 +100,6 @@ final class DefinitionTests: XCTestCase {
let expectFr = """ let expectFr = """
/// Translation in fr : /// Translation in fr :
/// C'est la traduction francaise /// C'est la traduction francaise
///
/// Comment :
/// This is a comment
var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "This is a comment")
}
"""
let expectEn = """
/// Translation in en :
/// This is the english translation
///
/// Comment :
/// This is a comment
var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "This is a comment")
}
"""
let expectEnUs = """
/// Translation in en-us :
/// This is the english us translation
///
/// Comment :
/// This is a comment
var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "This is a comment")
}
"""
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
}
func testGeneratedNSLocalizedStringPropertyWithEmptyComment() {
// Given
let definition = Definition(name: "definition_name")
definition.tags = ["ios","iosonly","notranslation"]
definition.comment = ""
definition.translations = [
"fr": "C'est la traduction francaise",
"en": "This is the english translation",
"en-us": "This is the english us translation"
]
// When
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us")
// Expect
let expectFr = """
/// Translation in fr :
/// C'est la traduction francaise
///
/// Comment :
/// No comment
var definition_name: String { var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
} }
@ -166,9 +108,6 @@ final class DefinitionTests: XCTestCase {
let expectEn = """ let expectEn = """
/// Translation in en : /// Translation in en :
/// This is the english translation /// This is the english translation
///
/// Comment :
/// No comment
var definition_name: String { var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
} }
@ -177,9 +116,6 @@ final class DefinitionTests: XCTestCase {
let expectEnUs = """ let expectEnUs = """
/// Translation in en-us : /// Translation in en-us :
/// This is the english us translation /// This is the english us translation
///
/// Comment :
/// No comment
var definition_name: String { var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
} }
@ -190,62 +126,6 @@ final class DefinitionTests: XCTestCase {
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest()) XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
} }
func testGeneratedNSLocalizedStringPropertyWithNoComment() {
// Given
let definition = Definition(name: "definition_name")
definition.tags = ["ios","iosonly","notranslation"]
definition.translations = [
"fr": "C'est la traduction francaise",
"en": "This is the english translation",
"en-us": "This is the english us translation"
]
// When
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us")
// Expect
let expectFr = """
/// Translation in fr :
/// C'est la traduction francaise
///
/// Comment :
/// No comment
var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
}
"""
let expectEn = """
/// Translation in en :
/// This is the english translation
///
/// Comment :
/// No comment
var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
}
"""
let expectEnUs = """
/// Translation in en-us :
/// This is the english us translation
///
/// Comment :
/// No comment
var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
}
"""
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
}
// MARK: - getNSLocalizedStringStaticProperty
func testGeneratedNSLocalizedStringStaticProperty() { func testGeneratedNSLocalizedStringStaticProperty() {
// Given // Given
let definition = Definition(name: "definition_name") let definition = Definition(name: "definition_name")
@ -266,64 +146,6 @@ final class DefinitionTests: XCTestCase {
let expectFr = """ let expectFr = """
/// Translation in fr : /// Translation in fr :
/// C'est la traduction francaise /// C'est la traduction francaise
///
/// Comment :
/// This is a comment
static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "This is a comment")
}
"""
let expectEn = """
/// Translation in en :
/// This is the english translation
///
/// Comment :
/// This is a comment
static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "This is a comment")
}
"""
let expectEnUs = """
/// Translation in en-us :
/// This is the english us translation
///
/// Comment :
/// This is a comment
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")
}
"""
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
}
func testGeneratedNSLocalizedStringStaticPropertyWithEmptyComment() {
// Given
let definition = Definition(name: "definition_name")
definition.tags = ["ios","iosonly","notranslation"]
definition.comment = ""
definition.translations = [
"fr": "C'est la traduction francaise",
"en": "This is the english translation",
"en-us": "This is the english us translation"
]
// When
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us")
// Expect
let expectFr = """
/// Translation in fr :
/// C'est la traduction francaise
///
/// Comment :
/// No comment
static var definition_name: String { static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
} }
@ -332,9 +154,6 @@ final class DefinitionTests: XCTestCase {
let expectEn = """ let expectEn = """
/// Translation in en : /// Translation in en :
/// This is the english translation /// This is the english translation
///
/// Comment :
/// No comment
static var definition_name: String { static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
} }
@ -343,63 +162,6 @@ final class DefinitionTests: XCTestCase {
let expectEnUs = """ let expectEnUs = """
/// Translation in en-us : /// Translation in en-us :
/// This is the english us translation /// This is the english us translation
///
/// Comment :
/// No comment
static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
}
"""
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
}
func testGeneratedNSLocalizedStringStaticPropertyWithNoComment() {
// Given
let definition = Definition(name: "definition_name")
definition.tags = ["ios","iosonly","notranslation"]
definition.translations = [
"fr": "C'est la traduction francaise",
"en": "This is the english translation",
"en-us": "This is the english us translation"
]
// When
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr")
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en")
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us")
// Expect
let expectFr = """
/// Translation in fr :
/// C'est la traduction francaise
///
/// Comment :
/// No comment
static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
}
"""
let expectEn = """
/// Translation in en :
/// This is the english translation
///
/// Comment :
/// No comment
static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
}
"""
let expectEnUs = """
/// Translation in en-us :
/// This is the english us translation
///
/// Comment :
/// No comment
static var definition_name: String { static var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
} }
@ -426,18 +188,12 @@ final class DefinitionTests: XCTestCase {
let expectFr = """ let expectFr = """
/// Translation in fr : /// Translation in fr :
/// Welcome "%@" ! /// Welcome "%@" !
///
/// Comment :
/// This is a comment
var definition_name: String { var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "This is a comment") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "")
} }
/// Translation in fr : /// Translation in fr :
/// Welcome "%@" ! /// Welcome "%@" !
///
/// Comment :
/// This is a comment
func definition_name(arg0: String) -> String { func definition_name(arg0: String) -> String {
String(format: self.definition_name, arg0) String(format: self.definition_name, arg0)
} }
@ -462,18 +218,12 @@ final class DefinitionTests: XCTestCase {
let expectFr = """ let expectFr = """
/// Translation in fr : /// Translation in fr :
/// Welcome "%@" ! Your age is %d :) Your weight is %f ;-) /// Welcome "%@" ! Your age is %d :) Your weight is %f ;-)
///
/// Comment :
/// This is a comment
var definition_name: String { 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") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" ! Your age is %d :) Your weight is %f ;-)", comment: "")
} }
/// Translation in fr : /// Translation in fr :
/// Welcome "%@" ! Your age is %d :) Your weight is %f ;-) /// Welcome "%@" ! Your age is %d :) Your weight is %f ;-)
///
/// Comment :
/// This is a comment
func definition_name(arg0: String, arg1: Int, arg2: Double) -> String { func definition_name(arg0: String, arg1: Int, arg2: Double) -> String {
String(format: self.definition_name, arg0, arg1, arg2) String(format: self.definition_name, arg0, arg1, arg2)
} }
@ -499,18 +249,12 @@ final class DefinitionTests: XCTestCase {
let expectFr = """ let expectFr = """
/// Translation in fr : /// Translation in fr :
/// Vous %%: %1$@ %2$@ Age: %3$d /// Vous %%: %1$@ %2$@ Age: %3$d
///
/// Comment :
/// This is a comment
var definition_name: String { var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Vous %%: %1$@ %2$@ Age: %3$d", comment: "This is a comment") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Vous %%: %1$@ %2$@ Age: %3$d", comment: "")
} }
/// Translation in fr : /// Translation in fr :
/// Vous %%: %1$@ %2$@ Age: %3$d /// Vous %%: %1$@ %2$@ Age: %3$d
///
/// Comment :
/// This is a comment
func definition_name(arg0: String, arg1: String, arg2: Int) -> String { func definition_name(arg0: String, arg1: String, arg2: Int) -> String {
String(format: self.definition_name, arg0, arg1, arg2) String(format: self.definition_name, arg0, arg1, arg2)
} }
@ -519,18 +263,12 @@ final class DefinitionTests: XCTestCase {
let expectEn = """ let expectEn = """
/// Translation in en : /// Translation in en :
/// You %%: %2$@ %1$@ Age: %3$d /// You %%: %2$@ %1$@ Age: %3$d
///
/// Comment :
/// This is a comment
var definition_name: String { var definition_name: String {
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "This is a comment") NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "")
} }
/// Translation in en : /// Translation in en :
/// You %%: %2$@ %1$@ Age: %3$d /// You %%: %2$@ %1$@ Age: %3$d
///
/// Comment :
/// This is a comment
func definition_name(arg0: String, arg1: String, arg2: Int) -> String { func definition_name(arg0: String, arg1: String, arg2: Int) -> String {
String(format: self.definition_name, arg0, arg1, arg2) String(format: self.definition_name, arg0, arg1, arg2)
} }
@ -562,9 +300,6 @@ final class DefinitionTests: XCTestCase {
let expectFr = """ let expectFr = """
/// Translation in fr : /// Translation in fr :
/// C'est la traduction francaise /// C'est la traduction francaise
///
/// Comment :
/// This is a comment
var definition_name: String { var definition_name: String {
"C'est la traduction francaise" "C'est la traduction francaise"
} }
@ -573,9 +308,6 @@ final class DefinitionTests: XCTestCase {
let expectEn = """ let expectEn = """
/// Translation in en : /// Translation in en :
/// This is the english translation /// This is the english translation
///
/// Comment :
/// This is a comment
var definition_name: String { var definition_name: String {
"This is the english translation" "This is the english translation"
} }
@ -584,9 +316,6 @@ final class DefinitionTests: XCTestCase {
let expectEnUs = """ let expectEnUs = """
/// Translation in en-us : /// Translation in en-us :
/// This is the english us translation /// This is the english us translation
///
/// Comment :
/// This is a comment
var definition_name: String { var definition_name: String {
"This is the english us translation" "This is the english us translation"
} }
@ -597,117 +326,6 @@ final class DefinitionTests: XCTestCase {
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest()) XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
} }
func testGeneratedRawPropertyWithEmptyComment() {
// Given
let definition = Definition(name: "definition_name")
definition.tags = ["ios","iosonly","notranslation"]
definition.comment = ""
definition.translations = [
"fr": "C'est la traduction francaise",
"en": "This is the english translation",
"en-us": "This is the english us translation"
]
// When
let propertyFr = definition.getProperty(forLang: "fr")
let propertyEn = definition.getProperty(forLang: "en")
let propertyEnUs = definition.getProperty(forLang: "en-us")
// Expect
let expectFr = """
/// Translation in fr :
/// C'est la traduction francaise
///
/// Comment :
/// No comment
var definition_name: String {
"C'est la traduction francaise"
}
"""
let expectEn = """
/// Translation in en :
/// This is the english translation
///
/// Comment :
/// No comment
var definition_name: String {
"This is the english translation"
}
"""
let expectEnUs = """
/// Translation in en-us :
/// This is the english us translation
///
/// Comment :
/// No comment
var definition_name: String {
"This is the english us translation"
}
"""
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
}
func testGeneratedRawPropertyWithNoComment() {
// Given
let definition = Definition(name: "definition_name")
definition.tags = ["ios","iosonly","notranslation"]
definition.translations = [
"fr": "C'est la traduction francaise",
"en": "This is the english translation",
"en-us": "This is the english us translation"
]
// When
let propertyFr = definition.getProperty(forLang: "fr")
let propertyEn = definition.getProperty(forLang: "en")
let propertyEnUs = definition.getProperty(forLang: "en-us")
// Expect
let expectFr = """
/// Translation in fr :
/// C'est la traduction francaise
///
/// Comment :
/// No comment
var definition_name: String {
"C'est la traduction francaise"
}
"""
let expectEn = """
/// Translation in en :
/// This is the english translation
///
/// Comment :
/// No comment
var definition_name: String {
"This is the english translation"
}
"""
let expectEnUs = """
/// Translation in en-us :
/// This is the english us translation
///
/// Comment :
/// No comment
var definition_name: String {
"This is the english us translation"
}
"""
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
}
// MARK: - Raw static properties
func testGeneratedRawStaticProperty() { func testGeneratedRawStaticProperty() {
// Given // Given
let definition = Definition(name: "definition_name") let definition = Definition(name: "definition_name")
@ -728,9 +346,6 @@ final class DefinitionTests: XCTestCase {
let expectFr = """ let expectFr = """
/// Translation in fr : /// Translation in fr :
/// C'est la traduction francaise /// C'est la traduction francaise
///
/// Comment :
/// This is a comment
static var definition_name: String { static var definition_name: String {
"C'est la traduction francaise" "C'est la traduction francaise"
} }
@ -739,10 +354,7 @@ final class DefinitionTests: XCTestCase {
let expectEn = """ let expectEn = """
/// Translation in en : /// Translation in en :
/// This is the english translation /// This is the english translation
/// static var definition_name: String {
/// Comment :
/// This is a comment
static var definition_name: String {
"This is the english translation" "This is the english translation"
} }
""" """
@ -750,118 +362,6 @@ final class DefinitionTests: XCTestCase {
let expectEnUs = """ let expectEnUs = """
/// Translation in en-us : /// Translation in en-us :
/// This is the english us translation /// This is the english us translation
///
/// Comment :
/// This is a comment
static var definition_name: String {
"This is the english us translation"
}
"""
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
}
func testGeneratedRawStaticPropertyWithEmptyComment() {
// Given
let definition = Definition(name: "definition_name")
definition.tags = ["ios","iosonly","notranslation"]
definition.comment = ""
definition.translations = [
"fr": "C'est la traduction francaise",
"en": "This is the english translation",
"en-us": "This is the english us translation"
]
// When
let propertyFr = definition.getStaticProperty(forLang: "fr")
let propertyEn = definition.getStaticProperty(forLang: "en")
let propertyEnUs = definition.getStaticProperty(forLang: "en-us")
// Expect
let expectFr = """
/// Translation in fr :
/// C'est la traduction francaise
///
/// Comment :
/// No comment
static var definition_name: String {
"C'est la traduction francaise"
}
"""
let expectEn = """
/// Translation in en :
/// This is the english translation
///
/// Comment :
/// No comment
static var definition_name: String {
"This is the english translation"
}
"""
let expectEnUs = """
/// Translation in en-us :
/// This is the english us translation
///
/// Comment :
/// No comment
static var definition_name: String {
"This is the english us translation"
}
"""
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
}
func testGeneratedRawStaticPropertyWithNoComment() {
// Given
let definition = Definition(name: "definition_name")
definition.tags = ["ios","iosonly","notranslation"]
definition.translations = [
"fr": "C'est la traduction francaise",
"en": "This is the english translation",
"en-us": "This is the english us translation"
]
// When
let propertyFr = definition.getStaticProperty(forLang: "fr")
let propertyEn = definition.getStaticProperty(forLang: "en")
let propertyEnUs = definition.getStaticProperty(forLang: "en-us")
// Expect
let expectFr = """
/// Translation in fr :
/// C'est la traduction francaise
///
/// Comment :
/// No comment
static var definition_name: String {
"C'est la traduction francaise"
}
"""
let expectEn = """
/// Translation in en :
/// This is the english translation
///
/// Comment :
/// No comment
static var definition_name: String {
"This is the english translation"
}
"""
let expectEnUs = """
/// Translation in en-us :
/// This is the english us translation
///
/// Comment :
/// No comment
static var definition_name: String { static var definition_name: String {
"This is the english us translation" "This is the english us translation"
} }

File diff suppressed because it is too large Load Diff