From ee5055efa5578dc56e3c98bfa4f8e9afe546200c Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Fri, 8 Dec 2023 17:37:21 +0100 Subject: [PATCH 1/7] Retours sur la structure du code --- Sources/ResgenSwift/Analytics/Analytics.swift | 23 ++-- .../Generator/AnalyticsGenerator.swift | 30 ++--- .../Generator/FirebaseGenerator.swift | 23 ++-- .../Analytics/Generator/MatomoGenerator.swift | 27 +++-- .../Analytics/Model/AnalyticsCategory.swift | 4 + .../Analytics/Model/AnalyticsDefinition.swift | 35 ++---- .../Analytics/Model/AnalyticsFile.swift | 6 +- .../Analytics/Model/AnalyticsParameter.swift | 2 + .../ResgenSwift/Analytics/Model/TagType.swift | 16 +++ .../Analytics/Model/TargetType.swift | 22 ++++ .../Parser/AnalyticsFileParser.swift | 112 +++++++++--------- Sources/ToolCore/StringExtensions.swift | 17 ++- .../Analytics/DiffString.swift | 3 +- 13 files changed, 177 insertions(+), 143 deletions(-) create mode 100644 Sources/ResgenSwift/Analytics/Model/TagType.swift create mode 100644 Sources/ResgenSwift/Analytics/Model/TargetType.swift diff --git a/Sources/ResgenSwift/Analytics/Analytics.swift b/Sources/ResgenSwift/Analytics/Analytics.swift index ef42c9c..d245e39 100644 --- a/Sources/ResgenSwift/Analytics/Analytics.swift +++ b/Sources/ResgenSwift/Analytics/Analytics.swift @@ -33,6 +33,10 @@ struct Analytics: ParsableCommand { mutating func run() { print("[\(Self.toolName)] Starting analytics generation") print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate analytics for target: \(options.target)") + + // Check requirements + guard checkRequirements() else { return } + print("[\(Self.toolName)] Will generate analytics") // Check requirements @@ -76,18 +80,11 @@ struct Analytics: ParsableCommand { } } -extension Analytics { - enum TargetType: CaseIterable { - case matomo - case firebase - - var value: String { - switch self { - case .matomo: - "matomo" - case .firebase: - "firebase" - } - } + // MARK: - Requirements + + private func checkRequirements() -> Bool { + // Check les requirements et gestion des erreurs + // Il faut que toutes les erreurs générées par Analytics soit des AnalyticsError + true } } diff --git a/Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift b/Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift index cd7adb3..a4a9e1f 100644 --- a/Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift +++ b/Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift @@ -10,13 +10,13 @@ import ToolCore import CoreVideo class AnalyticsGenerator { - static var targets: [Analytics.TargetType] = [] - + static var targets: [TrackerType] = [] + static func writeExtensionFiles(sections: [AnalyticsCategory], target: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { // Get target type from enum let targetsString: [String] = target.components(separatedBy: " ") - Analytics.TargetType.allCases.forEach { enumTarget in + TrackerType.allCases.forEach { enumTarget in if targetsString.contains(enumTarget.value) { targets.append(enumTarget) } @@ -58,7 +58,7 @@ class AnalyticsGenerator { \(Self.getImport()) - \(Self.getAnalytics()) + \(Self.getAnalyticsProtocol()) // MARK: - Manager class AnalyticsManager { @@ -91,10 +91,10 @@ class AnalyticsGenerator { private static func getImport() -> String { var result: [String] = [] - if targets.contains(Analytics.TargetType.matomo) { + if targets.contains(TrackerType.matomo) { result.append("import MatomoTracker") } - if targets.contains(Analytics.TargetType.firebase) { + if targets.contains(TrackerType.firebase) { result.append("import FirebaseAnalytics") } @@ -136,13 +136,13 @@ class AnalyticsGenerator { var content: [String] = [] let footer = " }" - if targets.contains(Analytics.TargetType.matomo) { + if targets.contains(TrackerType.matomo) { header = "func configure(siteId: String, url: String) {" - } else if targets.contains(Analytics.TargetType.firebase) { + } else if targets.contains(TrackerType.firebase) { header = "func configure() {" } - if targets.contains(Analytics.TargetType.matomo) { + if targets.contains(TrackerType.matomo) { content.append(""" managers.append( MatomoAnalyticsManager( @@ -152,7 +152,7 @@ class AnalyticsGenerator { ) """) } - if targets.contains(Analytics.TargetType.firebase) { + if targets.contains(TrackerType.firebase) { content.append(" managers.append(FirebaseAnalyticsManager())") } @@ -164,7 +164,7 @@ class AnalyticsGenerator { .joined(separator: "\n") } - private static func getAnalytics() -> String { + private static func getAnalyticsProtocol() -> String { let proto = """ // MARK: - Protocol @@ -182,12 +182,12 @@ class AnalyticsGenerator { var result: [String] = [proto] - if targets.contains(Analytics.TargetType.matomo) { - result.append(MatomoGenerator.service.content) + if targets.contains(TrackerType.matomo) { + result.append(MatomoGenerator.service) } - if targets.contains(Analytics.TargetType.firebase) { - result.append(FirebaseGenerator.service.content) + if targets.contains(TrackerType.firebase) { + result.append(FirebaseGenerator.service) } return result.joined(separator: "\n") diff --git a/Sources/ResgenSwift/Analytics/Generator/FirebaseGenerator.swift b/Sources/ResgenSwift/Analytics/Generator/FirebaseGenerator.swift index 4352b0e..95ab7f4 100644 --- a/Sources/ResgenSwift/Analytics/Generator/FirebaseGenerator.swift +++ b/Sources/ResgenSwift/Analytics/Generator/FirebaseGenerator.swift @@ -8,19 +8,20 @@ import Foundation enum FirebaseGenerator { - case service - - var content: String { + + static var service: String { [ - FirebaseGenerator.service.header, - FirebaseGenerator.service.logScreen, - FirebaseGenerator.service.logEvent, - FirebaseGenerator.service.footer + FirebaseGenerator.header, + FirebaseGenerator.logScreen, + FirebaseGenerator.logEvent, + FirebaseGenerator.footer ] .joined(separator: "\n") } - private var header: String { + // MARK: - Private vars + + private static var header: String { """ // MARK: - Firebase @@ -28,7 +29,7 @@ enum FirebaseGenerator { """ } - private var logScreen: String { + private static var logScreen: String { """ func logScreen(name: String, path: String) { var parameters = [ @@ -44,7 +45,7 @@ enum FirebaseGenerator { """ } - private var logEvent: String { + private static var logEvent: String { """ func logEvent( name: String, @@ -71,7 +72,7 @@ enum FirebaseGenerator { """ } - private var footer: String { + private static var footer: String { """ } diff --git a/Sources/ResgenSwift/Analytics/Generator/MatomoGenerator.swift b/Sources/ResgenSwift/Analytics/Generator/MatomoGenerator.swift index 739e8cb..740cf95 100644 --- a/Sources/ResgenSwift/Analytics/Generator/MatomoGenerator.swift +++ b/Sources/ResgenSwift/Analytics/Generator/MatomoGenerator.swift @@ -8,20 +8,21 @@ import Foundation enum MatomoGenerator { - case service - - var content: String { + + static var service: String { [ - MatomoGenerator.service.header, - MatomoGenerator.service.setup, - MatomoGenerator.service.logScreen, - MatomoGenerator.service.logEvent, - MatomoGenerator.service.footer + MatomoGenerator.header, + MatomoGenerator.setup, + MatomoGenerator.logScreen, + MatomoGenerator.logEvent, + MatomoGenerator.footer ] .joined(separator: "\n") } - private var header: String { + // MARK: - Private vars + + private static var header: String { """ // MARK: - Matomo @@ -34,7 +35,7 @@ enum MatomoGenerator { """ } - private var setup: String { + private static var setup: String { """ // MARK: - Init @@ -63,7 +64,7 @@ enum MatomoGenerator { """ } - private var logScreen: String { + private static var logScreen: String { """ func logScreen(name: String, path: String) { guard !tracker.isOptedOut else { return } @@ -79,7 +80,7 @@ enum MatomoGenerator { """ } - private var logEvent: String { + private static var logEvent: String { """ func logEvent( name: String, @@ -100,7 +101,7 @@ enum MatomoGenerator { """ } - private var footer: String { + private static var footer: String { """ } diff --git a/Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift b/Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift index 9cec892..9451d99 100644 --- a/Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift +++ b/Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift @@ -11,10 +11,14 @@ class AnalyticsCategory { let id: String // OnBoarding var definitions = [AnalyticsDefinition]() + // MARK: - Init + init(id: String) { self.id = id } + // MARK: - Methods + func hasOneOrMoreMatchingTags(tags: [String]) -> Bool { let allTags = definitions.flatMap { $0.tags } let allTagsSet = Set(allTags) diff --git a/Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift b/Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift index 9c7cadb..acef609 100644 --- a/Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift +++ b/Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift @@ -6,6 +6,7 @@ // import Foundation +import ToolCore class AnalyticsDefinition { let id: String @@ -18,12 +19,16 @@ class AnalyticsDefinition { var parameters: [AnalyticsParameter] = [] var type: TagType + // MARK: - Init + init(id: String, name: String, type: TagType) { self.id = id self.name = name self.type = type } + // MARK: - Methods + func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool { if Set(inputTags).intersection(Set(self.tags)).isEmpty { return false @@ -31,8 +36,8 @@ class AnalyticsDefinition { return true } - // MARK: - Methods - + // MARK: - Private Methods + private func getFuncName() -> String { var pascalCaseTitle: String = "" id.components(separatedBy: "_").forEach { word in @@ -96,7 +101,7 @@ class AnalyticsDefinition { supplementaryParams.forEach { param in params.append("\"\(param.name)\": \(param.name)") } - + if params.count > 1 { result = """ [ @@ -150,27 +155,3 @@ class AnalyticsDefinition { """ } } - -extension AnalyticsDefinition { - enum TagType { - case screen - case event - } -} - -extension String { - func replacingFirstOccurrence(of: String, with: String) -> Self { - if let range = self.range(of: of) { - let tmp = self.replacingOccurrences( - of: of, - with: with, - options: .literal, - range: range - ) - - return tmp - } - - return self - } -} diff --git a/Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift b/Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift index 5c4f690..d775768 100644 --- a/Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift +++ b/Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift @@ -17,9 +17,7 @@ struct AnalyticsCategoryDTO: Codable { var events: [AnalyticsDefinitionEventDTO]? } -protocol AnalyticsDefinitionDTO: Codable {} - -struct AnalyticsDefinitionScreenDTO: AnalyticsDefinitionDTO { +struct AnalyticsDefinitionScreenDTO: Codable { var id: String var name: String var tags: String @@ -29,7 +27,7 @@ struct AnalyticsDefinitionScreenDTO: AnalyticsDefinitionDTO { var path: String? } -struct AnalyticsDefinitionEventDTO: AnalyticsDefinitionDTO { +struct AnalyticsDefinitionEventDTO: Codable { var id: String var name: String var tags: String diff --git a/Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift b/Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift index e7f1079..9c659c3 100644 --- a/Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift +++ b/Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift @@ -12,6 +12,8 @@ class AnalyticsParameter { var type: String var replaceIn: [String] = [] + // MARK: - Init + init(name: String, type: String) { self.name = name self.type = type diff --git a/Sources/ResgenSwift/Analytics/Model/TagType.swift b/Sources/ResgenSwift/Analytics/Model/TagType.swift new file mode 100644 index 0000000..62d3d28 --- /dev/null +++ b/Sources/ResgenSwift/Analytics/Model/TagType.swift @@ -0,0 +1,16 @@ +// +// TagType.swift +// +// +// Created by Thibaut Schmitt on 08/12/2023. +// + +import Foundation + +extension AnalyticsDefinition { + + enum TagType { + case screen + case event + } +} diff --git a/Sources/ResgenSwift/Analytics/Model/TargetType.swift b/Sources/ResgenSwift/Analytics/Model/TargetType.swift new file mode 100644 index 0000000..c9b5649 --- /dev/null +++ b/Sources/ResgenSwift/Analytics/Model/TargetType.swift @@ -0,0 +1,22 @@ +// +// TargetType.swift +// +// +// Created by Thibaut Schmitt on 08/12/2023. +// + +import Foundation + +enum TrackerType: CaseIterable { + case matomo + case firebase + + var value: String { + switch self { + case .matomo: + "matomo" + case .firebase: + "firebase" + } + } +} diff --git a/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift b/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift index 0ad912e..9a5b429 100644 --- a/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift +++ b/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift @@ -25,11 +25,9 @@ class AnalyticsFileParser { Analytics.exit(withError: error) } } - - private static func getParameters(fromData data: [AnalyticsParameterDTO]) -> [AnalyticsParameter] { - var parameters: [AnalyticsParameter] = [] - - data.forEach { value in + + private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] { + parameters.map { dtoParameter in // Type let type = value.type.uppercasedFirst() @@ -43,17 +41,18 @@ class AnalyticsFileParser { let error = AnalyticsError.invalidParameter("type of \(value.name)") Analytics.exit(withError: error) } - - let parameter: AnalyticsParameter = AnalyticsParameter(name: value.name, type: type) - - if let replaceIn = value.replaceIn { + + let parameter = AnalyticsParameter( + name: dtoParameter.name, + type: type + ) + + if let replaceIn = dtoParameter.replaceIn { parameter.replaceIn = replaceIn.components(separatedBy: ",") } - - parameters.append(parameter) + + return parameter } - - return parameters } private static func getTagDefinition( @@ -64,24 +63,24 @@ class AnalyticsFileParser { comments: String?, parameters: [AnalyticsParameterDTO]? ) -> AnalyticsDefinition { - let definition: AnalyticsDefinition = AnalyticsDefinition(id: id, name: name, type: type) - definition.tags = tags.components(separatedBy: ",") - + let definition = AnalyticsDefinition(id: id, name: name, type: type) + definition.tags = tags + .components(separatedBy: ",") + .map { $0.removeLeadingTrailingWhitespace() } + if let comments = comments { definition.comments = comments } if let parameters = parameters { - definition.parameters = Self.getParameters(fromData: parameters) + definition.parameters = Self.getParameters(from: parameters) } return definition } - private static func getTagDefinitionScreen(fromData screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] { - var definitions: [AnalyticsDefinition] = [] - - for screen in screens { + private static func getTagDefinitionScreen(from screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] { + screens.map { screen in let definition: AnalyticsDefinition = Self.getTagDefinition( id: screen.id, name: screen.name, @@ -90,10 +89,10 @@ class AnalyticsFileParser { comments: screen.comments, parameters: screen.parameters ) - + if target.contains(Analytics.TargetType.matomo.value) { // Path - + guard let path = screen.path else { let error = AnalyticsError.missingElement("screen path") Analytics.exit(withError: error) @@ -101,17 +100,13 @@ class AnalyticsFileParser { definition.path = path } - - definitions.append(definition) + + return definition } - - return definitions } - private static func getTagDefinitionEvent(fromData events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] { - var definitions: [AnalyticsDefinition] = [] - - for event in events { + private static func getTagDefinitionEvent(from events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] { + events.map { event in let definition: AnalyticsDefinition = Self.getTagDefinition( id: event.id, name: event.name, @@ -120,54 +115,57 @@ class AnalyticsFileParser { comments: event.comments, parameters: event.parameters ) - + if target.contains(Analytics.TargetType.matomo.value) { // Category - guard let category = event.category else { let error = AnalyticsError.missingElement("event category") Analytics.exit(withError: error) } - + definition.category = category - + // Action - guard let action = event.action else { let error = AnalyticsError.missingElement("event action") Analytics.exit(withError: error) } - + definition.action = action } - - definitions.append(definition) + + return 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] = [] + let tagFile = Self.parseYaml() - 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)) + return tagFile + .categories + .map { categorie in + let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id) + + if let screens = categorie.screens { + section + .definitions + .append( + contentsOf: Self.getTagDefinitionScreen(from: screens) + ) + } + + if let events = categorie.events { + section + .definitions + .append( + contentsOf: Self.getTagDefinitionEvent(from: events) + ) + } + + return section } - - if let events = categorie.events { - section.definitions.append(contentsOf: Self.getTagDefinitionEvent(fromData: events)) - } - - sections.append(section) - } - - return sections } } diff --git a/Sources/ToolCore/StringExtensions.swift b/Sources/ToolCore/StringExtensions.swift index 52ea186..401f7a7 100644 --- a/Sources/ToolCore/StringExtensions.swift +++ b/Sources/ToolCore/StringExtensions.swift @@ -63,7 +63,7 @@ public extension String { replacingOccurrences(of: "~", with: "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)") } - func colorComponent() -> (alpha: String, red: String, green: String, blue: String) { + func colorComponent() -> (alpha: String, red: String, green: String, blue: String) { var alpha: String = "FF" var red: String var green: String @@ -89,4 +89,19 @@ public extension String { func uppercasedFirst() -> String { prefix(1).uppercased() + dropFirst() } + + func replacingFirstOccurrence(of: String, with: String) -> Self { + if let range = self.range(of: of) { + let tmp = self.replacingOccurrences( + of: of, + with: with, + options: .literal, + range: range + ) + + return tmp + } + + return self + } } diff --git a/Tests/ResgenSwiftTests/Analytics/DiffString.swift b/Tests/ResgenSwiftTests/Analytics/DiffString.swift index 60f41cf..182eb23 100644 --- a/Tests/ResgenSwiftTests/Analytics/DiffString.swift +++ b/Tests/ResgenSwiftTests/Analytics/DiffString.swift @@ -1,5 +1,5 @@ // -// File.swift +// DiffString.swift // // // Created by Loris Perret on 06/12/2023. @@ -7,7 +7,6 @@ import Foundation - /// Find first differing character between two strings /// /// :param: s1 First String From f1b62d83c495c10d4bff26c4efb17df798465c7b Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Mon, 11 Dec 2023 10:29:19 +0100 Subject: [PATCH 2/7] Add missing print error --- .../ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift b/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift index 9a5b429..f4e18ce 100644 --- a/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift +++ b/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift @@ -15,6 +15,7 @@ class AnalyticsFileParser { private static func parseYaml() -> AnalyticsFile { guard let data = FileManager().contents(atPath: inputFile) else { let error = AnalyticsError.fileNotExists(inputFile) + print(error.description) Analytics.exit(withError: error) } @@ -22,6 +23,7 @@ class AnalyticsFileParser { let tagFile = try YAMLDecoder().decode(AnalyticsFile.self, from: data) return tagFile } catch let error { + print(error.description) Analytics.exit(withError: error) } } @@ -39,6 +41,7 @@ class AnalyticsFileParser { type == "Bool" else { let error = AnalyticsError.invalidParameter("type of \(value.name)") + print(error.description) Analytics.exit(withError: error) } @@ -95,6 +98,7 @@ class AnalyticsFileParser { guard let path = screen.path else { let error = AnalyticsError.missingElement("screen path") + print(error.description) Analytics.exit(withError: error) } @@ -120,6 +124,7 @@ class AnalyticsFileParser { // Category guard let category = event.category else { let error = AnalyticsError.missingElement("event category") + print(error.description) Analytics.exit(withError: error) } @@ -128,6 +133,7 @@ class AnalyticsFileParser { // Action guard let action = event.action else { let error = AnalyticsError.missingElement("event action") + print(error.description) Analytics.exit(withError: error) } From f6c49bf626dae431b076866a46a7e4565c3b5cbd Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Mon, 11 Dec 2023 11:19:19 +0100 Subject: [PATCH 3/7] Add parse error --- Sources/ResgenSwift/Analytics/Analytics.swift | 17 +++++++---------- .../ResgenSwift/Analytics/AnalyticsError.swift | 11 +++++++++-- .../Analytics/Model/TargetType.swift | 7 +++++++ .../Analytics/Parser/AnalyticsFileParser.swift | 13 +++++++------ .../Analytics/AnalyticsGeneratorTests.swift | 6 +++--- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/Sources/ResgenSwift/Analytics/Analytics.swift b/Sources/ResgenSwift/Analytics/Analytics.swift index d245e39..e9c0e13 100644 --- a/Sources/ResgenSwift/Analytics/Analytics.swift +++ b/Sources/ResgenSwift/Analytics/Analytics.swift @@ -67,7 +67,13 @@ struct Analytics: ParsableCommand { print(error.description) Analytics.exit(withError: error) } - + + guard TrackerType.hasValidTarget(in: options.target) else { + let error = AnalyticsError.noValidTracker(options.target) + print(error.description) + Analytics.exit(withError: error) + } + // Check if needed to regenerate guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, @@ -79,12 +85,3 @@ struct Analytics: ParsableCommand { return true } } - - // MARK: - Requirements - - private func checkRequirements() -> Bool { - // Check les requirements et gestion des erreurs - // Il faut que toutes les erreurs générées par Analytics soit des AnalyticsError - true - } -} diff --git a/Sources/ResgenSwift/Analytics/AnalyticsError.swift b/Sources/ResgenSwift/Analytics/AnalyticsError.swift index 9674522..b32198d 100644 --- a/Sources/ResgenSwift/Analytics/AnalyticsError.swift +++ b/Sources/ResgenSwift/Analytics/AnalyticsError.swift @@ -8,16 +8,20 @@ import Foundation enum AnalyticsError: Error { + case noValidTracker(String) case fileNotExists(String) case missingElement(String) case invalidParameter(String) - + case parseFailed(String) case writeFile(String, String) var description: String { switch self { + case .noValidTracker(let inputTargets): + return "error: [\(Analytics.toolName)] '\(inputTargets)' ne contient aucun tracker valid" + case .fileNotExists(let filename): - return "error: [\(Analytics.toolName)] File \(filename) does not exists " + return "error: [\(Analytics.toolName)] File \(filename) does not exists" case .missingElement(let element): return "error: [\(Analytics.toolName)] Missing \(element) for Matomo" @@ -25,6 +29,9 @@ enum AnalyticsError: Error { case .invalidParameter(let reason): return "error: [\(Analytics.toolName)] Invalid parameter \(reason)" + case .parseFailed(let baseError): + return "error: [\(Analytics.toolName)] Parse input file failed: \(baseError)" + case .writeFile(let subErrorDescription, let filename): return "error: [\(Analytics.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)" } diff --git a/Sources/ResgenSwift/Analytics/Model/TargetType.swift b/Sources/ResgenSwift/Analytics/Model/TargetType.swift index c9b5649..412d5c3 100644 --- a/Sources/ResgenSwift/Analytics/Model/TargetType.swift +++ b/Sources/ResgenSwift/Analytics/Model/TargetType.swift @@ -19,4 +19,11 @@ enum TrackerType: CaseIterable { "firebase" } } + + static func hasValidTarget(in targets: String) -> Bool { + for tracker in Self.allCases where targets.contains(tracker.value) { + return true + } + return false + } } diff --git a/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift b/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift index f4e18ce..1137f8e 100644 --- a/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift +++ b/Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift @@ -22,7 +22,8 @@ class AnalyticsFileParser { do { let tagFile = try YAMLDecoder().decode(AnalyticsFile.self, from: data) return tagFile - } catch let error { + } catch { + let error = AnalyticsError.parseFailed(error.localizedDescription) print(error.description) Analytics.exit(withError: error) } @@ -32,15 +33,15 @@ class AnalyticsFileParser { parameters.map { dtoParameter in // Type - let type = value.type.uppercasedFirst() - + let type = dtoParameter.type.uppercasedFirst() + guard type == "String" || type == "Int" || type == "Double" || type == "Bool" else { - let error = AnalyticsError.invalidParameter("type of \(value.name)") + let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)") print(error.description) Analytics.exit(withError: error) } @@ -93,7 +94,7 @@ class AnalyticsFileParser { parameters: screen.parameters ) - if target.contains(Analytics.TargetType.matomo.value) { + if target.contains(TrackerType.matomo.value) { // Path guard let path = screen.path else { @@ -120,7 +121,7 @@ class AnalyticsFileParser { parameters: event.parameters ) - if target.contains(Analytics.TargetType.matomo.value) { + if target.contains(TrackerType.matomo.value) { // Category guard let category = event.category else { let error = AnalyticsError.missingElement("event category") diff --git a/Tests/ResgenSwiftTests/Analytics/AnalyticsGeneratorTests.swift b/Tests/ResgenSwiftTests/Analytics/AnalyticsGeneratorTests.swift index 88f9883..7abe69f 100644 --- a/Tests/ResgenSwiftTests/Analytics/AnalyticsGeneratorTests.swift +++ b/Tests/ResgenSwiftTests/Analytics/AnalyticsGeneratorTests.swift @@ -51,7 +51,7 @@ final class AnalyticsGeneratorTests: XCTestCase { ] // When - AnalyticsGenerator.targets = [Analytics.TargetType.firebase] + AnalyticsGenerator.targets = [TrackerType.firebase] let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, @@ -216,7 +216,7 @@ final class AnalyticsGeneratorTests: XCTestCase { ] // When - AnalyticsGenerator.targets = [Analytics.TargetType.matomo] + AnalyticsGenerator.targets = [TrackerType.matomo] let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, @@ -409,7 +409,7 @@ final class AnalyticsGeneratorTests: XCTestCase { ] // When - AnalyticsGenerator.targets = [Analytics.TargetType.matomo, Analytics.TargetType.firebase] + AnalyticsGenerator.targets = [TrackerType.matomo, TrackerType.firebase] let extensionContent = AnalyticsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], tags: ["ios", "iosonly"], staticVar: false, From 1d58fd5510c0ca68fdcc82105537c1be52716e61 Mon Sep 17 00:00:00 2001 From: Loris Perret Date: Tue, 12 Dec 2023 16:58:25 +0100 Subject: [PATCH 4/7] Edit sample file --- .../Tags/Generated/Analytics+GenAllScript.swift | 13 ++++++++----- SampleFiles/Tags/sampleTags.yml | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/SampleFiles/Tags/Generated/Analytics+GenAllScript.swift b/SampleFiles/Tags/Generated/Analytics+GenAllScript.swift index 95f5601..1b5bd44 100644 --- a/SampleFiles/Tags/Generated/Analytics+GenAllScript.swift +++ b/SampleFiles/Tags/Generated/Analytics+GenAllScript.swift @@ -169,19 +169,22 @@ class AnalyticsManager { // MARK: - section_one - func logScreenS1DefOne() { + func logScreenS1DefOne(title: String) { logScreen( - name: "s1 def one", - path: "s1_def_one/" + name: "s1 def one \(title)", + path: "s1_def_one/\(title)" ) } - func logEventS1DefTwo() { + func logEventS1DefTwo(title: String, count: String) { logEvent( name: "s1 def two", action: "test", category: "test", - params: [:] + params: [ + "title": title, + "count": count + ] ) } diff --git a/SampleFiles/Tags/sampleTags.yml b/SampleFiles/Tags/sampleTags.yml index 310acb4..4c91dd0 100644 --- a/SampleFiles/Tags/sampleTags.yml +++ b/SampleFiles/Tags/sampleTags.yml @@ -3,9 +3,13 @@ categories: - id: section_one screens: - id: s1_def_one - name: s1 def one - path: s1_def_one/ - tags: ios + name: s1 def one _TITLE_ + path: s1_def_one/_TITLE_ + tags: ios,droid + parameters: + - name: title + type: String + replaceIn: name,path events: - id: s1_def_two @@ -13,6 +17,11 @@ categories: action: test category: test tags: ios + parameters: + - name: title + type: String + - name: count + type: String - id: section_two screens: From 9b27f241973115c14668b9b65f0dbabedff92eac Mon Sep 17 00:00:00 2001 From: Loris Perret Date: Wed, 13 Dec 2023 10:20:53 +0100 Subject: [PATCH 5/7] docs: Add Analytics section in README --- README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/README.md b/README.md index eaf249e..1fcd5af 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,71 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \ > ⚠️ If extension name is not set or is `Tags`, it will generate the following typaloas `typealias Tags = String`. + +## Analytics + +Analytics will generate all you need to analyze UX with Matomo or Firebase Analytics. Input files are formatted in YAML. This command will generate a manager for each target and an AnalyticsManager. This is this one you will need to use. And it will generate a method for all tags you have declared in the YAML file. If you want to use Matomo, you will need to use the `configure()` method of AnalyticsManager to set up the `siteId` and the `url` of the site. + +```sh +swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \ + --target "matomo firebase" \ + --extension-output-path "./Analytics/Generated" \ + --extension-name "AppAnalytics" \ + --extension-suffix "GreatApp" \ + --static-members true +``` + + **Parameters** + +1. `-f`: force generation +2. Input tags file (must be YAML formatted) +3. `--target`: target with you will log UX +4. `--extension-output-path`: path where to generate generated extension +5. `--extension-name` *(optional)* : name of class to add the extension +6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppAnalytics+GreatApp.swift`) +7. `--static-members` *(optional)*: generate static properties or not + +> ⚠️ If extension name is not set or is `Analytics`, it will generate the following typaloas `typealias Analytics = String`. + +### YAML + +``` + - id: s1_def_one + name: s1 def one _TITLE_ + path: s1_def_one/_TITLE_ + action: Tap + category: User + tags: ios,droid + comments: + parameters: + - name: title + type: String + replaceIn: name,path +``` + +1. `id`: name of the method (method name will be composed of `log` + `Event|Screen` + `id`) +2. `name`: name of the tag +3. `path` *(optional with firebase)* : needed for matomo but not with firebase (log screen) +4. `action` *(optional with firebase)* : needed for matomo but not with firebase (log event) +5. `category` *(optional with firebase)* : needed for matomo but not with firebase (log event) +6. `tags`: which platform target +7. `comments` *(optional)* +8. `parameters` *(optional)* + + **Parameters** + +You can use parameters in generate methods. + +1. `name`: name of the parameter +2. `type`: type of the parameter (Int, String, Bool, Double) +3. `replaceIn` *(optional)* + +**Replace in** + +This is section is equivalent of `%s | %d | %f | %@`. You can put the content of the parameter in *name*, *path*, *action*, *category*. +You need to put `_` + `name of the parameter` + `_` in the target and which target you want in the value of `replaceIn`. + + ## Images Images generator will generate images assets along with extensions to access those images easily. From d8937f2de62a0d26b11f86c7c2770428dad2fc9f Mon Sep 17 00:00:00 2001 From: Loris Perret Date: Wed, 13 Dec 2023 10:23:31 +0100 Subject: [PATCH 6/7] docs: Add Analytics section in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1fcd5af..028a5c1 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ You can use parameters in generate methods. **Replace in** This is section is equivalent of `%s | %d | %f | %@`. You can put the content of the parameter in *name*, *path*, *action*, *category*. -You need to put `_` + `name of the parameter` + `_` in the target and which target you want in the value of `replaceIn`. +You need to put `_` + `NAME OF THE PARAMETER` + `_` in the target and which target you want in the value of `replaceIn`. (name need to be in uppercase) ## Images From d79af06c38b2c30c829100e2a0494e8da7d8b089 Mon Sep 17 00:00:00 2001 From: Loris Perret Date: Wed, 13 Dec 2023 10:27:35 +0100 Subject: [PATCH 7/7] docs: Add Analytics section in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 028a5c1..904878a 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \ ## Analytics -Analytics will generate all you need to analyze UX with Matomo or Firebase Analytics. Input files are formatted in YAML. This command will generate a manager for each target and an AnalyticsManager. This is this one you will need to use. And it will generate a method for all tags you have declared in the YAML file. If you want to use Matomo, you will need to use the `configure()` method of AnalyticsManager 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 swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \