From 107cf6de141a17ff16e6688dff0f90c6a5d74228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Costa?= Date: Tue, 26 May 2026 15:36:46 +0100 Subject: [PATCH 1/3] Fix missing optional chaining for Swift people properties When a people property has an optional enum type (e.g. ReverseTrialType?), the generated Swift code emitted `value.rawValue` instead of `value?.rawValue`, causing a compilation error. Pass `is_optional` through to the template so it can emit the `?` for optional chaining. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/evva/swift_generator.rb | 1 + lib/evva/templates/swift/people_properties.swift | 2 +- spec/lib/evva/swift_generator_spec.rb | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/evva/swift_generator.rb b/lib/evva/swift_generator.rb index 9623760..18e086f 100644 --- a/lib/evva/swift_generator.rb +++ b/lib/evva/swift_generator.rb @@ -64,6 +64,7 @@ def people_properties(people_bundle, _file_name, _enums_file_name, _destinations property_name: p.property_name, type: type, is_special_property: special_property?(type), + is_optional: type.end_with?("?"), destinations: p.destinations.map { |p| camelize(p) }, } end diff --git a/lib/evva/templates/swift/people_properties.swift b/lib/evva/templates/swift/people_properties.swift index e4b80ee..9282f5f 100644 --- a/lib/evva/templates/swift/people_properties.swift +++ b/lib/evva/templates/swift/people_properties.swift @@ -40,7 +40,7 @@ enum Property { <%- properties.each_with_index do |p, index| -%> case let .<%= p[:case_name] %>(value): return PropertyData(type: .<%= p[:case_name] %>, - value: value<% if p[:is_special_property] %>.rawValue<% end %>) + value: value<% if p[:is_special_property] %><%= "?" if p[:is_optional] %>.rawValue<% end %>) <%- unless index == properties.count - 1 -%> <%- end -%> diff --git a/spec/lib/evva/swift_generator_spec.rb b/spec/lib/evva/swift_generator_spec.rb index 370b36f..58c0473 100644 --- a/spec/lib/evva/swift_generator_spec.rb +++ b/spec/lib/evva/swift_generator_spec.rb @@ -144,6 +144,7 @@ let(:people_bundle) { [ Evva::AnalyticsProperty.new("rounds_with_wear", "String", ["firebase"]), Evva::AnalyticsProperty.new("wear_platform", "WearableAppPlatform", ["firebase", "custom destination"]), + Evva::AnalyticsProperty.new("reverse_trial_type", "ReverseTrialType?", ["firebase"]), Evva::AnalyticsProperty.new("number_of_times_it_happened", "Long", []), ] } @@ -173,6 +174,7 @@ enum PropertyType: String { case roundsWithWear = "rounds_with_wear" case wearPlatform = "wear_platform" + case reverseTrialType = "reverse_trial_type" case numberOfTimesItHappened = "number_of_times_it_happened" var name: String { return rawValue } @@ -181,6 +183,7 @@ switch self { case .roundsWithWear: return [.firebase] case .wearPlatform: return [.firebase, .customDestination] + case .reverseTrialType: return [.firebase] case .numberOfTimesItHappened: return [] } } @@ -189,6 +192,7 @@ enum Property { case roundsWithWear(String) case wearPlatform(WearableAppPlatform) + case reverseTrialType(ReverseTrialType?) case numberOfTimesItHappened(Int) var data: PropertyData { @@ -201,6 +205,10 @@ return PropertyData(type: .wearPlatform, value: value.rawValue) + case let .reverseTrialType(value): + return PropertyData(type: .reverseTrialType, + value: value?.rawValue) + case let .numberOfTimesItHappened(value): return PropertyData(type: .numberOfTimesItHappened, value: value) From bb8618459fe46954672cea2a7a07e0190aa9ea26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Costa?= Date: Tue, 26 May 2026 15:41:45 +0100 Subject: [PATCH 2/3] Escape Swift reserved keywords in generated enum case names When an enum value like "default" is camelized, the generated Swift code emits `case default = "default"` which is a parse error since `default` is a reserved keyword. Wrap such names in backticks to produce valid Swift. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/evva/swift_generator.rb | 2 ++ spec/lib/evva/swift_generator_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lib/evva/swift_generator.rb b/lib/evva/swift_generator.rb index 18e086f..82bcfac 100644 --- a/lib/evva/swift_generator.rb +++ b/lib/evva/swift_generator.rb @@ -11,6 +11,7 @@ class SwiftGenerator TAB_SIZE = " " # \t -> 4 spaces NATIVE_TYPES = %w[Int String Double Float Bool Date].freeze + SWIFT_KEYWORDS = %w[default].freeze def initialize(swift_public: false) @swift_public_modifier = swift_public ? "public " : "" @@ -137,6 +138,7 @@ def camelize(term) string = string.sub(/^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase } string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" } string.gsub!("/".freeze, "::".freeze) + string = "`#{string}`" if SWIFT_KEYWORDS.include?(string) string end end diff --git a/spec/lib/evva/swift_generator_spec.rb b/spec/lib/evva/swift_generator_spec.rb index 58c0473..acf6515 100644 --- a/spec/lib/evva/swift_generator_spec.rb +++ b/spec/lib/evva/swift_generator_spec.rb @@ -113,6 +113,7 @@ let(:enums) { [ Evva::AnalyticsEnum.new("CourseProfileSource", ["course_discovery", "synced_courses"]), Evva::AnalyticsEnum.new("PremiumFrom", ["Course Profile", "Round Setup"]), + Evva::AnalyticsEnum.new("PreAuthenticationScreenType", ["default", "self_improvement"]), ] } let(:expected) { @@ -131,6 +132,11 @@ case courseProfile = "Course Profile" case roundSetup = "Round Setup" } + + enum PreAuthenticationScreenType: String { + case `default` = "default" + case selfImprovement = "self_improvement" + } } Swift } From f7b0bb7ce21b12bf9a7e83b6b7c8d07d15dae444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Costa?= Date: Tue, 26 May 2026 15:46:27 +0100 Subject: [PATCH 3/3] Remove redundant public modifier from Destination enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `public` on `enum Destination` is redundant when declared inside a `public extension` — Swift warns about this. Other templates (events, people_properties) don't add it to their inner types either. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/evva/templates/swift/destinations.swift | 2 +- spec/lib/evva/swift_generator_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/evva/templates/swift/destinations.swift b/lib/evva/templates/swift/destinations.swift index a01d239..bf144c5 100644 --- a/lib/evva/templates/swift/destinations.swift +++ b/lib/evva/templates/swift/destinations.swift @@ -1,4 +1,4 @@ -<%= @swift_public_modifier %>enum Destination { +enum Destination { <%- destinations.each do |d| -%> case <%= d %> <%- end -%> diff --git a/spec/lib/evva/swift_generator_spec.rb b/spec/lib/evva/swift_generator_spec.rb index acf6515..027d6d6 100644 --- a/spec/lib/evva/swift_generator_spec.rb +++ b/spec/lib/evva/swift_generator_spec.rb @@ -263,7 +263,7 @@ import Foundation public extension Analytics { - public enum Destination { + enum Destination { case firebase case whateverYouWantReally }