Repository: Automattic/pocket-casts-ios
File: podcasts/Analytics/Adapters/Tracks/TracksAdapter.swift (Swift)

Code before:
        tracksService.eventNamePrefix = TracksConfig.prefix
        tracksService.authenticatedUserTypeKey = TracksConfig.userKey

        TracksLogging.delegate = TracksAdapterLoggingDelegate.shared
#if DEBUG
        FileLog.shared.console("TracksAdapter anonymous UUID \(anonymousUUID)")
#endif
        // Setup the rest of the properties
        reloadExPlat()
        updateUserProperties()
        addNotificationObservers()
        updateAuthenticationState()
    }

    func track(name: String, properties: [AnyHashable: Any]?) {
        #if DEBUG
        if let properties {
            validateProperties(properties)
        }
        #endif
        tracksService.trackEventName(name, withCustomProperties: properties)
    }

    private func validateProperties(_ properties: [AnyHashable: Any]) {
        guard let castedProperties = properties as? [String: Any] else {
            fatalError("Tracks event properties types keys must be a String")
        }

        for key in castedProperties.keys {
            if Self.reservedPropertyNames.contains(key) {
                fatalError("Tracks event properties key \(key) is reserved property name.")
            }

            let value = castedProperties[key]
            if !(value is Int || value is String || value is Double || value is Bool || value is Float) {
                fatalError("Tracks event properties value for `\(key)` must be of one the valid types: Int, String, Bool, Double, Float")
            }
        }
    }

    private var defaultProperties: [String: AnyHashable] {
        let hasSubscription = subscriptionData.hasActiveSubscription()
        let platform = subscriptionData.subscriptionPlatform()
        let type = hasSubscription ? subscriptionData.subscriptionType() : .none
        let tier = subscriptionData.subscriptionTier
        let frequency = hasSubscription ? subscriptionData.subscriptionFrequency() : .none
        let hasLifetime = subscriptionData.hasLifetimeGift()

        return [
            // General keys
            "user_is_logged_in": SyncManager.isUserLoggedIn(),

Review comment (bug):
Would it make sense for these `fatalError` to be `assertionFailure`? Just to triple check that it's never released in prod?

Code after:
        tracksService.eventNamePrefix = TracksConfig.prefix
        tracksService.authenticatedUserTypeKey = TracksConfig.userKey

        TracksLogging.delegate = TracksAdapterLoggingDelegate.shared
#if DEBUG
        FileLog.shared.console("TracksAdapter anonymous UUID \(anonymousUUID)")
#endif
        // Setup the rest of the properties
        reloadExPlat()
        updateUserProperties()
        addNotificationObservers()
        updateAuthenticationState()
    }

    func track(name: String, properties: [AnyHashable: Any]?) {
        #if DEBUG
        if let properties {
            validateProperties(properties)
        }
        #endif
        tracksService.trackEventName(name, withCustomProperties: properties)
    }

    private func validateProperties(_ properties: [AnyHashable: Any]) {
        guard let castedProperties = properties as? [String: Any] else {
            assertionFailure("Tracks event properties types keys must be a String")
            return
        }

        for key in castedProperties.keys {
            if Self.reservedPropertyNames.contains(key) {
                assertionFailure("Tracks event properties key `\(key)` is reserved property name.")
                return
            }

            let value = castedProperties[key]
            if !(value is Int || value is String || value is Double || value is Bool || value is Float) {
                assertionFailure("Tracks event properties value for `\(key)` must be of one the valid types: Int, String, Bool, Double, Float")
                return
            }
        }
    }

    private var defaultProperties: [String: AnyHashable] {
        let hasSubscription = subscriptionData.hasActiveSubscription()
        let platform = subscriptionData.subscriptionPlatform()
        let type = hasSubscription ? subscriptionData.subscriptionType() : .none
        let tier = subscriptionData.subscriptionTier
        let frequency = hasSubscription ? subscriptionData.subscriptionFrequency() : .none
        let hasLifetime = subscriptionData.hasLifetimeGift()