From 3fc2fd9bacf9ad66c74a4171d876cd759657f0a0 Mon Sep 17 00:00:00 2001 From: Loris Perret Date: Fri, 8 Dec 2023 11:29:29 +0100 Subject: [PATCH] fix: Tags -> Anlytics --- .../Generated/Analytics+GenAllScript.swift | 190 ++++++++++++++++++ SampleFiles/Tags/sampleTags.yml | 24 +++ Sources/ResgenSwift/Analytics/Analytics.swift | 67 ++++++ .../AnalyticsOptions.swift} | 14 +- .../Generator/AnalyticsGenerator.swift} | 75 ++++--- .../Generator/FirebaseGenerator.swift | 32 ++- .../Generator/MatomoGenerator.swift | 18 +- .../Analytics/Model/AnalyticsCategory.swift | 28 +++ .../Analytics/Model/AnalyticsDefinition.swift | 174 ++++++++++++++++ .../Analytics/Model/AnalyticsFile.swift | 47 +++++ .../Analytics/Model/AnalyticsParameter.swift | 19 ++ .../Parser/AnalyticsFileParser.swift | 173 ++++++++++++++++ .../AnalyticsConfiguration+Runnable.swift | 43 ++++ .../Tags/Model/TagDefinition.swift | 138 ------------- .../ResgenSwift/Tags/Model/TagSection.swift | 39 ---- .../Tags/Parser/TagFileParser.swift | 93 --------- Sources/ResgenSwift/Tags/Tags.swift | 95 --------- .../AnalyticsDefinitionTests.swift} | 64 +----- .../AnalyticsGeneratorTests.swift} | 95 +++++---- .../AnalyticsSectionTests.swift} | 52 +---- .../{Tags => Analytics}/DiffString.swift | 0 21 files changed, 930 insertions(+), 550 deletions(-) create mode 100644 SampleFiles/Tags/Generated/Analytics+GenAllScript.swift create mode 100644 SampleFiles/Tags/sampleTags.yml create mode 100644 Sources/ResgenSwift/Analytics/Analytics.swift rename Sources/ResgenSwift/{Tags/TagsOptions.swift => Analytics/AnalyticsOptions.swift} (80%) rename Sources/ResgenSwift/{Tags/Generator/TagsGenerator.swift => Analytics/Generator/AnalyticsGenerator.swift} (69%) rename Sources/ResgenSwift/{Tags => Analytics}/Generator/FirebaseGenerator.swift (52%) rename Sources/ResgenSwift/{Tags => Analytics}/Generator/MatomoGenerator.swift (85%) create mode 100644 Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift create mode 100644 Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift create mode 100644 Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift create mode 100644 Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift create mode 100644 Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift create mode 100644 Sources/ResgenSwift/Generate/Runnable/AnalyticsConfiguration+Runnable.swift delete mode 100644 Sources/ResgenSwift/Tags/Model/TagDefinition.swift delete mode 100644 Sources/ResgenSwift/Tags/Model/TagSection.swift delete mode 100644 Sources/ResgenSwift/Tags/Parser/TagFileParser.swift delete mode 100644 Sources/ResgenSwift/Tags/Tags.swift rename Tests/ResgenSwiftTests/{Tags/TagDefinitionTests.swift => Analytics/AnalyticsDefinitionTests.swift} (61%) rename Tests/ResgenSwiftTests/{Tags/TagsGeneratorTests.swift => Analytics/AnalyticsGeneratorTests.swift} (72%) rename Tests/ResgenSwiftTests/{Tags/TagSectionTests.swift => Analytics/AnalyticsSectionTests.swift} (52%) rename Tests/ResgenSwiftTests/{Tags => Analytics}/DiffString.swift (100%) diff --git a/SampleFiles/Tags/Generated/Analytics+GenAllScript.swift b/SampleFiles/Tags/Generated/Analytics+GenAllScript.swift new file mode 100644 index 0000000..bf18797 --- /dev/null +++ b/SampleFiles/Tags/Generated/Analytics+GenAllScript.swift @@ -0,0 +1,190 @@ +// Generated by ResgenSwift.Analytics 1.2 + +import MatomoTracker +import Firebase + +// MARK: - Protocol + +protocol AnalyticsManagerProtocol { + func logScreen(name: String, path: String) + func logEvent( + name: String, + action: String, + category: String, + params: [String: Any]? + ) +} + +// 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) { + 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 + ] + + Analytics.logEvent( + AnalyticsEventScreenView, + parameters: parameters + ) + } + + func logEvent( + name: String, + action: String, + category: String, + params: [String: Any]? + ) { + var parameters: [String:Any] = [ + "action": action, + "category": category, + ] + + if let supplementaryParameters = params { + parameters.merge(supplementaryParameters) { (origin, new) -> Any in + return origin + } + } + + Analytics.logEvent( + name, + parameters: parameters + ) + } +} + +// MARK: - Manager + +class AnalyticsManager { + static var shared = AnalyticsManager() + + // MARK: - Properties + + var managers: [AnalyticsManagerProtocol] = [] + + private var isEnabled: Bool = true + + // MARK: - Methods + + func setAnalyticsEnabled(_ enable: Bool) { + isEnabled = enable + } + + func configure(siteId: String, url: String) { + managers.append( + MatomoAnalyticsManager( + siteId: siteId, + url: url + ) + ) + managers.append(FirebaseAnalyticsManager()) + } + + private func logScreen(name: String, path: String) { + guard isEnabled else { return } + + managers.forEach { manager in + manager.logScreen(name: name, path: path) + } + } + + private func logEvent( + name: String, + action: String, + category: String, + params: [String: Any]? + ) { + guard isEnabled else { return } + + managers.forEach { manager in + manager.logEvent( + name: name, + action: action, + category: category, + params: params + ) + } + } + + // MARK: - Introduction + + func logScreenIntroductionScreen(title: String) { + logScreen( + name: "Bienvenue \(title)", + path: "introduction/" + ) + } + + func logEventIntroductionScreen(test: String, data: Int) { + logEvent( + name: "Bienvenue", + action: "action", + category: "category", + params: [ + "test": test, + "data": data + ] + ) + } +} diff --git a/SampleFiles/Tags/sampleTags.yml b/SampleFiles/Tags/sampleTags.yml new file mode 100644 index 0000000..4933909 --- /dev/null +++ b/SampleFiles/Tags/sampleTags.yml @@ -0,0 +1,24 @@ +--- +categories: + - id: Introduction + screens: + - id: introduction_screen + name: Bienvenue _TITLE_ + path: introduction/ + tags: droid,ios + parameters: + - name: title + type: String + replaceIn: name + + events: + - id: introduction_screen + name: Bienvenue + category: category + action: action + tags: droid,ios + parameters: + - name: test + type: String + - name: data + type: Int diff --git a/Sources/ResgenSwift/Analytics/Analytics.swift b/Sources/ResgenSwift/Analytics/Analytics.swift new file mode 100644 index 0000000..dd9a61c --- /dev/null +++ b/Sources/ResgenSwift/Analytics/Analytics.swift @@ -0,0 +1,67 @@ +// +// Analytics.swift +// +// +// Created by Loris Perret on 08/12/2023. +// + +import ToolCore +import Foundation +import ArgumentParser + +struct Analytics: ParsableCommand { + + // MARK: - Command Configuration + + static var configuration = CommandConfiguration( + abstract: "Generate analytics extension file.", + version: ResgenSwiftVersion + ) + + + // MARK: - Static + + static let toolName = "Analytics" + static let defaultExtensionName = "Analytics" + + // MARK: - Command Options + + @OptionGroup var options: AnalyticsOptions + + // MARK: - Run + + mutating func run() { + print("[\(Self.toolName)] Starting analytics generation") + print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate analytics for target: \(options.target)") + print("[\(Self.toolName)] Will generate analytics") + + // Parse input file + let sections = AnalyticsFileParser.parse(options.inputFile, target: options.target) + + // Generate extension + AnalyticsGenerator.writeExtensionFiles(sections: sections, + target: options.target, + tags: ["ios", "iosonly"], + staticVar: options.staticMembers, + extensionName: options.extensionName, + extensionFilePath: options.extensionFilePath) + + print("[\(Self.toolName)] Analytics generated") + } +} + +extension Analytics { + enum TargetType: CaseIterable { + case matomo + case firebase + + var value: String { + switch self { + case .matomo: + "matomo" + case .firebase: + "firebase" + } + } + } +} diff --git a/Sources/ResgenSwift/Tags/TagsOptions.swift b/Sources/ResgenSwift/Analytics/AnalyticsOptions.swift similarity index 80% rename from Sources/ResgenSwift/Tags/TagsOptions.swift rename to Sources/ResgenSwift/Analytics/AnalyticsOptions.swift index d4e4fac..0f3172e 100644 --- a/Sources/ResgenSwift/Tags/TagsOptions.swift +++ b/Sources/ResgenSwift/Analytics/AnalyticsOptions.swift @@ -1,14 +1,14 @@ // -// TagOptions.swift +// AnalyticsOptions.swift // // -// Created by Thibaut Schmitt on 10/01/2022. +// Created by Loris Perret on 08/12/2023. // import Foundation import ArgumentParser -struct TagsOptions: ParsableArguments { +struct AnalyticsOptions: ParsableArguments { @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation") var forceGeneration = false @@ -24,16 +24,16 @@ struct TagsOptions: ParsableArguments { @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 Tag extension.") - var extensionName: String = Tags.defaultExtensionName + @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}+Tag{extensionSuffix}.swift") + @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Analytics{extensionSuffix}.swift") var extensionSuffix: String? } // MARK: - Computed var -extension TagsOptions { +extension AnalyticsOptions { var extensionFileName: String { if let extensionSuffix = extensionSuffix { return "\(extensionName)+\(extensionSuffix).swift" diff --git a/Sources/ResgenSwift/Tags/Generator/TagsGenerator.swift b/Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift similarity index 69% rename from Sources/ResgenSwift/Tags/Generator/TagsGenerator.swift rename to Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift index e803a69..f16f2ef 100644 --- a/Sources/ResgenSwift/Tags/Generator/TagsGenerator.swift +++ b/Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift @@ -1,22 +1,22 @@ // -// TagsGenerator.swift -// +// AnalyticsGenerator.swift // -// Created by Thibaut Schmitt on 10/01/2022. +// +// Created by Loris Perret on 08/12/2023. // import Foundation import ToolCore import CoreVideo -class TagsGenerator { - static var targets: [Tags.TargetType] = [] +class AnalyticsGenerator { + static var targets: [Analytics.TargetType] = [] - static func writeExtensionFiles(sections: [TagSection], target: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { + static func writeExtensionFiles(sections: [AnalyticsCategory], target: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { // Get target type from enum let targetsString: [String] = target.components(separatedBy: " ") - Tags.TargetType.allCases.forEach { enumTarget in + Analytics.TargetType.allCases.forEach { enumTarget in if targetsString.contains(enumTarget.value) { targets.append(enumTarget) } @@ -43,7 +43,7 @@ class TagsGenerator { // MARK: - Extension content - static func getExtensionContent(sections: [TagSection], tags: [String], staticVar: Bool, extensionName: String) -> String { + static func getExtensionContent(sections: [AnalyticsCategory], tags: [String], staticVar: Bool, extensionName: String) -> String { [ Self.getHeader(extensionClassname: extensionName, staticVar: staticVar), Self.getProperties(sections: sections, tags: tags, staticVar: staticVar), @@ -56,9 +56,8 @@ class TagsGenerator { private static func getHeader(extensionClassname: String, staticVar: Bool) -> String { """ - // Generated by ResgenSwift.\(Tags.toolName) \(ResgenSwiftVersion) + // Generated by ResgenSwift.\(Analytics.toolName) \(ResgenSwiftVersion) - \(staticVar ? "typelias Tags = String\n\n" : "")import UIKit \(Self.getImport()) \(Self.getAnalytics()) @@ -85,17 +84,19 @@ class TagsGenerator { // MARK: - Methods - func setAnalyticsEnabled(_ enable: Bool) { isEnabled = enable } + func setAnalyticsEnabled(_ enable: Bool) { + isEnabled = enable + } """ } private static func getImport() -> String { var result: [String] = [] - if targets.contains(Tags.TargetType.matomo) { + if targets.contains(Analytics.TargetType.matomo) { result.append("import MatomoTracker") } - if targets.contains(Tags.TargetType.firebase) { + if targets.contains(Analytics.TargetType.firebase) { result.append("import Firebase") } @@ -106,15 +107,27 @@ class TagsGenerator { """ private func logScreen(name: String, path: String) { guard isEnabled else { return } + managers.forEach { manager in manager.logScreen(name: name, path: path) } } - private func logEvent(name: String) { + private func logEvent( + name: String, + action: String, + category: String, + params: [String: Any]? + ) { guard isEnabled else { return } + managers.forEach { manager in - manager.logEvent(name: name) + manager.logEvent( + name: name, + action: action, + category: category, + params: params + ) } } """ @@ -125,16 +138,23 @@ class TagsGenerator { var content: [String] = [] let footer = " }" - if targets.contains(Tags.TargetType.matomo) { + if targets.contains(Analytics.TargetType.matomo) { header = "func configure(siteId: String, url: String) {" - } else if targets.contains(Tags.TargetType.firebase) { + } else if targets.contains(Analytics.TargetType.firebase) { header = "func configure() {" } - if targets.contains(Tags.TargetType.matomo) { - content.append(" managers.append(MatomoAnalyticsManager(siteId: siteId, url: url))") + if targets.contains(Analytics.TargetType.matomo) { + content.append(""" + managers.append( + MatomoAnalyticsManager( + siteId: siteId, + url: url + ) + ) + """) } - if targets.contains(Tags.TargetType.firebase) { + if targets.contains(Analytics.TargetType.firebase) { content.append(" managers.append(FirebaseAnalyticsManager())") } @@ -152,25 +172,30 @@ class TagsGenerator { protocol AnalyticsManagerProtocol { func logScreen(name: String, path: String) - func logEvent(name: String) + func logEvent( + name: String, + action: String, + category: String, + params: [String: Any]? + ) } """ var result: [String] = [proto] - if targets.contains(Tags.TargetType.matomo) { + if targets.contains(Analytics.TargetType.matomo) { result.append(MatomoGenerator.service.content) } - if targets.contains(Tags.TargetType.firebase) { + if targets.contains(Analytics.TargetType.firebase) { result.append(FirebaseGenerator.service.content) } return result.joined(separator: "\n") } - private static func getProperties(sections: [TagSection], tags: [String], staticVar: Bool) -> String { + private static func getProperties(sections: [AnalyticsCategory], tags: [String], staticVar: Bool) -> String { sections .compactMap { section in // Check that at least one string will be generated @@ -178,7 +203,7 @@ class TagsGenerator { return nil// Go to next section } - var res = "\n // MARK: - \(section.name)" + var res = "\n // MARK: - \(section.id)" section.definitions.forEach { definition in guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { return // Go to next definition diff --git a/Sources/ResgenSwift/Tags/Generator/FirebaseGenerator.swift b/Sources/ResgenSwift/Analytics/Generator/FirebaseGenerator.swift similarity index 52% rename from Sources/ResgenSwift/Tags/Generator/FirebaseGenerator.swift rename to Sources/ResgenSwift/Analytics/Generator/FirebaseGenerator.swift index 5f2b690..4352b0e 100644 --- a/Sources/ResgenSwift/Tags/Generator/FirebaseGenerator.swift +++ b/Sources/ResgenSwift/Analytics/Generator/FirebaseGenerator.swift @@ -31,7 +31,14 @@ enum FirebaseGenerator { private var logScreen: String { """ func logScreen(name: String, path: String) { - Analytics.logEvent(AnalyticsEventScreenView, parameters: [AnalyticsParameterScreenName: name]) + var parameters = [ + AnalyticsParameterScreenName: name + ] + + Analytics.logEvent( + AnalyticsEventScreenView, + parameters: parameters + ) } """ @@ -39,12 +46,27 @@ enum FirebaseGenerator { private var logEvent: String { """ - func logEvent(name: String) { - var parameters = [ - AnalyticsParameterValue: name + func logEvent( + name: String, + action: String, + category: String, + params: [String: Any]? + ) { + var parameters: [String:Any] = [ + "action": action, + "category": category, ] + + if let supplementaryParameters = params { + parameters.merge(supplementaryParameters) { (origin, new) -> Any in + return origin + } + } - Analytics.logEvent(AnalyticsEventSelectContent, parameters: parameters) + Analytics.logEvent( + name, + parameters: parameters + ) } """ } diff --git a/Sources/ResgenSwift/Tags/Generator/MatomoGenerator.swift b/Sources/ResgenSwift/Analytics/Generator/MatomoGenerator.swift similarity index 85% rename from Sources/ResgenSwift/Tags/Generator/MatomoGenerator.swift rename to Sources/ResgenSwift/Analytics/Generator/MatomoGenerator.swift index 2d7b56a..739e8cb 100644 --- a/Sources/ResgenSwift/Tags/Generator/MatomoGenerator.swift +++ b/Sources/ResgenSwift/Analytics/Generator/MatomoGenerator.swift @@ -41,7 +41,10 @@ enum MatomoGenerator { 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)!) + tracker = MatomoTracker( + siteId: siteId, + baseURL: URL(string: url)! + ) #if DEBUG tracker.dispatchInterval = 5 @@ -65,6 +68,7 @@ enum MatomoGenerator { 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], @@ -77,11 +81,17 @@ enum MatomoGenerator { private var logEvent: String { """ - func logEvent(name: String) { + func logEvent( + name: String, + action: String, + category: String, + params: [String: Any]? + ) { guard !tracker.isOptedOut else { return } + tracker.track( - eventWithCategory: "category", - action: "action", + eventWithCategory: category, + action: action, name: name, number: nil, url: nil diff --git a/Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift b/Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift new file mode 100644 index 0000000..9cec892 --- /dev/null +++ b/Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift @@ -0,0 +1,28 @@ +// +// AnalyticsCategory.swift +// +// +// Created by Loris Perret on 05/12/2023. +// + +import Foundation + +class AnalyticsCategory { + let id: String // OnBoarding + var definitions = [AnalyticsDefinition]() + + init(id: String) { + self.id = id + } + + func hasOneOrMoreMatchingTags(tags: [String]) -> Bool { + let allTags = definitions.flatMap { $0.tags } + let allTagsSet = Set(allTags) + + let intersection = Set(tags).intersection(allTagsSet) + if intersection.isEmpty { + return false + } + return true + } +} diff --git a/Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift b/Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift new file mode 100644 index 0000000..e0bdbe1 --- /dev/null +++ b/Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift @@ -0,0 +1,174 @@ +// +// AnalyticsDefinition.swift +// +// +// Created by Loris Perret on 05/12/2023. +// + +import Foundation + +class AnalyticsDefinition { + let id: String + var name: String + var path: String = "" + var category: String = "" + var action: String = "" + var comments: String = "" + var tags: [String] = [] + var parameters: [AnalyticsParameter] = [] + var type: TagType + + init(id: String, name: String, type: TagType) { + self.id = id + self.name = name + self.type = type + } + + func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool { + if Set(inputTags).intersection(Set(self.tags)).isEmpty { + return false + } + return true + } + + // MARK: - Methods + + private func getFuncName() -> String { + var pascalCaseTitle: String = "" + id.components(separatedBy: "_").forEach { word in + pascalCaseTitle.append(contentsOf: word.uppercasedFirst()) + } + + return "log\(type == .screen ? "Screen" : "Event")\(pascalCaseTitle)" + } + + private func getParameters() -> String { + var params = parameters + var result: String + + if type == .screen { + params = params.filter{ param in + !param.replaceIn.isEmpty + } + } + + let paramsString = params.map { parameter in + "\(parameter.name): \(parameter.type)" + } + + if paramsString.count > 2 { + result = """ + ( + \(paramsString.joined(separator: ",\n\t\t")) + ) + """ + } else { + result = """ + (\(paramsString.joined(separator: ", "))) + """ + } + + return result + } + + private func replaceIn(){ + for parameter in parameters { + 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 + } + } + } + } + + private func getlogFunction() -> String { + var params: [String] = [] + var result: String + + let supplementaryParams = parameters.filter { param in + param.replaceIn.isEmpty + } + + supplementaryParams.forEach { param in + params.append("\"\(param.name)\": \(param.name)") + } + + if params.count > 1 { + result = """ + [ + \(params.joined(separator: ",\n\t\t\t\t")) + ] + """ + } else { + result = """ + [\(params.joined(separator: ", "))] + """ + } + + if type == .screen { + return """ + logScreen( + name: "\(name)", + path: "\(path)" + ) + """ + } else { + return """ + logEvent( + name: "\(name)", + action: "\(action)", + category: "\(category)", + params: \(result) + ) + """ + } + } + + // MARK: - Raw strings + + func getProperty() -> String { + replaceIn() + return """ + func \(getFuncName())\(getParameters()) { + \(getlogFunction()) + } + """ + } + + func getStaticProperty() -> String { + replaceIn() + return """ + static func \(getFuncName())\(getParameters()) { + \(getlogFunction()) + } + """ + } +} + +extension AnalyticsDefinition { + enum TagType { + case screen + case event + } +} + +extension String { + func replacingFirstOccurrence(of: String, with: String) -> Self { + if let range = self.range(of: of) { + let tmp = self.replacingOccurrences( + of: of, + with: with, + options: .literal, + range: range + ) + + return tmp + } + + return self + } +} diff --git a/Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift b/Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift new file mode 100644 index 0000000..5c4f690 --- /dev/null +++ b/Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift @@ -0,0 +1,47 @@ +// +// AnalyticsFile.swift +// +// +// Created by Loris Perret on 06/12/2023. +// + +import Foundation + +struct AnalyticsFile: Codable { + var categories: [AnalyticsCategoryDTO] +} + +struct AnalyticsCategoryDTO: Codable { + var id: String + var screens: [AnalyticsDefinitionScreenDTO]? + var events: [AnalyticsDefinitionEventDTO]? +} + +protocol AnalyticsDefinitionDTO: Codable {} + +struct AnalyticsDefinitionScreenDTO: AnalyticsDefinitionDTO { + var id: String + var name: String + var tags: String + var comments: String? + var parameters: [AnalyticsParameterDTO]? + + var path: String? +} + +struct AnalyticsDefinitionEventDTO: AnalyticsDefinitionDTO { + var id: String + var name: String + var tags: String + var comments: String? + var parameters: [AnalyticsParameterDTO]? + + var category: String? + var action: String? +} + +struct AnalyticsParameterDTO: Codable { + var name: String + var type: String + var replaceIn: String? +} diff --git a/Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift b/Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift new file mode 100644 index 0000000..e7f1079 --- /dev/null +++ b/Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift @@ -0,0 +1,19 @@ +// +// AnalyticsParameter.swift +// +// +// Created by Loris Perret on 06/12/2023. +// + +import Foundation + +class AnalyticsParameter { + var name: String + var type: String + var replaceIn: [String] = [] + + init(name: String, type: String) { + self.name = name + self.type = type + } +} diff --git a/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift b/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift new file mode 100644 index 0000000..0992aed --- /dev/null +++ b/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift @@ -0,0 +1,173 @@ +// +// AnalyticsFileParser.swift +// +// +// Created by Loris Perret on 06/12/2023. +// + +import Foundation +import Yams + +class AnalyticsFileParser { + private static var inputFile: String = "" + private static var target: String = "" + + private static func parseYaml() -> AnalyticsFile { + guard let data = FileManager().contents(atPath: inputFile) else { + let error = GenerateError.fileNotExists(inputFile) + Generate.exit(withError: error) + } + + do { + let tagFile = try YAMLDecoder().decode(AnalyticsFile.self, from: data) + return tagFile + } catch let error { + Generate.exit(withError: error) + } + } + + private static func getParameters(fromData data: [AnalyticsParameterDTO]) -> [AnalyticsParameter] { + var parameters: [AnalyticsParameter] = [] + + data.forEach { value in + // Type + + let type = value.type.uppercasedFirst() + + guard + type == "String" || + type == "Int" || + type == "Double" || + type == "Bool" + else { + let error = GenerateError.invalidParameter("type of \(value.name)") + Generate.exit(withError: error) + } + + let parameter: AnalyticsParameter = AnalyticsParameter(name: value.name, type: type) + + if let replaceIn = value.replaceIn { + parameter.replaceIn = replaceIn.components(separatedBy: ",") + } + + parameters.append(parameter) + } + + return parameters + } + + private static func getTagDefinition( + id: String, + name: String, + type: AnalyticsDefinition.TagType, + tags: String, + comments: String?, + parameters: [AnalyticsParameterDTO]? + ) -> AnalyticsDefinition { + let definition: AnalyticsDefinition = AnalyticsDefinition(id: id, name: name, type: type) + definition.tags = tags.components(separatedBy: ",") + + if let comments = comments { + definition.comments = comments + } + + if let parameters = parameters { + definition.parameters = Self.getParameters(fromData: parameters) + } + + return definition + } + + private static func getTagDefinitionScreen(fromData screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] { + var definitions: [AnalyticsDefinition] = [] + + for screen in screens { + let definition: AnalyticsDefinition = Self.getTagDefinition( + id: screen.id, + name: screen.name, + type: .screen, + tags: screen.tags, + comments: screen.comments, + parameters: screen.parameters + ) + + guard target.contains(Analytics.TargetType.matomo.value) else { continue } + + // Path + + guard let path = screen.path else { + let error = GenerateError.missingElement("screen path") + Generate.exit(withError: error) + } + + definition.path = path + + definitions.append(definition) + } + + return definitions + } + + private static func getTagDefinitionEvent(fromData events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] { + var definitions: [AnalyticsDefinition] = [] + + for event in events { + let definition: AnalyticsDefinition = Self.getTagDefinition( + id: event.id, + name: event.name, + type: .event, + tags: event.tags, + comments: event.comments, + parameters: event.parameters + ) + + guard target.contains(Analytics.TargetType.matomo.value) else { continue } + + // Category + + guard let category = event.category else { + let error = GenerateError.missingElement("event category") + Generate.exit(withError: error) + } + + definition.category = category + + // Action + + guard let action = event.action else { + let error = GenerateError.missingElement("event action") + Generate.exit(withError: error) + } + + definition.action = action + + definitions.append(definition) + } + + return definitions + } + + static func parse(_ inputFile: String, target: String) -> [AnalyticsCategory] { + self.inputFile = inputFile + self.target = target + + let tagFile: AnalyticsFile = Self.parseYaml() + var sections: [AnalyticsCategory] = [] + + tagFile.categories.forEach { categorie in + let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id) + + if let screens = categorie.screens { + section.definitions.append(contentsOf: Self.getTagDefinitionScreen(fromData: screens)) + } + + if let events = categorie.events { + section.definitions.append(contentsOf: Self.getTagDefinitionEvent(fromData: events)) + } + + sections.append(section) + } + + return sections + } +} diff --git a/Sources/ResgenSwift/Generate/Runnable/AnalyticsConfiguration+Runnable.swift b/Sources/ResgenSwift/Generate/Runnable/AnalyticsConfiguration+Runnable.swift new file mode 100644 index 0000000..1689a24 --- /dev/null +++ b/Sources/ResgenSwift/Generate/Runnable/AnalyticsConfiguration+Runnable.swift @@ -0,0 +1,43 @@ +// +// AnalyticsConfiguration+Runnable.swift +// +// +// Created by Loris Perret on 08/12/2023. +// + +import Foundation + +extension AnalyticsConfiguration: Runnable { + func run(projectDirectory: String, force: Bool) { + var args = [String]() + + if force { + args += ["-f"] + } + + args += [ + inputFile.prependIfRelativePath(projectDirectory), + "--target", + target, + "--extension-output-path", + extensionOutputPath.prependIfRelativePath(projectDirectory), + "--static-members", + "\(staticMembersOptions)" + ] + + if let extensionName = extensionName { + args += [ + "--extension-name", + extensionName + ] + } + if let extensionSuffix = extensionSuffix { + args += [ + "--extension-suffix", + extensionSuffix + ] + } + + Analytics.main(args) + } +} diff --git a/Sources/ResgenSwift/Tags/Model/TagDefinition.swift b/Sources/ResgenSwift/Tags/Model/TagDefinition.swift deleted file mode 100644 index 806ec8b..0000000 --- a/Sources/ResgenSwift/Tags/Model/TagDefinition.swift +++ /dev/null @@ -1,138 +0,0 @@ -// -// TagDefinition.swift -// -// -// Created by Loris Perret on 05/12/2023. -// - -import Foundation - -class TagDefinition { - let title: String - var path: String = "" - var name: String = "" - var type: String = "" - var tags = [String]() - var comment: String? - - var isValid: Bool { - title.isEmpty == false && - name.isEmpty == false && - (type == TagType.screen.value || type == TagType.event.value) - } - - init(title: String) { - self.title = title - } - - static func match(_ line: String) -> TagDefinition? { - guard line.range(of: "\\[(.*?)]$", options: .regularExpression, range: nil, locale: nil) != nil else { - return nil - } - - let definitionTitle = line - .replacingOccurrences(of: ["[", "]"], with: "") - .removeLeadingTrailingWhitespace() - - return TagDefinition(title: definitionTitle) - } - - func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool { - if Set(inputTags).intersection(Set(self.tags)).isEmpty { - return false - } - return true - } - - // MARK: - - - private func getStringParameters(input: String) -> (inputParameters: [String], translationArguments: [String])? { - var methodsParameters = [String]() - - let printfPlaceholderRegex = try! NSRegularExpression(pattern: "%(?:\\d+\\$)?[+-]?(?:[ 0]|'.{1})?-?\\d*(?:\\.\\d+)?[blcdeEufFgGosxX@]*") - printfPlaceholderRegex.enumerateMatches(in: input, options: [], range: NSRange(location: 0, length: input.count)) { match, _, stop in - guard let match = match else { return } - - if let range = Range(match.range, in: input), let last = input[range].last { - switch last { - case "d", "u": - methodsParameters.append("Int") - case "f", "F": - methodsParameters.append("Double") - case "@", "s", "c": - methodsParameters.append("String") - case "%": - // if you need to print %, you have to add %% - break - default: - break - } - } - } - - if methodsParameters.isEmpty { - return nil - } - - var inputParameters = [String]() - var translationArguments = [String]() - for (index, paramType) in methodsParameters.enumerated() { - let paramName = "arg\(index)" - translationArguments.append(paramName) - inputParameters.append("\(paramName): \(paramType)") - } - - return (inputParameters: inputParameters, translationArguments: translationArguments) - } - - private func getFuncName() -> String { - var pascalCaseTitle: String = "" - name.components(separatedBy: " ").forEach { word in - pascalCaseTitle.append(contentsOf: word.uppercasedFirst()) - } - - return "log\(type == TagType.screen.value ? "Screen" : "Event")\(pascalCaseTitle)" - } - - private func getlogFunction() -> String { - if type == TagType.screen.value { - "logScreen(name: \"\(name)\", path: \"\(path)\")" - } else { - "logEvent(name: \"\(name)\")" - } - } - - // MARK: - Raw strings - - func getProperty() -> String { - """ - func \(getFuncName())() { - \(getlogFunction()) - } - """ - } - - func getStaticProperty() -> String { - """ - static func \(getFuncName())() { - \(getlogFunction()) - } - """ - } -} - -extension TagDefinition { - enum TagType { - case screen - case event - - var value: String { - switch self { - case .screen: - "screen" - case .event: - "event" - } - } - } -} diff --git a/Sources/ResgenSwift/Tags/Model/TagSection.swift b/Sources/ResgenSwift/Tags/Model/TagSection.swift deleted file mode 100644 index afabacd..0000000 --- a/Sources/ResgenSwift/Tags/Model/TagSection.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// TagSection.swift -// -// -// Created by Loris Perret on 05/12/2023. -// - -import Foundation - -class TagSection { - let name: String // OnBoarding - var definitions = [TagDefinition]() - - init(name: String) { - self.name = name - } - - static func match(_ line: String) -> TagSection? { - guard line.range(of: "\\[\\[(.*?)]]$", options: .regularExpression, range: nil, locale: nil) != nil else { - return nil - } - - let sectionName = line - .replacingOccurrences(of: ["[", "]"], with: "") - .removeLeadingTrailingWhitespace() - return TagSection(name: sectionName) - } - - func hasOneOrMoreMatchingTags(tags: [String]) -> Bool { - let allTags = definitions.flatMap { $0.tags } - let allTagsSet = Set(allTags) - - let intersection = Set(tags).intersection(allTagsSet) - if intersection.isEmpty { - return false - } - return true - } -} diff --git a/Sources/ResgenSwift/Tags/Parser/TagFileParser.swift b/Sources/ResgenSwift/Tags/Parser/TagFileParser.swift deleted file mode 100644 index c8e93e2..0000000 --- a/Sources/ResgenSwift/Tags/Parser/TagFileParser.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// TagFileParser.swift -// -// -// Created by Loris Perret on 05/12/2023. -// - -import Foundation - -class TagFileParser { - static func parse(_ inputFile: String) -> [TagSection] { - let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8) - let stringsByLines = inputFileContent.components(separatedBy: .newlines) - - var sections = [TagSection]() - - // Parse file - stringsByLines.forEach { - // TagSection - if let section = TagSection.match($0) { - sections.append(section) - return - } - - // Definition - if let definition = TagDefinition.match($0) { - sections.last?.definitions.append(definition) - return - } - - // Definition content - if $0.isEmpty == false { - // name = Test => ["name ", " Test"] - let splitLine = $0 - .removeLeadingTrailingWhitespace() - .split(separator: "=") - - guard let lastDefinition = sections.last?.definitions.last, - let leftElement = splitLine.first else { - return - } - - let rightElement: String = splitLine.dropFirst().joined(separator: "=") - - // "name " => "name" - let leftHand = String(leftElement).removeTrailingWhitespace() - // " Test" => "Test" - let rightHand = String(rightElement).removeLeadingWhitespace() - - // Handle comments, tags and translation - switch leftHand { - case "comments": - lastDefinition.comment = rightHand - - case "tags": - lastDefinition.tags = rightHand - .split(separator: ",") - .map { String($0) } - - case "path": - lastDefinition.path = rightHand - - case "name": - lastDefinition.name = rightHand - - case "type": - lastDefinition.type = rightHand - - default: - break - } - } - } - - // Keep only valid definition - var invalidDefinitionNames = [String]() - sections.forEach { section in - section.definitions = section.definitions - .filter { - if $0.isValid == false { - invalidDefinitionNames.append($0.name) - return false - } - return true - } - } - if invalidDefinitionNames.count > 0 { - print("warning: [\(Stringium.toolName)] Found \(invalidDefinitionNames.count) definition (\(invalidDefinitionNames.joined(separator: ", "))") - } - - return sections - } -} diff --git a/Sources/ResgenSwift/Tags/Tags.swift b/Sources/ResgenSwift/Tags/Tags.swift deleted file mode 100644 index 971392f..0000000 --- a/Sources/ResgenSwift/Tags/Tags.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// Tag.swift -// -// -// Created by Thibaut Schmitt on 10/01/2022. -// - -import ToolCore -import Foundation -import ArgumentParser - -struct Tags: ParsableCommand { - - // MARK: - Command Configuration - - static var configuration = CommandConfiguration( - abstract: "Generate tags extension file.", - version: ResgenSwiftVersion - ) - - - // MARK: - Static - - static let toolName = "Tags" - static let defaultExtensionName = "Tags" - static let noTranslationTag: String = "notranslation" - - // MARK: - Command Options - - @OptionGroup var options: TagsOptions - - // MARK: - Run - - mutating func run() { - print("[\(Self.toolName)] Starting tags generation") - print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate tags for target: \(options.target)") - - // Check requirements - guard checkRequirements() else { return } - - print("[\(Self.toolName)] Will generate tags") - - // Parse input file - let sections = TagFileParser.parse(options.inputFile) - - // Generate extension - TagsGenerator.writeExtensionFiles(sections: sections, - target: options.target, - tags: ["ios", "iosonly", Self.noTranslationTag], - staticVar: options.staticMembers, - extensionName: options.extensionName, - extensionFilePath: options.extensionFilePath) - - print("[\(Self.toolName)] Tags generated") - } - - // MARK: - Requirements - - private func checkRequirements() -> Bool { - let fileManager = FileManager() - - // Input file - guard fileManager.fileExists(atPath: options.inputFile) else { - let error = StringiumError.fileNotExists(options.inputFile) - print(error.description) - Stringium.exit(withError: error) - } - - // Check if needed to regenerate - guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, - inputFilePath: options.inputFile, - extensionFilePath: options.extensionFilePath) else { - print("[\(Self.toolName)] Tags are already up to date :) ") - return false - } - - return true - } -} - -extension Tags { - enum TargetType: CaseIterable { - case matomo - case firebase - - var value: String { - switch self { - case .matomo: - "matomo" - case .firebase: - "firebase" - } - } - } -} diff --git a/Tests/ResgenSwiftTests/Tags/TagDefinitionTests.swift b/Tests/ResgenSwiftTests/Analytics/AnalyticsDefinitionTests.swift similarity index 61% rename from Tests/ResgenSwiftTests/Tags/TagDefinitionTests.swift rename to Tests/ResgenSwiftTests/Analytics/AnalyticsDefinitionTests.swift index 9a9d116..bbf451d 100644 --- a/Tests/ResgenSwiftTests/Tags/TagDefinitionTests.swift +++ b/Tests/ResgenSwiftTests/Analytics/AnalyticsDefinitionTests.swift @@ -1,5 +1,5 @@ // -// TagDefinitionTests.swift +// AnalyticsDefinitionTests.swift // // // Created by Loris Perret on 06/12/2023. @@ -10,44 +10,13 @@ import XCTest @testable import ResgenSwift -final class TagDefinitionTests: XCTestCase { - - // MARK: - Match line - - func testMatchingTagDefinition() { - // Given - let line = "[definition_name]" - - // When - let definition = TagDefinition.match(line) - - // Expect - XCTAssertNotNil(definition) - XCTAssertEqual(definition?.title, "definition_name") - } - - func testNotMatchingTagDefinition() { - // Given - let line1 = "definition_name" - let line2 = "[definition_name" - let line3 = "definition_name]" - - // When - let definition1 = TagDefinition.match(line1) - let definition2 = TagDefinition.match(line2) - let definition3 = TagDefinition.match(line3) - - // Expect - XCTAssertNil(definition1) - XCTAssertNil(definition2) - XCTAssertNil(definition3) - } +final class AnalyticsDefinitionTests: XCTestCase { // MARK: - Matching tags - func testMatchingTags() { + func testMatchingAnalyticss() { // Given - let definition = TagDefinition(title: "definition_name") + let definition = AnalyticsDefinition(id: "definition_name", name: "", type: .screen) definition.tags = ["ios","iosonly","notranslation"] // When @@ -62,9 +31,9 @@ final class TagDefinitionTests: XCTestCase { XCTAssertTrue(match3) } - func testNotMatchingTags() { + func testNotMatchingAnalyticss() { // Given - let definition = TagDefinition(title: "definition_name") + let definition = AnalyticsDefinition(id: "definition_name", name: "", type: .screen) definition.tags = ["ios","iosonly","notranslation"] // When @@ -82,10 +51,8 @@ final class TagDefinitionTests: XCTestCase { func testGeneratedRawPropertyScreen() { // Given - let definition = TagDefinition(title: "definition_name") - definition.path = "ecran_un/" - definition.name = "Ecran un" - definition.type = "screen" + let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .screen) + // When let propertyScreen = definition.getProperty() @@ -102,10 +69,7 @@ final class TagDefinitionTests: XCTestCase { func testGeneratedRawPropertyEvent() { // Given - let definition = TagDefinition(title: "definition_name") - definition.path = "ecran_un/" - definition.name = "Ecran un" - definition.type = "event" + let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .screen) // When let propertyEvent = definition.getProperty() @@ -122,10 +86,7 @@ final class TagDefinitionTests: XCTestCase { func testGeneratedRawStaticPropertyScreen() { // Given - let definition = TagDefinition(title: "definition_name") - definition.path = "ecran_un/" - definition.name = "Ecran un" - definition.type = "screen" + let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .screen) // When let propertyScreen = definition.getStaticProperty() @@ -142,10 +103,7 @@ final class TagDefinitionTests: XCTestCase { func testGeneratedRawStaticPropertyEvent() { // Given - let definition = TagDefinition(title: "definition_name") - definition.path = "ecran_un/" - definition.name = "Ecran un" - definition.type = "event" + let definition = AnalyticsDefinition(id: "definition_name", name: "Ecran un", type: .screen) // When let propertyEvent = definition.getStaticProperty() diff --git a/Tests/ResgenSwiftTests/Tags/TagsGeneratorTests.swift b/Tests/ResgenSwiftTests/Analytics/AnalyticsGeneratorTests.swift similarity index 72% rename from Tests/ResgenSwiftTests/Tags/TagsGeneratorTests.swift rename to Tests/ResgenSwiftTests/Analytics/AnalyticsGeneratorTests.swift index b10dfb5..3978476 100644 --- a/Tests/ResgenSwiftTests/Tags/TagsGeneratorTests.swift +++ b/Tests/ResgenSwiftTests/Analytics/AnalyticsGeneratorTests.swift @@ -1,5 +1,5 @@ // -// TagsGeneratorTests.swift +// AnalyticsGeneratorTests.swift // // // Created by Thibaut Schmitt on 06/09/2022. @@ -11,46 +11,43 @@ import ToolCore @testable import ResgenSwift -final class TagsGeneratorTests: XCTestCase { +final class AnalyticsGeneratorTests: XCTestCase { - private func getTagDefinition(title: String, path: String, name: String, type: String, tags: [String]) -> TagDefinition { - let definition = TagDefinition(title: title) - definition.path = path - definition.name = name - definition.type = type + private func getAnalyticsDefinition(id: String, path: String, name: String, type: AnalyticsDefinition.TagType, tags: [String]) -> AnalyticsDefinition { + let definition = AnalyticsDefinition(id: id, name: name, type: type) definition.tags = tags return definition } func testGeneratedExtensionContentFirebase() { // Given - let sectionOne = TagSection(name: "section_one") + let sectionOne = AnalyticsCategory(id: "section_one") sectionOne.definitions = [ - getTagDefinition(title: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: "screen", tags: ["ios", "iosonly"]), - getTagDefinition(title: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: "event", tags: ["ios", "iosonly"]), + getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: AnalyticsDefinition.TagType.screen, tags: ["ios", "iosonly"]), + getAnalyticsDefinition(id: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: AnalyticsDefinition.TagType.event, tags: ["ios", "iosonly"]), ] - let sectionTwo = TagSection(name: "section_two") + let sectionTwo = AnalyticsCategory(id: "section_two") sectionTwo.definitions = [ - getTagDefinition(title: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: "screen", tags: ["ios","iosonly"]), - getTagDefinition(title: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: "event", tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: AnalyticsDefinition.TagType.screen, tags: ["ios","iosonly"]), + getAnalyticsDefinition(id: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: AnalyticsDefinition.TagType.event, tags: ["droid","droidonly"]), ] - let sectionThree = TagSection(name: "section_three") + let sectionThree = AnalyticsCategory(id: "section_three") sectionThree.definitions = [ - getTagDefinition(title: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: "screen", tags: ["droid","droidonly"]), - getTagDefinition(title: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: "event", tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: AnalyticsDefinition.TagType.screen, tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: AnalyticsDefinition.TagType.event, tags: ["droid","droidonly"]), ] // When - TagsGenerator.targets = [Tags.TargetType.firebase] - let extensionContent = TagsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], + AnalyticsGenerator.targets = [Analytics.TargetType.firebase] + let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, - extensionName: "GenTags") - // Expect Tags + extensionName: "GenAnalytics") + // Expect Analytics let expect = """ - // Generated by ResgenSwift.Tags 1.2 + // Generated by ResgenSwift.Analytics 1.2 import UIKit import Firebase @@ -138,33 +135,33 @@ final class TagsGeneratorTests: XCTestCase { func testGeneratedExtensionContentMatomo() { // Given - let sectionOne = TagSection(name: "section_one") + let sectionOne = AnalyticsCategory(id: "section_one") sectionOne.definitions = [ - getTagDefinition(title: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: "screen", tags: ["ios", "iosonly"]), - getTagDefinition(title: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: "event", tags: ["ios", "iosonly"]), + getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: AnalyticsDefinition.TagType.screen, tags: ["ios", "iosonly"]), + getAnalyticsDefinition(id: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: AnalyticsDefinition.TagType.event, tags: ["ios", "iosonly"]), ] - let sectionTwo = TagSection(name: "section_two") + let sectionTwo = AnalyticsCategory(id: "section_two") sectionTwo.definitions = [ - getTagDefinition(title: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: "screen", tags: ["ios","iosonly"]), - getTagDefinition(title: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: "event", tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: AnalyticsDefinition.TagType.screen, tags: ["ios","iosonly"]), + getAnalyticsDefinition(id: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: AnalyticsDefinition.TagType.event, tags: ["droid","droidonly"]), ] - let sectionThree = TagSection(name: "section_three") + let sectionThree = AnalyticsCategory(id: "section_three") sectionThree.definitions = [ - getTagDefinition(title: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: "screen", tags: ["droid","droidonly"]), - getTagDefinition(title: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: "event", tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: AnalyticsDefinition.TagType.screen, tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: AnalyticsDefinition.TagType.event, tags: ["droid","droidonly"]), ] // When - TagsGenerator.targets = [Tags.TargetType.matomo] - let extensionContent = TagsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], + AnalyticsGenerator.targets = [Analytics.TargetType.matomo] + let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, - extensionName: "GenTags") - // Expect Tags + extensionName: "GenAnalytics") + // Expect Analytics let expect = """ - // Generated by ResgenSwift.Tags 1.2 + // Generated by ResgenSwift.Analytics 1.2 import UIKit import MatomoTracker @@ -287,33 +284,33 @@ final class TagsGeneratorTests: XCTestCase { func testGeneratedExtensionContentMatomoAndFirebase() { // Given - let sectionOne = TagSection(name: "section_one") + let sectionOne = AnalyticsCategory(id: "section_one") sectionOne.definitions = [ - getTagDefinition(title: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: "screen", tags: ["ios", "iosonly"]), - getTagDefinition(title: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: "event", tags: ["ios", "iosonly"]), + getAnalyticsDefinition(id: "s1_def_one", path: "s1_def_one/", name: "s1 def one", type: AnalyticsDefinition.TagType.screen, tags: ["ios", "iosonly"]), + getAnalyticsDefinition(id: "s1_def_two", path: "s1_def_two/", name: "s1 def two", type: AnalyticsDefinition.TagType.event, tags: ["ios", "iosonly"]), ] - let sectionTwo = TagSection(name: "section_two") + let sectionTwo = AnalyticsCategory(id: "section_two") sectionTwo.definitions = [ - getTagDefinition(title: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: "screen", tags: ["ios","iosonly"]), - getTagDefinition(title: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: "event", tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s2_def_one", path: "s2_def_one/", name: "s2 def one", type: AnalyticsDefinition.TagType.screen, tags: ["ios","iosonly"]), + getAnalyticsDefinition(id: "s2_def_two", path: "s2_def_two/", name: "s2 def two", type: AnalyticsDefinition.TagType.event, tags: ["droid","droidonly"]), ] - let sectionThree = TagSection(name: "section_three") + let sectionThree = AnalyticsCategory(id: "section_three") sectionThree.definitions = [ - getTagDefinition(title: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: "screen", tags: ["droid","droidonly"]), - getTagDefinition(title: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: "event", tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s3_def_one", path: "s3_def_one/", name: "s3 def one", type: AnalyticsDefinition.TagType.screen, tags: ["droid","droidonly"]), + getAnalyticsDefinition(id: "s3_def_two", path: "s3_def_two/", name: "s3 def two", type: AnalyticsDefinition.TagType.event, tags: ["droid","droidonly"]), ] // When - TagsGenerator.targets = [Tags.TargetType.matomo, Tags.TargetType.firebase] - let extensionContent = TagsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], + AnalyticsGenerator.targets = [Analytics.TargetType.matomo, Analytics.TargetType.firebase] + let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, - extensionName: "GenTags") - // Expect Tags + extensionName: "GenAnalytics") + // Expect Analytics let expect = """ - // Generated by ResgenSwift.Tags 1.2 + // Generated by ResgenSwift.Analytics 1.2 import UIKit import MatomoTracker diff --git a/Tests/ResgenSwiftTests/Tags/TagSectionTests.swift b/Tests/ResgenSwiftTests/Analytics/AnalyticsSectionTests.swift similarity index 52% rename from Tests/ResgenSwiftTests/Tags/TagSectionTests.swift rename to Tests/ResgenSwiftTests/Analytics/AnalyticsSectionTests.swift index 979fe38..47447e7 100644 --- a/Tests/ResgenSwiftTests/Tags/TagSectionTests.swift +++ b/Tests/ResgenSwiftTests/Analytics/AnalyticsSectionTests.swift @@ -1,5 +1,5 @@ // -// TagSectionTests.swift +// AnalyticsSectionTests.swift // // // Created by Loris Perret on 06/12/2023. @@ -10,53 +10,21 @@ import XCTest @testable import ResgenSwift -final class TagSectionTests: XCTestCase { - - // MARK: - Match line - - func testMatchingTagSection() { - // Given - let line = "[[section_name]]" - - // When - let section = TagSection.match(line) - - // Expect - XCTAssertNotNil(section) - XCTAssertEqual(section?.name, "section_name") - } - - func testNotMatchingTagSection() { - // Given - let lines = ["section_name", - "[section_name]", - "[section_name", - "[[section_name", - "[[section_name]", - "section_name]", - "section_name]]", - "[section_name]]"] - - // When - let matches = lines.compactMap { TagSection.match($0) } - - // Expect - XCTAssertEqual(matches.isEmpty, true) - } +final class AnalyticsSectionTests: XCTestCase { // MARK: - Matching tags - func testMatchingTags() { + func testMatchingAnalytics() { // Given - let section = TagSection(name: "section_name") + let section = AnalyticsCategory(id: "section_name") section.definitions = [ { - let def = TagDefinition(title: "definition_name") + let def = AnalyticsDefinition(id: "definition_name", name: "", type: .screen) def.tags = ["ios","iosonly"] return def }(), { - let def = TagDefinition(title: "definition_name_two") + let def = AnalyticsDefinition(id: "definition_name_two", name: "", type: .screen) def.tags = ["droid","droidonly"] return def }() @@ -75,17 +43,17 @@ final class TagSectionTests: XCTestCase { XCTAssertTrue(match4) } - func testNotMatchingTags() { + func testNotMatchingAnalytics() { // Given - let section = TagSection(name: "section_name") + let section = AnalyticsCategory(id: "section_name") section.definitions = [ { - let def = TagDefinition(title: "definition_name") + let def = AnalyticsDefinition(id: "definition_name", name: "", type: .screen) def.tags = ["ios","iosonly"] return def }(), { - let def = TagDefinition(title: "definition_name_two") + let def = AnalyticsDefinition(id: "definition_name_two", name: "", type: .screen) def.tags = ["droid","droidonly"] return def }() diff --git a/Tests/ResgenSwiftTests/Tags/DiffString.swift b/Tests/ResgenSwiftTests/Analytics/DiffString.swift similarity index 100% rename from Tests/ResgenSwiftTests/Tags/DiffString.swift rename to Tests/ResgenSwiftTests/Analytics/DiffString.swift