Skip to content

Feature: Environment Switcher #316

@MaatheusGois

Description

@MaatheusGois

📋 Feature Description

Add an Environment Switcher to DebugSwift that allows developers to quickly toggle between different environments (dev, staging, production) and manage environment-specific configurations.

🎯 Motivation

Most apps support multiple environments with different:

  • API base URLs
  • Feature flags
  • Authentication endpoints
  • Analytics keys
  • Third-party SDK configurations
  • Database connections

Currently, developers switch environments by:

  • Changing build schemes
  • Modifying configuration files
  • Rebuilding the app

This is time-consuming and error-prone. An in-app environment switcher would dramatically improve development workflow.

✨ Proposed Features

Core Functionality

  • Quick Environment Switch: Toggle environments instantly
  • Environment Profiles: Predefined environment configurations
  • Custom Environments: Create and manage custom environments
  • Configuration Management: Store environment-specific settings
  • Auto Data Clear: Optionally clear data on environment switch
  • Environment Indicator: Visual badge showing current environment

Environment Configuration

  • API Base URL: Environment-specific endpoints
  • Feature Flags: Enable/disable features per environment
  • Authentication: Different auth servers
  • Analytics: Environment-specific tracking
  • Database: Separate databases per environment
  • Custom Keys: Any environment-specific values

Advanced Features

  • Environment History: Track environment switches
  • Quick Switch Gesture: Shake or swipe to change environment
  • Environment Comparison: Compare settings across environments
  • Import/Export: Share environment configurations
  • Network Stub Integration: Auto-mock for specific environments
  • Certificate Pinning: Per-environment SSL configurations

🎨 UI/UX Design

Environment Switcher Screen

┌─────────────────────────────────────────┐
│ Environment Switcher          [✓ Active] │
├─────────────────────────────────────────┤
│ Current Environment: Production         │
├─────────────────────────────────────────┤
│ Available Environments                   │
│                                          │
│ ● Development                           │
│   https://dev-api.example.com           │
│   [Select]                              │
│                                          │
│ ◯ Staging                               │
│   https://staging-api.example.com       │
│   [Select]                              │
│                                          │
│ ◯ Production (Current)                  │
│   https://api.example.com               │
│   [✓ Active]                            │
│                                          │
│ ◯ QA                                    │
│   https://qa-api.example.com            │
│   [Select]                              │
├─────────────────────────────────────────┤
│ [+ Add Custom Environment]              │
├─────────────────────────────────────────┤
│ Options                                  │
│ ☑ Clear network history on switch      │
│ ☑ Clear user data on switch            │
│ ☑ Restart app after switch             │
│ ☐ Show environment badge               │
└─────────────────────────────────────────┘

Environment Detail Screen

┌─────────────────────────────────────────┐
│ < Environments    Development      [Edit]│
├─────────────────────────────────────────┤
│ General                                  │
│ Name: Development                       │
│ Icon: 🔧                                │
│ Color: Blue                             │
├─────────────────────────────────────────┤
│ API Configuration                        │
│ Base URL:                               │
│   https://dev-api.example.com           │
│                                          │
│ Auth URL:                               │
│   https://dev-auth.example.com          │
│                                          │
│ GraphQL Endpoint:                       │
│   https://dev-api.example.com/graphql   │
├─────────────────────────────────────────┤
│ Feature Flags                           │
│ ☑ New UI Enabled                        │
│ ☑ Debug Logging                         │
│ ☐ Beta Features                         │
│ ☑ Mock Payments                         │
├─────────────────────────────────────────┤
│ Custom Configuration                     │
│ • Analytics Key: dev-analytics-123      │
│ • Database Name: app_dev.db             │
│ • Mock Data: Enabled                    │
│                                          │
│ [+ Add Custom Key]                      │
├─────────────────────────────────────────┤
│ Actions                                  │
│ [Export Configuration]                   │
│ [Duplicate Environment]                  │
│ [Delete Environment]                     │
└─────────────────────────────────────────┘

Environment Badge (when enabled)

┌────────────────────────────┐
│ App Screen        [DEV 🔧] │  <- Badge in corner
│                            │
│                            │
└────────────────────────────┘

🔧 Technical Implementation

Architecture

public class EnvironmentManager {
    public static let shared = EnvironmentManager()
    
    public var currentEnvironment: Environment {
        didSet {
            handleEnvironmentChange(from: oldValue, to: currentEnvironment)
        }
    }
    
    public var availableEnvironments: [Environment]
    public var onEnvironmentChanged: ((Environment, Environment) -> Void)?
    
    public func switchEnvironment(_ environment: Environment, clearData: Bool = true)
    public func addCustomEnvironment(_ environment: Environment)
    public func exportConfiguration() -> Data
    public func importConfiguration(_ data: Data) throws
}

public struct Environment: Codable, Identifiable {
    public let id: UUID
    public var name: String
    public var icon: String
    public var color: UIColor
    public var configuration: EnvironmentConfiguration
    
    public init(
        name: String,
        icon: String = "🌍",
        color: UIColor = .blue,
        configuration: EnvironmentConfiguration
    )
}

public struct EnvironmentConfiguration: Codable {
    public var apiBaseURL: String
    public var authURL: String?
    public var graphQLEndpoint: String?
    public var featureFlags: [String: Bool]
    public var customValues: [String: String]
    
    // Convenience accessors
    public func value(forKey key: String) -> String?
    public func bool(forKey key: String) -> Bool
    public func isFeatureEnabled(_ feature: String) -> Bool
}

Usage in App

// Define environments in AppDelegate or App struct
func setupEnvironments() {
    let development = Environment(
        name: "Development",
        icon: "🔧",
        color: .blue,
        configuration: EnvironmentConfiguration(
            apiBaseURL: "https://dev-api.example.com",
            authURL: "https://dev-auth.example.com",
            featureFlags: [
                "newUI": true,
                "debugLogging": true,
                "mockPayments": true
            ],
            customValues: [
                "analyticsKey": "dev-analytics-123",
                "databaseName": "app_dev.db"
            ]
        )
    )
    
    let staging = Environment(
        name: "Staging",
        icon: "🧪",
        color: .orange,
        configuration: EnvironmentConfiguration(
            apiBaseURL: "https://staging-api.example.com",
            authURL: "https://staging-auth.example.com",
            featureFlags: [
                "newUI": true,
                "debugLogging": false,
                "mockPayments": false
            ],
            customValues: [
                "analyticsKey": "staging-analytics-456"
            ]
        )
    )
    
    let production = Environment(
        name: "Production",
        icon: "🚀",
        color: .green,
        configuration: EnvironmentConfiguration(
            apiBaseURL: "https://api.example.com",
            authURL: "https://auth.example.com",
            featureFlags: [:],
            customValues: [:]
        )
    )
    
    DebugSwift.Environment.availableEnvironments = [
        development, staging, production
    ]
    
    DebugSwift.Environment.currentEnvironment = development
}

// Access environment configuration
let apiURL = DebugSwift.Environment.current.configuration.apiBaseURL
let isFeatureEnabled = DebugSwift.Environment.current.configuration.isFeatureEnabled("newUI")

// Listen for environment changes
DebugSwift.Environment.onEnvironmentChanged = { old, new in
    print("Switched from \(old.name) to \(new.name)")
    
    // Clear network history
    DebugSwift.Network.shared.clearNetworkHistory()
    
    // Reconnect services
    APIClient.shared.reconnect()
    
    // Optionally restart app
    if DebugSwift.Environment.restartOnSwitch {
        exit(0) // System will restart the app
    }
}

Environment Badge

class EnvironmentBadgeView: UIView {
    private let label = UILabel()
    
    func show(environment: Environment) {
        label.text = "\(environment.icon) \(environment.name)"
        backgroundColor = environment.color.withAlphaComponent(0.8)
        
        // Position in top-right corner
        if let window = UIApplication.shared.windows.first {
            window.addSubview(self)
            self.frame = CGRect(
                x: window.bounds.width - 100,
                y: window.safeAreaInsets.top,
                width: 90,
                height: 30
            )
        }
    }
}

Environment Persistence

extension EnvironmentManager {
    private let currentEnvironmentKey = "debugswift.current.environment"
    
    func saveCurrentEnvironment() {
        if let data = try? JSONEncoder().encode(currentEnvironment) {
            UserDefaults.standard.set(data, forKey: currentEnvironmentKey)
        }
    }
    
    func loadCurrentEnvironment() {
        guard let data = UserDefaults.standard.data(forKey: currentEnvironmentKey),
              let environment = try? JSONDecoder().decode(Environment.self, from: data) else {
            return
        }
        currentEnvironment = environment
    }
}

📝 Implementation Checklist

Phase 1: Basic Environment Switching

  • Environment data structure
  • Environment storage and persistence
  • Basic environment switcher UI
  • Environment configuration management
  • Current environment indicator

Phase 2: Enhanced Features

  • Custom environment creation
  • Feature flags management
  • Auto data clear on switch
  • Environment badge overlay
  • Import/export configurations

Phase 3: Advanced Features

  • Quick switch gesture (shake)
  • Environment comparison view
  • History tracking
  • Network integration (auto-clear)
  • Certificate pinning per environment
  • Environment validation

🧪 Testing Requirements

  • Environment switching tests
  • Configuration persistence tests
  • Feature flag tests
  • Import/export tests
  • Data clearing tests
  • UI state restoration tests

📚 Documentation

### Environment Switcher
- Quick toggle between dev/staging/production
- Environment-specific configurations
- Feature flags management
- Auto data clearing on switch
- Custom environments

// Setup environments
DebugSwift.Environment.configure(
    environments: [development, staging, production],
    default: development
)

// Access current environment
let apiURL = DebugSwift.Environment.current.apiBaseURL

// Listen for changes
DebugSwift.Environment.onEnvironmentChanged = { old, new in
    // Handle environment switch
}

🎯 Success Criteria

  • Switch environments without rebuilding
  • Manage environment-specific configurations
  • Clear data automatically on switch
  • Visual environment indicator
  • Import/export configurations
  • Feature flags per environment
  • Minimal setup required
  • Persist environment across launches

📊 Priority

High - Major developer productivity improvement.

🏷️ Labels

enhancement, feature, app-tools, developer-experience, high-priority

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions