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