[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: inderdhir\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: SwiftLint\n\non:\n  pull_request:\n    paths:\n      - '.github/workflows/swiftlint.yml'\n      - '.swiftlint.yml'\n      - '**/*.swift'\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v1\n      - name: GitHub Action for SwiftLint (Different working directory)\n        uses: norio-nomura/action-swiftlint@3.2.1\n        env:\n          WORKING_DIRECTORY: Source"
  },
  {
    "path": ".gitignore",
    "content": "# Xcode\n.DS_Store\n\n# Backup files\n*~\n\n# JetBrains\n.idea/\n\n## Build generated\nbuild/\nDerivedData\n\n## Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\n\n## Other\n*.xccheckout\n*.moved-aside\n*.xcuserstate\n*.xcscmblueprint\n\n## Obj-C/Swift specific\n*.hmap\n*.ipa\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\nThe above adds CocoaPods, \n\nPods/\nPodfile.lock\n\n# Keys\nDatWeatherDoe/Resources/Keys.plist\n\n# Secrets\nConfig.xcconfig\n"
  },
  {
    "path": ".swiftformat",
    "content": "--disable trailingCommas"
  },
  {
    "path": ".swiftlint.yml",
    "content": "disabled_rules: # rule identifiers to exclude from running\n  - colon\n  - comma\n  - control_statement\n  - trailing_whitespace\n  - vertical_parameter_alignment\n  - opening_brace\nopt_in_rules: # some rules are only opt-in\n  - empty_count\n  # Find all the available rules by running:\n  # swiftlint rules\nincluded: # paths to include during linting. `--path` is ignored if present.\n  - DatWeatherDoe\nexcluded: # paths to ignore during linting. Takes precedence over `included`.\n  - Pods\n# configurable rules can be customized from this configuration file\n# configurable rules can be customized from this configuration file\n# binary rules can set their severity level\ncyclomatic_complexity:\n  ignores_case_statements: true\nforce_cast: warning # implicitly\nforce_try:\n  severity: warning # explicitly\n# rules that have both warning and error levels, can set just the warning level\n# implicitly\nline_length: 110\n# they can set both implicitly with an array\ntype_body_length:\n  - 300 # warning\n  - 400 # error\n# or they can set both explicitly\nfile_length:\n  warning: 500\n  error: 1200\n# naming rules can set warnings/errors for min_length and max_length\n# additionally they can set excluded names\ntype_name:\n  min_length: 4 # only warning\n  max_length: # warning and error\n    warning: 40\n    error: 50\n  excluded: iPhone # excluded via string\nidentifier_name:\n  min_length: # only min_length\n    error: 2 # only error\n  excluded: # excluded via string array\n    - id\n    - URL\n    - GlobalAPIKey\nfunction_body_length:\n  warning: 50\n  error: 100\nreporter: \"xcode\" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji)\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nContributions / pull requests are welcome!\n\nPlease note that the goal of this project to provide a lightweight menu bar app for weather at a glance on macOS (similar to weather app indicators on Ubuntu).\n\n**MacOS 11 provides a weather widget that can be used in conjunction with this app so a macOS widget is NOT on the roadmap.**"
  },
  {
    "path": "DatWeatherDoe/API/NetworkClient.swift",
    "content": "//\n//  NetworkClient.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/14/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nprotocol NetworkClientType {\n    func performRequest(url: URL) async throws -> Data\n}\n\nfinal class NetworkClient: NetworkClientType {\n    func performRequest(url: URL) async throws -> Data {\n        do {\n            return try await URLSession.shared.data(from: url).0\n        } catch {\n            throw WeatherError.networkError\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/Response/AirQuality.swift",
    "content": "//\n//  AirQuality.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 7/1/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\n// US - EPA standard\nenum AirQualityIndex: Int, Decodable {\n    case good = 1\n    case moderate = 2\n    case unhealthyForSensitive = 3\n    case unhealthy = 4\n    case veryUnhealthy = 5\n    case hazardous = 6\n\n    var description: String {\n        switch self {\n        case .good:\n            String(localized: \"Good\")\n        case .moderate:\n            String(localized: \"Moderate\")\n        case .unhealthyForSensitive:\n            String(localized: \"Unhealthy for sensitive groups\")\n        case .unhealthy:\n            String(localized: \"Unhealthy\")\n        case .veryUnhealthy:\n            String(localized: \"Very unhealthy\")\n        case .hazardous:\n            String(localized: \"Hazardous\")\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/Response/ForecastData.swift",
    "content": "//\n//  ForecastData.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/23/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct Forecast: Decodable {\n    let dayDataArr: [ForecastDayData]\n\n    private enum CodingKeys: String, CodingKey {\n        case dayDataArr = \"forecastday\"\n    }\n}\n\nstruct ForecastDayData: Decodable {\n    let temperatureData: ForecastTemperatureData\n    let astro: SunriseSunsetData\n    let hour: [HourlyUVIndex]\n\n    private enum CodingKeys: String, CodingKey {\n        case temperatureData = \"day\"\n        case astro\n        case hour\n    }\n}\n\nstruct HourlyUVIndex: Decodable {\n    // swiftlint:disable:next identifier_name\n    let uv: Double\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/Response/ForecastTemperatureData.swift",
    "content": "//\n//  ForecastTemperatureData.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/23/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct ForecastTemperatureData: Decodable {\n    let maxTempC: Double\n    let maxTempF: Double\n    let minTempC: Double\n    let minTempF: Double\n\n    private enum CodingKeys: String, CodingKey {\n        case maxTempC = \"maxtemp_c\"\n        case maxTempF = \"maxtemp_f\"\n        case minTempC = \"mintemp_c\"\n        case minTempF = \"mintemp_f\"\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/Response/SunriseSunsetData.swift",
    "content": "//\n//  SunriseSunsetData.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/22/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct SunriseSunsetData: Decodable {\n    let sunrise: String\n    let sunset: String\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/Response/TemperatureData.swift",
    "content": "//\n//  TemperatureData.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/23/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct TemperatureData: Decodable {\n    let tempCelsius: Double\n    let feelsLikeTempCelsius: Double\n    let tempFahrenheit: Double\n    let feelsLikeTempFahrenheit: Double\n\n    private enum CodingKeys: String, CodingKey {\n        case tempCelsius = \"temp_c\"\n        case feelsLikeTempCelsius = \"feelslike_c\"\n        case tempFahrenheit = \"temp_f\"\n        case feelsLikeTempFahrenheit = \"feelslike_f\"\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/Response/WeatherAPIResponse.swift",
    "content": "//\n//  WeatherAPIResponse.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 2/3/18.\n//  Copyright © 2018 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct WeatherAPIResponse: Decodable {\n    let locationName: String\n    let temperatureData: TemperatureData\n    let isDay: Bool\n    let weatherConditionCode: Int\n    let humidity: Int\n    let windData: WindData\n    let uvIndex: Double\n    let forecastDayData: ForecastDayData\n    let airQualityIndex: AirQualityIndex\n\n    private enum RootKeys: String, CodingKey {\n        case location, current, forecast\n    }\n\n    private enum LocationKeys: String, CodingKey {\n        case name\n    }\n\n    private enum CurrentKeys: String, CodingKey {\n        case isDay = \"is_day\"\n        case condition, humidity\n        case airQuality = \"air_quality\"\n        case uvIndex = \"uv\"\n    }\n\n    private enum WeatherConditionKeys: String, CodingKey {\n        case code\n    }\n\n    private enum ForecastKeys: String, CodingKey {\n        case forecastDay = \"forecastday\"\n    }\n\n    private enum ForecastDayKeys: String, CodingKey {\n        case day, astro\n    }\n\n    private enum AirQualityKeys: String, CodingKey {\n        case usEpaIndex = \"us-epa-index\"\n    }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: RootKeys.self)\n\n        let locationContainer = try container.nestedContainer(keyedBy: LocationKeys.self, forKey: .location)\n        locationName = try locationContainer.decode(String.self, forKey: .name)\n        temperatureData = try container.decode(TemperatureData.self, forKey: .current)\n\n        let currentContainer = try container.nestedContainer(keyedBy: CurrentKeys.self, forKey: .current)\n        let isDayInt = try currentContainer.decode(Int.self, forKey: .isDay)\n        isDay = isDayInt > 0\n\n        let weatherConditionContainer = try currentContainer.nestedContainer(\n            keyedBy: WeatherConditionKeys.self,\n            forKey: .condition\n        )\n        weatherConditionCode = try weatherConditionContainer.decode(Int.self, forKey: .code)\n\n        humidity = try currentContainer.decode(Int.self, forKey: .humidity)\n\n        windData = try container.decode(WindData.self, forKey: .current)\n\n        uvIndex = try currentContainer.decode(Double.self, forKey: .uvIndex)\n\n        let forecast = try container.decode(Forecast.self, forKey: .forecast)\n        if let dayData = forecast.dayDataArr.first {\n            forecastDayData = dayData\n        } else {\n            throw DecodingError.dataCorruptedError(\n                forKey: .forecast,\n                in: container,\n                debugDescription: \"Missing forecast day data\"\n            )\n        }\n\n        let airQualityContainer =\n        try currentContainer.nestedContainer(keyedBy: AirQualityKeys.self, forKey: .airQuality)\n        airQualityIndex = try airQualityContainer.decode(AirQualityIndex.self, forKey: .usEpaIndex)\n    }\n\n    init(\n        locationName: String,\n        temperatureData: TemperatureData,\n        isDay: Bool,\n        weatherConditionCode: Int,\n        humidity: Int,\n        windData: WindData,\n        uvIndex: Double,\n        forecastDayData: ForecastDayData,\n        airQualityIndex: AirQualityIndex\n    ) {\n        self.locationName = locationName\n        self.temperatureData = temperatureData\n        self.isDay = isDay\n        self.weatherConditionCode = weatherConditionCode\n        self.humidity = humidity\n        self.windData = windData\n        self.uvIndex = uvIndex\n        self.forecastDayData = forecastDayData\n        self.airQualityIndex = airQualityIndex\n    }\n\n    // hour = [0-23]\n    func getHourlyUVIndex(hour: Int) -> Double {\n        forecastDayData.hour[safe: hour]?.uv ?? uvIndex\n    }\n}\n\nprivate extension Array {\n    subscript(safe index: Index) -> Element? { indices ~= index ? self[index] : nil }\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/Response/WeatherAPIResponseParser.swift",
    "content": "//\n//  WeatherAPIResponseParser.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/10/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nprotocol WeatherAPIResponseParserType {\n    func parse(_ data: Data) throws -> WeatherAPIResponse\n}\n\nfinal class WeatherAPIResponseParser: WeatherAPIResponseParserType {\n    func parse(_ data: Data) throws -> WeatherAPIResponse {\n        try JSONDecoder().decode(WeatherAPIResponse.self, from: data)\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/Response/WindData.swift",
    "content": "//\n//  WindData.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/23/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct WindData: Decodable {\n    let speedMph: Double\n    let degrees: Int\n    let direction: String\n\n    private enum CodingKeys: String, CodingKey {\n        case speedMph = \"wind_mph\"\n        case degrees = \"wind_degree\"\n        case direction = \"wind_dir\"\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/WeatherData.swift",
    "content": "//\n//  WeatherData.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 5/22/21.\n//  Copyright © 2021 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct WeatherData {\n    let showWeatherIcon: Bool\n    let textualRepresentation: String?\n    let weatherCondition: WeatherCondition\n    let response: WeatherAPIResponse\n}\n"
  },
  {
    "path": "DatWeatherDoe/API/WeatherError.swift",
    "content": "//\n//  WeatherError.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 5/22/21.\n//  Copyright © 2021 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nenum WeatherError: LocalizedError {\n    case unableToConstructUrl\n    case locationError\n    case latLongIncorrect\n    case networkError\n\n    var errorDescription: String? {\n        switch self {\n        case .unableToConstructUrl:\n            \"Unable to construct URL\"\n        case .locationError:\n            String(localized: \"❗️Location\")\n        case .latLongIncorrect:\n            String(localized: \"❗️Lat/Long\")\n        case .networkError:\n            \"🖧\"\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/Config/APIKeyParser.swift",
    "content": "//\n//  APIKeyParser.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/11/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nfinal class APIKeyParser {\n    func parse() -> String {\n        guard let apiKey = Bundle.main.infoDictionary?[\"WEATHER_API_KEY\"] as? String else {\n            fatalError(\"Unable to find OPENWEATHERMAP_APP_ID in `Config.xcconfig`\")\n        }\n        return apiKey\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/Config/ConfigManager.swift",
    "content": "//\n//  ConfigManager.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/29/16.\n//  Copyright © 2016 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\nimport SwiftUI\n\nprotocol ConfigManagerType: AnyObject {\n    var measurementUnit: String { get set }\n    var weatherSource: String { get set }\n    var weatherSourceText: String { get set }\n    var refreshInterval: TimeInterval { get set }\n    var isShowingWeatherIcon: Bool { get set }\n    var isShowingHumidity: Bool { get set }\n    var isShowingUVIndex: Bool { get set }\n    var isRoundingOffData: Bool { get set }\n    var isUnitLetterOff: Bool { get set }\n    var isUnitSymbolOff: Bool { get set }\n    var valueSeparator: String { get set }\n    var isWeatherConditionAsTextEnabled: Bool { get set }\n    var weatherConditionPosition: String { get set }\n\n    func updateWeatherSource(_ source: WeatherSource, sourceText: String)\n    func setConfigOptions(_ options: ConfigOptions)\n\n    var parsedMeasurementUnit: MeasurementUnit { get }\n}\n\nfinal class ConfigManager: ConfigManagerType {\n    @AppStorage(\"measurementUnit\")\n    public var measurementUnit = MeasurementUnit.imperial.rawValue\n\n    @AppStorage(\"weatherSource\")\n    public var weatherSource = WeatherSource.location.rawValue\n\n    @AppStorage(\"weatherSourceText\")\n    public var weatherSourceText = \"\"\n\n    @AppStorage(\"refreshInterval\")\n    public var refreshInterval = RefreshInterval.fifteenMinutes.rawValue\n\n    @AppStorage(\"isShowingWeatherIcon\")\n    public var isShowingWeatherIcon = true\n\n    @AppStorage(\"isShowingHumidity\")\n    public var isShowingHumidity = false\n\n    @AppStorage(\"isShowingUVIndex\")\n    public var isShowingUVIndex = false\n\n    @AppStorage(\"isRoundingOffData\")\n    public var isRoundingOffData = false\n\n    @AppStorage(\"isUnitLetterOff\")\n    public var isUnitLetterOff = false\n\n    @AppStorage(\"isUnitSymbolOff\")\n    public var isUnitSymbolOff = false\n\n    @AppStorage(\"valueSeparator\")\n    public var valueSeparator = \"\\u{007C}\"\n\n    @AppStorage(\"isWeatherConditionAsTextEnabled\")\n    public var isWeatherConditionAsTextEnabled = false\n\n    @AppStorage(\"weatherConditionPosition\")\n    public var weatherConditionPosition = WeatherConditionPosition.beforeTemperature.rawValue\n\n    func updateWeatherSource(_ source: WeatherSource, sourceText: String) {\n        weatherSource = source.rawValue\n        weatherSourceText = source == .location ? \"\" : sourceText\n    }\n\n    func setConfigOptions(_ options: ConfigOptions) {\n        refreshInterval = options.refreshInterval.rawValue\n        isShowingHumidity = options.isShowingHumidity\n        isShowingUVIndex = options.isShowingUVIndex\n        isRoundingOffData = options.isRoundingOffData\n        isUnitLetterOff = options.isUnitLetterOff\n        isUnitSymbolOff = options.isUnitSymbolOff\n        valueSeparator = options.valueSeparator\n        isWeatherConditionAsTextEnabled = options.isWeatherConditionAsTextEnabled\n    }\n\n    var parsedMeasurementUnit: MeasurementUnit {\n        MeasurementUnit(rawValue: measurementUnit) ?? .imperial\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/Config/ConfigOptions.swift",
    "content": "//\n//  ConfigOptions.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 8/3/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct ConfigOptions {\n    let refreshInterval: RefreshInterval\n    let isShowingHumidity: Bool\n    let isShowingUVIndex: Bool\n    let isRoundingOffData: Bool\n    let isUnitLetterOff: Bool\n    let isUnitSymbolOff: Bool\n    let valueSeparator: String\n    let isWeatherConditionAsTextEnabled: Bool\n}\n"
  },
  {
    "path": "DatWeatherDoe/DatWeatherDoeApp.swift",
    "content": "//\n//  DatWeatherDoeApp.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/17/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport MenuBarExtraAccess\nimport OSLog\nimport SwiftUI\n\n@main\nstruct DatWeatherDoeApp: App {\n    @State private var configManager: ConfigManager\n    @ObservedObject private var configureViewModel = ConfigureViewModel(configManager: ConfigManager())\n    @ObservedObject private var viewModel: WeatherViewModel\n    @State private var isMenuPresented = false\n    @State private var statusItem: NSStatusItem?\n\n    init() {\n        configManager = ConfigManager()\n\n        let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? \"bundleID\", category: \"main\")\n        viewModel = WeatherViewModel(\n            locationFetcher: SystemLocationFetcher(logger: logger),\n            weatherFactory: WeatherRepositoryFactory(\n                appId: APIKeyParser().parse(),\n                networkClient: NetworkClient(),\n                logger: logger\n            ),\n            configManager: ConfigManager(),\n            logger: logger\n        )\n        viewModel.setup(with: WeatherDataFormatter(configManager: configManager))\n    }\n\n    var body: some Scene {\n        MenuBarExtra(\n            content: {\n                MenuView(\n                    viewModel: viewModel,\n                    configureViewModel: configureViewModel,\n                    onSeeWeather: {\n                        viewModel.seeForecastForCurrentCity()\n                        closePopover()\n                    },\n                    onRefresh: {\n                        viewModel.getUpdatedWeatherAfterRefresh()\n                        closePopover()\n                    },\n                    onSave: {\n                        closePopover()\n                    }\n                )\n            },\n            label: {\n                StatusBarView(weatherResult: viewModel.weatherResult)\n                    .onAppear {\n                        viewModel.getUpdatedWeatherAfterRefresh()\n                    }\n            }\n        )\n        .menuBarExtraAccess(isPresented: $isMenuPresented) { statusItem in\n            self.statusItem = statusItem\n        }\n        .onChange(of: isMenuPresented) { newValue in\n            if !newValue {\n                configureViewModel.saveConfig()\n                viewModel.getUpdatedWeatherAfterRefresh()\n            }\n        }\n        .windowStyle(.hiddenTitleBar)\n        .menuBarExtraStyle(.window)\n    }\n\n    private func closePopover() {\n        statusItem?.togglePresented()\n    }\n}\n    \n"
  },
  {
    "path": "DatWeatherDoe/Localization/en.xcloc/Localized Contents/en.xliff",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" version=\"1.2\" xsi:schemaLocation=\"urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd\">\n  <file original=\"DatWeatherDoe/Resources/en.lproj/InfoPlist.strings\" source-language=\"en\" target-language=\"en\" datatype=\"plaintext\">\n    <header>\n      <tool tool-id=\"com.apple.dt.xcode\" tool-name=\"Xcode\" tool-version=\"12.5\" build-num=\"12E262\"/>\n    </header>\n    <body>\n      <trans-unit id=\"CFBundleName\" xml:space=\"preserve\">\n        <source>DatWeatherDoe</source>\n        <target>DatWeatherDoe</target>\n        <note>Bundle name</note>\n      </trans-unit>\n      <trans-unit id=\"NSHumanReadableCopyright\" xml:space=\"preserve\">\n        <source>Copyright © 2016 Inder Dhir. All rights reserved.</source>\n        <target>Copyright © 2016 Inder Dhir. All rights reserved.</target>\n        <note>Copyright (human-readable)</note>\n      </trans-unit>\n      <trans-unit id=\"NSLocationWhenInUseUsageDescription\" xml:space=\"preserve\">\n        <source>DatWeatherDoe optionally uses your current location to get the weather</source>\n        <target>DatWeatherDoe optionally uses your current location to get the weather</target>\n        <note>Privacy - Location When In Use Usage Description</note>\n      </trans-unit>\n    </body>\n  </file>\n  <file original=\"DatWeatherDoe/UI/Base.lproj/MainMenu.xib\" source-language=\"en\" target-language=\"en\" datatype=\"plaintext\">\n    <header>\n      <tool tool-id=\"com.apple.dt.xcode\" tool-name=\"Xcode\" tool-version=\"12.5\" build-num=\"12E262\"/>\n    </header>\n    <body>\n      <trans-unit id=\"1UK-8n-QPP.title\" xml:space=\"preserve\">\n        <source>Customize Toolbar…</source>\n        <target>Customize Toolbar…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Customize Toolbar…\"; ObjectID = \"1UK-8n-QPP\";</note>\n      </trans-unit>\n      <trans-unit id=\"1Xt-HY-uBw.title\" xml:space=\"preserve\">\n        <source>DatWeatherDoe</source>\n        <target>DatWeatherDoe</target>\n        <note>Class = \"NSMenuItem\"; title = \"DatWeatherDoe\"; ObjectID = \"1Xt-HY-uBw\";</note>\n      </trans-unit>\n      <trans-unit id=\"1b7-l0-nxx.title\" xml:space=\"preserve\">\n        <source>Find</source>\n        <target>Find</target>\n        <note>Class = \"NSMenu\"; title = \"Find\"; ObjectID = \"1b7-l0-nxx\";</note>\n      </trans-unit>\n      <trans-unit id=\"1tx-W0-xDw.title\" xml:space=\"preserve\">\n        <source>Lower</source>\n        <target>Lower</target>\n        <note>Class = \"NSMenuItem\"; title = \"Lower\"; ObjectID = \"1tx-W0-xDw\";</note>\n      </trans-unit>\n      <trans-unit id=\"2h7-ER-AoG.title\" xml:space=\"preserve\">\n        <source>Raise</source>\n        <target>Raise</target>\n        <note>Class = \"NSMenuItem\"; title = \"Raise\"; ObjectID = \"2h7-ER-AoG\";</note>\n      </trans-unit>\n      <trans-unit id=\"2oI-Rn-ZJC.title\" xml:space=\"preserve\">\n        <source>Transformations</source>\n        <target>Transformations</target>\n        <note>Class = \"NSMenuItem\"; title = \"Transformations\"; ObjectID = \"2oI-Rn-ZJC\";</note>\n      </trans-unit>\n      <trans-unit id=\"3IN-sU-3Bg.title\" xml:space=\"preserve\">\n        <source>Spelling</source>\n        <target>Spelling</target>\n        <note>Class = \"NSMenu\"; title = \"Spelling\"; ObjectID = \"3IN-sU-3Bg\";</note>\n      </trans-unit>\n      <trans-unit id=\"3Om-Ey-2VK.title\" xml:space=\"preserve\">\n        <source>Use Default</source>\n        <target>Use Default</target>\n        <note>Class = \"NSMenuItem\"; title = \"Use Default\"; ObjectID = \"3Om-Ey-2VK\";</note>\n      </trans-unit>\n      <trans-unit id=\"3rS-ZA-NoH.title\" xml:space=\"preserve\">\n        <source>Speech</source>\n        <target>Speech</target>\n        <note>Class = \"NSMenu\"; title = \"Speech\"; ObjectID = \"3rS-ZA-NoH\";</note>\n      </trans-unit>\n      <trans-unit id=\"4EN-yA-p0u.title\" xml:space=\"preserve\">\n        <source>Find</source>\n        <target>Find</target>\n        <note>Class = \"NSMenuItem\"; title = \"Find\"; ObjectID = \"4EN-yA-p0u\";</note>\n      </trans-unit>\n      <trans-unit id=\"4sb-4s-VLi.title\" xml:space=\"preserve\">\n        <source>Quit DatWeatherDoe</source>\n        <target>Quit DatWeatherDoe</target>\n        <note>Class = \"NSMenuItem\"; title = \"Quit DatWeatherDoe\"; ObjectID = \"4sb-4s-VLi\";</note>\n      </trans-unit>\n      <trans-unit id=\"5QF-Oa-p0T.title\" xml:space=\"preserve\">\n        <source>Edit</source>\n        <target>Edit</target>\n        <note>Class = \"NSMenuItem\"; title = \"Edit\"; ObjectID = \"5QF-Oa-p0T\";</note>\n      </trans-unit>\n      <trans-unit id=\"5Vv-lz-BsD.title\" xml:space=\"preserve\">\n        <source>Copy Style</source>\n        <target>Copy Style</target>\n        <note>Class = \"NSMenuItem\"; title = \"Copy Style\"; ObjectID = \"5Vv-lz-BsD\";</note>\n      </trans-unit>\n      <trans-unit id=\"5kV-Vb-QxS.title\" xml:space=\"preserve\">\n        <source>About DatWeatherDoe</source>\n        <target>About DatWeatherDoe</target>\n        <note>Class = \"NSMenuItem\"; title = \"About DatWeatherDoe\"; ObjectID = \"5kV-Vb-QxS\";</note>\n      </trans-unit>\n      <trans-unit id=\"6dh-zS-Vam.title\" xml:space=\"preserve\">\n        <source>Redo</source>\n        <target>Redo</target>\n        <note>Class = \"NSMenuItem\"; title = \"Redo\"; ObjectID = \"6dh-zS-Vam\";</note>\n      </trans-unit>\n      <trans-unit id=\"8mr-sm-Yjd.title\" xml:space=\"preserve\">\n        <source>Writing Direction</source>\n        <target>Writing Direction</target>\n        <note>Class = \"NSMenu\"; title = \"Writing Direction\"; ObjectID = \"8mr-sm-Yjd\";</note>\n      </trans-unit>\n      <trans-unit id=\"9ic-FL-obx.title\" xml:space=\"preserve\">\n        <source>Substitutions</source>\n        <target>Substitutions</target>\n        <note>Class = \"NSMenuItem\"; title = \"Substitutions\"; ObjectID = \"9ic-FL-obx\";</note>\n      </trans-unit>\n      <trans-unit id=\"9yt-4B-nSM.title\" xml:space=\"preserve\">\n        <source>Smart Copy/Paste</source>\n        <target>Smart Copy/Paste</target>\n        <note>Class = \"NSMenuItem\"; title = \"Smart Copy/Paste\"; ObjectID = \"9yt-4B-nSM\";</note>\n      </trans-unit>\n      <trans-unit id=\"46P-cB-AYj.title\" xml:space=\"preserve\">\n        <source>Tighten</source>\n        <target>Tighten</target>\n        <note>Class = \"NSMenuItem\"; title = \"Tighten\"; ObjectID = \"46P-cB-AYj\";</note>\n      </trans-unit>\n      <trans-unit id=\"78Y-hA-62v.title\" xml:space=\"preserve\">\n        <source>Correct Spelling Automatically</source>\n        <target>Correct Spelling Automatically</target>\n        <note>Class = \"NSMenuItem\"; title = \"Correct Spelling Automatically\"; ObjectID = \"78Y-hA-62v\";</note>\n      </trans-unit>\n      <trans-unit id=\"AYu-sK-qS6.title\" xml:space=\"preserve\">\n        <source>Main Menu</source>\n        <target>Main Menu</target>\n        <note>Class = \"NSMenu\"; title = \"Main Menu\"; ObjectID = \"AYu-sK-qS6\";</note>\n      </trans-unit>\n      <trans-unit id=\"BOF-NM-1cW.title\" xml:space=\"preserve\">\n        <source>Preferences…</source>\n        <target>Preferences…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Preferences…\"; ObjectID = \"BOF-NM-1cW\";</note>\n      </trans-unit>\n      <trans-unit id=\"BgM-ve-c93.title\" xml:space=\"preserve\">\n        <source>\tLeft to Right</source>\n        <target>\tLeft to Right</target>\n        <note>Class = \"NSMenuItem\"; title = \"\\tLeft to Right\"; ObjectID = \"BgM-ve-c93\";</note>\n      </trans-unit>\n      <trans-unit id=\"Bw7-FT-i3A.title\" xml:space=\"preserve\">\n        <source>Save As…</source>\n        <target>Save As…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Save As…\"; ObjectID = \"Bw7-FT-i3A\";</note>\n      </trans-unit>\n      <trans-unit id=\"DVo-aG-piG.title\" xml:space=\"preserve\">\n        <source>Close</source>\n        <target>Close</target>\n        <note>Class = \"NSMenuItem\"; title = \"Close\"; ObjectID = \"DVo-aG-piG\";</note>\n      </trans-unit>\n      <trans-unit id=\"Dv1-io-Yv7.title\" xml:space=\"preserve\">\n        <source>Spelling and Grammar</source>\n        <target>Spelling and Grammar</target>\n        <note>Class = \"NSMenuItem\"; title = \"Spelling and Grammar\"; ObjectID = \"Dv1-io-Yv7\";</note>\n      </trans-unit>\n      <trans-unit id=\"F2S-fz-NVQ.title\" xml:space=\"preserve\">\n        <source>Help</source>\n        <target>Help</target>\n        <note>Class = \"NSMenu\"; title = \"Help\"; ObjectID = \"F2S-fz-NVQ\";</note>\n      </trans-unit>\n      <trans-unit id=\"FKE-Sm-Kum.title\" xml:space=\"preserve\">\n        <source>DatWeatherDoe Help</source>\n        <target>DatWeatherDoe Help</target>\n        <note>Class = \"NSMenuItem\"; title = \"DatWeatherDoe Help\"; ObjectID = \"FKE-Sm-Kum\";</note>\n      </trans-unit>\n      <trans-unit id=\"Fal-I4-PZk.title\" xml:space=\"preserve\">\n        <source>Text</source>\n        <target>Text</target>\n        <note>Class = \"NSMenuItem\"; title = \"Text\"; ObjectID = \"Fal-I4-PZk\";</note>\n      </trans-unit>\n      <trans-unit id=\"FeM-D8-WVr.title\" xml:space=\"preserve\">\n        <source>Substitutions</source>\n        <target>Substitutions</target>\n        <note>Class = \"NSMenu\"; title = \"Substitutions\"; ObjectID = \"FeM-D8-WVr\";</note>\n      </trans-unit>\n      <trans-unit id=\"GB9-OM-e27.title\" xml:space=\"preserve\">\n        <source>Bold</source>\n        <target>Bold</target>\n        <note>Class = \"NSMenuItem\"; title = \"Bold\"; ObjectID = \"GB9-OM-e27\";</note>\n      </trans-unit>\n      <trans-unit id=\"GEO-Iw-cKr.title\" xml:space=\"preserve\">\n        <source>Format</source>\n        <target>Format</target>\n        <note>Class = \"NSMenu\"; title = \"Format\"; ObjectID = \"GEO-Iw-cKr\";</note>\n      </trans-unit>\n      <trans-unit id=\"GUa-eO-cwY.title\" xml:space=\"preserve\">\n        <source>Use Default</source>\n        <target>Use Default</target>\n        <note>Class = \"NSMenuItem\"; title = \"Use Default\"; ObjectID = \"GUa-eO-cwY\";</note>\n      </trans-unit>\n      <trans-unit id=\"Gi5-1S-RQB.title\" xml:space=\"preserve\">\n        <source>Font</source>\n        <target>Font</target>\n        <note>Class = \"NSMenuItem\"; title = \"Font\"; ObjectID = \"Gi5-1S-RQB\";</note>\n      </trans-unit>\n      <trans-unit id=\"H1b-Si-o9J.title\" xml:space=\"preserve\">\n        <source>Writing Direction</source>\n        <target>Writing Direction</target>\n        <note>Class = \"NSMenuItem\"; title = \"Writing Direction\"; ObjectID = \"H1b-Si-o9J\";</note>\n      </trans-unit>\n      <trans-unit id=\"H8h-7b-M4v.title\" xml:space=\"preserve\">\n        <source>View</source>\n        <target>View</target>\n        <note>Class = \"NSMenuItem\"; title = \"View\"; ObjectID = \"H8h-7b-M4v\";</note>\n      </trans-unit>\n      <trans-unit id=\"HFQ-gK-NFA.title\" xml:space=\"preserve\">\n        <source>Text Replacement</source>\n        <target>Text Replacement</target>\n        <note>Class = \"NSMenuItem\"; title = \"Text Replacement\"; ObjectID = \"HFQ-gK-NFA\";</note>\n      </trans-unit>\n      <trans-unit id=\"HFo-cy-zxI.title\" xml:space=\"preserve\">\n        <source>Show Spelling and Grammar</source>\n        <target>Show Spelling and Grammar</target>\n        <note>Class = \"NSMenuItem\"; title = \"Show Spelling and Grammar\"; ObjectID = \"HFo-cy-zxI\";</note>\n      </trans-unit>\n      <trans-unit id=\"HyV-fh-RgO.title\" xml:space=\"preserve\">\n        <source>View</source>\n        <target>View</target>\n        <note>Class = \"NSMenu\"; title = \"View\"; ObjectID = \"HyV-fh-RgO\";</note>\n      </trans-unit>\n      <trans-unit id=\"I0S-gh-46l.title\" xml:space=\"preserve\">\n        <source>Subscript</source>\n        <target>Subscript</target>\n        <note>Class = \"NSMenuItem\"; title = \"Subscript\"; ObjectID = \"I0S-gh-46l\";</note>\n      </trans-unit>\n      <trans-unit id=\"IAo-SY-fd9.title\" xml:space=\"preserve\">\n        <source>Open…</source>\n        <target>Open…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Open…\"; ObjectID = \"IAo-SY-fd9\";</note>\n      </trans-unit>\n      <trans-unit id=\"J5U-5w-g23.title\" xml:space=\"preserve\">\n        <source>Justify</source>\n        <target>Justify</target>\n        <note>Class = \"NSMenuItem\"; title = \"Justify\"; ObjectID = \"J5U-5w-g23\";</note>\n      </trans-unit>\n      <trans-unit id=\"J7y-lM-qPV.title\" xml:space=\"preserve\">\n        <source>Use None</source>\n        <target>Use None</target>\n        <note>Class = \"NSMenuItem\"; title = \"Use None\"; ObjectID = \"J7y-lM-qPV\";</note>\n      </trans-unit>\n      <trans-unit id=\"KaW-ft-85H.title\" xml:space=\"preserve\">\n        <source>Revert to Saved</source>\n        <target>Revert to Saved</target>\n        <note>Class = \"NSMenuItem\"; title = \"Revert to Saved\"; ObjectID = \"KaW-ft-85H\";</note>\n      </trans-unit>\n      <trans-unit id=\"Kd2-mp-pUS.title\" xml:space=\"preserve\">\n        <source>Show All</source>\n        <target>Show All</target>\n        <note>Class = \"NSMenuItem\"; title = \"Show All\"; ObjectID = \"Kd2-mp-pUS\";</note>\n      </trans-unit>\n      <trans-unit id=\"LE2-aR-0XJ.title\" xml:space=\"preserve\">\n        <source>Bring All to Front</source>\n        <target>Bring All to Front</target>\n        <note>Class = \"NSMenuItem\"; title = \"Bring All to Front\"; ObjectID = \"LE2-aR-0XJ\";</note>\n      </trans-unit>\n      <trans-unit id=\"LVM-kO-fVI.title\" xml:space=\"preserve\">\n        <source>Paste Ruler</source>\n        <target>Paste Ruler</target>\n        <note>Class = \"NSMenuItem\"; title = \"Paste Ruler\"; ObjectID = \"LVM-kO-fVI\";</note>\n      </trans-unit>\n      <trans-unit id=\"Lbh-J2-qVU.title\" xml:space=\"preserve\">\n        <source>\tLeft to Right</source>\n        <target>\tLeft to Right</target>\n        <note>Class = \"NSMenuItem\"; title = \"\\tLeft to Right\"; ObjectID = \"Lbh-J2-qVU\";</note>\n      </trans-unit>\n      <trans-unit id=\"MkV-Pr-PK5.title\" xml:space=\"preserve\">\n        <source>Copy Ruler</source>\n        <target>Copy Ruler</target>\n        <note>Class = \"NSMenuItem\"; title = \"Copy Ruler\"; ObjectID = \"MkV-Pr-PK5\";</note>\n      </trans-unit>\n      <trans-unit id=\"NMo-om-nkz.title\" xml:space=\"preserve\">\n        <source>Services</source>\n        <target>Services</target>\n        <note>Class = \"NSMenuItem\"; title = \"Services\"; ObjectID = \"NMo-om-nkz\";</note>\n      </trans-unit>\n      <trans-unit id=\"Nop-cj-93Q.title\" xml:space=\"preserve\">\n        <source>\tDefault</source>\n        <target>\tDefault</target>\n        <note>Class = \"NSMenuItem\"; title = \"\\tDefault\"; ObjectID = \"Nop-cj-93Q\";</note>\n      </trans-unit>\n      <trans-unit id=\"OY7-WF-poV.title\" xml:space=\"preserve\">\n        <source>Minimize</source>\n        <target>Minimize</target>\n        <note>Class = \"NSMenuItem\"; title = \"Minimize\"; ObjectID = \"OY7-WF-poV\";</note>\n      </trans-unit>\n      <trans-unit id=\"OaQ-X3-Vso.title\" xml:space=\"preserve\">\n        <source>Baseline</source>\n        <target>Baseline</target>\n        <note>Class = \"NSMenuItem\"; title = \"Baseline\"; ObjectID = \"OaQ-X3-Vso\";</note>\n      </trans-unit>\n      <trans-unit id=\"Olw-nP-bQN.title\" xml:space=\"preserve\">\n        <source>Hide DatWeatherDoe</source>\n        <target>Hide DatWeatherDoe</target>\n        <note>Class = \"NSMenuItem\"; title = \"Hide DatWeatherDoe\"; ObjectID = \"Olw-nP-bQN\";</note>\n      </trans-unit>\n      <trans-unit id=\"OwM-mh-QMV.title\" xml:space=\"preserve\">\n        <source>Find Previous</source>\n        <target>Find Previous</target>\n        <note>Class = \"NSMenuItem\"; title = \"Find Previous\"; ObjectID = \"OwM-mh-QMV\";</note>\n      </trans-unit>\n      <trans-unit id=\"Oyz-dy-DGm.title\" xml:space=\"preserve\">\n        <source>Stop Speaking</source>\n        <target>Stop Speaking</target>\n        <note>Class = \"NSMenuItem\"; title = \"Stop Speaking\"; ObjectID = \"Oyz-dy-DGm\";</note>\n      </trans-unit>\n      <trans-unit id=\"Ptp-SP-VEL.title\" xml:space=\"preserve\">\n        <source>Bigger</source>\n        <target>Bigger</target>\n        <note>Class = \"NSMenuItem\"; title = \"Bigger\"; ObjectID = \"Ptp-SP-VEL\";</note>\n      </trans-unit>\n      <trans-unit id=\"Q5e-8K-NDq.title\" xml:space=\"preserve\">\n        <source>Show Fonts</source>\n        <target>Show Fonts</target>\n        <note>Class = \"NSMenuItem\"; title = \"Show Fonts\"; ObjectID = \"Q5e-8K-NDq\";</note>\n      </trans-unit>\n      <trans-unit id=\"QvC-M9-y7g.title\" xml:space=\"preserve\">\n        <source>DatWeatherDoe</source>\n        <target>DatWeatherDoe</target>\n        <note>Class = \"NSWindow\"; title = \"DatWeatherDoe\"; ObjectID = \"QvC-M9-y7g\";</note>\n      </trans-unit>\n      <trans-unit id=\"R4o-n2-Eq4.title\" xml:space=\"preserve\">\n        <source>Zoom</source>\n        <target>Zoom</target>\n        <note>Class = \"NSMenuItem\"; title = \"Zoom\"; ObjectID = \"R4o-n2-Eq4\";</note>\n      </trans-unit>\n      <trans-unit id=\"RB4-Sm-HuC.title\" xml:space=\"preserve\">\n        <source>\tRight to Left</source>\n        <target>\tRight to Left</target>\n        <note>Class = \"NSMenuItem\"; title = \"\\tRight to Left\"; ObjectID = \"RB4-Sm-HuC\";</note>\n      </trans-unit>\n      <trans-unit id=\"Rqc-34-cIF.title\" xml:space=\"preserve\">\n        <source>Superscript</source>\n        <target>Superscript</target>\n        <note>Class = \"NSMenuItem\"; title = \"Superscript\"; ObjectID = \"Rqc-34-cIF\";</note>\n      </trans-unit>\n      <trans-unit id=\"Ruw-6m-B2m.title\" xml:space=\"preserve\">\n        <source>Select All</source>\n        <target>Select All</target>\n        <note>Class = \"NSMenuItem\"; title = \"Select All\"; ObjectID = \"Ruw-6m-B2m\";</note>\n      </trans-unit>\n      <trans-unit id=\"S0p-oC-mLd.title\" xml:space=\"preserve\">\n        <source>Jump to Selection</source>\n        <target>Jump to Selection</target>\n        <note>Class = \"NSMenuItem\"; title = \"Jump to Selection\"; ObjectID = \"S0p-oC-mLd\";</note>\n      </trans-unit>\n      <trans-unit id=\"Td7-aD-5lo.title\" xml:space=\"preserve\">\n        <source>Window</source>\n        <target>Window</target>\n        <note>Class = \"NSMenu\"; title = \"Window\"; ObjectID = \"Td7-aD-5lo\";</note>\n      </trans-unit>\n      <trans-unit id=\"UEZ-Bs-lqG.title\" xml:space=\"preserve\">\n        <source>Capitalize</source>\n        <target>Capitalize</target>\n        <note>Class = \"NSMenuItem\"; title = \"Capitalize\"; ObjectID = \"UEZ-Bs-lqG\";</note>\n      </trans-unit>\n      <trans-unit id=\"VIY-Ag-zcb.title\" xml:space=\"preserve\">\n        <source>Center</source>\n        <target>Center</target>\n        <note>Class = \"NSMenuItem\"; title = \"Center\"; ObjectID = \"VIY-Ag-zcb\";</note>\n      </trans-unit>\n      <trans-unit id=\"Vdr-fp-XzO.title\" xml:space=\"preserve\">\n        <source>Hide Others</source>\n        <target>Hide Others</target>\n        <note>Class = \"NSMenuItem\"; title = \"Hide Others\"; ObjectID = \"Vdr-fp-XzO\";</note>\n      </trans-unit>\n      <trans-unit id=\"Vjx-xi-njq.title\" xml:space=\"preserve\">\n        <source>Italic</source>\n        <target>Italic</target>\n        <note>Class = \"NSMenuItem\"; title = \"Italic\"; ObjectID = \"Vjx-xi-njq\";</note>\n      </trans-unit>\n      <trans-unit id=\"W48-6f-4Dl.title\" xml:space=\"preserve\">\n        <source>Edit</source>\n        <target>Edit</target>\n        <note>Class = \"NSMenu\"; title = \"Edit\"; ObjectID = \"W48-6f-4Dl\";</note>\n      </trans-unit>\n      <trans-unit id=\"WRG-CD-K1S.title\" xml:space=\"preserve\">\n        <source>Underline</source>\n        <target>Underline</target>\n        <note>Class = \"NSMenuItem\"; title = \"Underline\"; ObjectID = \"WRG-CD-K1S\";</note>\n      </trans-unit>\n      <trans-unit id=\"Was-JA-tGl.title\" xml:space=\"preserve\">\n        <source>New</source>\n        <target>New</target>\n        <note>Class = \"NSMenuItem\"; title = \"New\"; ObjectID = \"Was-JA-tGl\";</note>\n      </trans-unit>\n      <trans-unit id=\"WeT-3V-zwk.title\" xml:space=\"preserve\">\n        <source>Paste and Match Style</source>\n        <target>Paste and Match Style</target>\n        <note>Class = \"NSMenuItem\"; title = \"Paste and Match Style\"; ObjectID = \"WeT-3V-zwk\";</note>\n      </trans-unit>\n      <trans-unit id=\"Xz5-n4-O0W.title\" xml:space=\"preserve\">\n        <source>Find…</source>\n        <target>Find…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Find…\"; ObjectID = \"Xz5-n4-O0W\";</note>\n      </trans-unit>\n      <trans-unit id=\"YEy-JH-Tfz.title\" xml:space=\"preserve\">\n        <source>Find and Replace…</source>\n        <target>Find and Replace…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Find and Replace…\"; ObjectID = \"YEy-JH-Tfz\";</note>\n      </trans-unit>\n      <trans-unit id=\"YGs-j5-SAR.title\" xml:space=\"preserve\">\n        <source>\tDefault</source>\n        <target>\tDefault</target>\n        <note>Class = \"NSMenuItem\"; title = \"\\tDefault\"; ObjectID = \"YGs-j5-SAR\";</note>\n      </trans-unit>\n      <trans-unit id=\"Ynk-f8-cLZ.title\" xml:space=\"preserve\">\n        <source>Start Speaking</source>\n        <target>Start Speaking</target>\n        <note>Class = \"NSMenuItem\"; title = \"Start Speaking\"; ObjectID = \"Ynk-f8-cLZ\";</note>\n      </trans-unit>\n      <trans-unit id=\"ZM1-6Q-yy1.title\" xml:space=\"preserve\">\n        <source>Align Left</source>\n        <target>Align Left</target>\n        <note>Class = \"NSMenuItem\"; title = \"Align Left\"; ObjectID = \"ZM1-6Q-yy1\";</note>\n      </trans-unit>\n      <trans-unit id=\"ZvO-Gk-QUH.title\" xml:space=\"preserve\">\n        <source>Paragraph</source>\n        <target>Paragraph</target>\n        <note>Class = \"NSMenuItem\"; title = \"Paragraph\"; ObjectID = \"ZvO-Gk-QUH\";</note>\n      </trans-unit>\n      <trans-unit id=\"aTl-1u-JFS.title\" xml:space=\"preserve\">\n        <source>Print…</source>\n        <target>Print…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Print…\"; ObjectID = \"aTl-1u-JFS\";</note>\n      </trans-unit>\n      <trans-unit id=\"aUF-d1-5bR.title\" xml:space=\"preserve\">\n        <source>Window</source>\n        <target>Window</target>\n        <note>Class = \"NSMenuItem\"; title = \"Window\"; ObjectID = \"aUF-d1-5bR\";</note>\n      </trans-unit>\n      <trans-unit id=\"aXa-aM-Jaq.title\" xml:space=\"preserve\">\n        <source>Font</source>\n        <target>Font</target>\n        <note>Class = \"NSMenu\"; title = \"Font\"; ObjectID = \"aXa-aM-Jaq\";</note>\n      </trans-unit>\n      <trans-unit id=\"agt-UL-0e3.title\" xml:space=\"preserve\">\n        <source>Use Default</source>\n        <target>Use Default</target>\n        <note>Class = \"NSMenuItem\"; title = \"Use Default\"; ObjectID = \"agt-UL-0e3\";</note>\n      </trans-unit>\n      <trans-unit id=\"bgn-CT-cEk.title\" xml:space=\"preserve\">\n        <source>Show Colors</source>\n        <target>Show Colors</target>\n        <note>Class = \"NSMenuItem\"; title = \"Show Colors\"; ObjectID = \"bgn-CT-cEk\";</note>\n      </trans-unit>\n      <trans-unit id=\"bib-Uj-vzu.title\" xml:space=\"preserve\">\n        <source>File</source>\n        <target>File</target>\n        <note>Class = \"NSMenu\"; title = \"File\"; ObjectID = \"bib-Uj-vzu\";</note>\n      </trans-unit>\n      <trans-unit id=\"buJ-ug-pKt.title\" xml:space=\"preserve\">\n        <source>Use Selection for Find</source>\n        <target>Use Selection for Find</target>\n        <note>Class = \"NSMenuItem\"; title = \"Use Selection for Find\"; ObjectID = \"buJ-ug-pKt\";</note>\n      </trans-unit>\n      <trans-unit id=\"c8a-y6-VQd.title\" xml:space=\"preserve\">\n        <source>Transformations</source>\n        <target>Transformations</target>\n        <note>Class = \"NSMenu\"; title = \"Transformations\"; ObjectID = \"c8a-y6-VQd\";</note>\n      </trans-unit>\n      <trans-unit id=\"cDB-IK-hbR.title\" xml:space=\"preserve\">\n        <source>Use None</source>\n        <target>Use None</target>\n        <note>Class = \"NSMenuItem\"; title = \"Use None\"; ObjectID = \"cDB-IK-hbR\";</note>\n      </trans-unit>\n      <trans-unit id=\"cqv-fj-IhA.title\" xml:space=\"preserve\">\n        <source>Selection</source>\n        <target>Selection</target>\n        <note>Class = \"NSMenuItem\"; title = \"Selection\"; ObjectID = \"cqv-fj-IhA\";</note>\n      </trans-unit>\n      <trans-unit id=\"cwL-P1-jid.title\" xml:space=\"preserve\">\n        <source>Smart Links</source>\n        <target>Smart Links</target>\n        <note>Class = \"NSMenuItem\"; title = \"Smart Links\"; ObjectID = \"cwL-P1-jid\";</note>\n      </trans-unit>\n      <trans-unit id=\"d9M-CD-aMd.title\" xml:space=\"preserve\">\n        <source>Make Lower Case</source>\n        <target>Make Lower Case</target>\n        <note>Class = \"NSMenuItem\"; title = \"Make Lower Case\"; ObjectID = \"d9M-CD-aMd\";</note>\n      </trans-unit>\n      <trans-unit id=\"d9c-me-L2H.title\" xml:space=\"preserve\">\n        <source>Text</source>\n        <target>Text</target>\n        <note>Class = \"NSMenu\"; title = \"Text\"; ObjectID = \"d9c-me-L2H\";</note>\n      </trans-unit>\n      <trans-unit id=\"dMs-cI-mzQ.title\" xml:space=\"preserve\">\n        <source>File</source>\n        <target>File</target>\n        <note>Class = \"NSMenuItem\"; title = \"File\"; ObjectID = \"dMs-cI-mzQ\";</note>\n      </trans-unit>\n      <trans-unit id=\"dRJ-4n-Yzg.title\" xml:space=\"preserve\">\n        <source>Undo</source>\n        <target>Undo</target>\n        <note>Class = \"NSMenuItem\"; title = \"Undo\"; ObjectID = \"dRJ-4n-Yzg\";</note>\n      </trans-unit>\n      <trans-unit id=\"gVA-U4-sdL.title\" xml:space=\"preserve\">\n        <source>Paste</source>\n        <target>Paste</target>\n        <note>Class = \"NSMenuItem\"; title = \"Paste\"; ObjectID = \"gVA-U4-sdL\";</note>\n      </trans-unit>\n      <trans-unit id=\"hQb-2v-fYv.title\" xml:space=\"preserve\">\n        <source>Smart Quotes</source>\n        <target>Smart Quotes</target>\n        <note>Class = \"NSMenuItem\"; title = \"Smart Quotes\"; ObjectID = \"hQb-2v-fYv\";</note>\n      </trans-unit>\n      <trans-unit id=\"hz2-CU-CR7.title\" xml:space=\"preserve\">\n        <source>Check Document Now</source>\n        <target>Check Document Now</target>\n        <note>Class = \"NSMenuItem\"; title = \"Check Document Now\"; ObjectID = \"hz2-CU-CR7\";</note>\n      </trans-unit>\n      <trans-unit id=\"hz9-B4-Xy5.title\" xml:space=\"preserve\">\n        <source>Services</source>\n        <target>Services</target>\n        <note>Class = \"NSMenu\"; title = \"Services\"; ObjectID = \"hz9-B4-Xy5\";</note>\n      </trans-unit>\n      <trans-unit id=\"i1d-Er-qST.title\" xml:space=\"preserve\">\n        <source>Smaller</source>\n        <target>Smaller</target>\n        <note>Class = \"NSMenuItem\"; title = \"Smaller\"; ObjectID = \"i1d-Er-qST\";</note>\n      </trans-unit>\n      <trans-unit id=\"ijk-EB-dga.title\" xml:space=\"preserve\">\n        <source>Baseline</source>\n        <target>Baseline</target>\n        <note>Class = \"NSMenu\"; title = \"Baseline\"; ObjectID = \"ijk-EB-dga\";</note>\n      </trans-unit>\n      <trans-unit id=\"jBQ-r6-VK2.title\" xml:space=\"preserve\">\n        <source>Kern</source>\n        <target>Kern</target>\n        <note>Class = \"NSMenuItem\"; title = \"Kern\"; ObjectID = \"jBQ-r6-VK2\";</note>\n      </trans-unit>\n      <trans-unit id=\"jFq-tB-4Kx.title\" xml:space=\"preserve\">\n        <source>\tRight to Left</source>\n        <target>\tRight to Left</target>\n        <note>Class = \"NSMenuItem\"; title = \"\\tRight to Left\"; ObjectID = \"jFq-tB-4Kx\";</note>\n      </trans-unit>\n      <trans-unit id=\"jxT-CU-nIS.title\" xml:space=\"preserve\">\n        <source>Format</source>\n        <target>Format</target>\n        <note>Class = \"NSMenuItem\"; title = \"Format\"; ObjectID = \"jxT-CU-nIS\";</note>\n      </trans-unit>\n      <trans-unit id=\"mK6-2p-4JG.title\" xml:space=\"preserve\">\n        <source>Check Grammar With Spelling</source>\n        <target>Check Grammar With Spelling</target>\n        <note>Class = \"NSMenuItem\"; title = \"Check Grammar With Spelling\"; ObjectID = \"mK6-2p-4JG\";</note>\n      </trans-unit>\n      <trans-unit id=\"o6e-r0-MWq.title\" xml:space=\"preserve\">\n        <source>Ligatures</source>\n        <target>Ligatures</target>\n        <note>Class = \"NSMenuItem\"; title = \"Ligatures\"; ObjectID = \"o6e-r0-MWq\";</note>\n      </trans-unit>\n      <trans-unit id=\"oas-Oc-fiZ.title\" xml:space=\"preserve\">\n        <source>Open Recent</source>\n        <target>Open Recent</target>\n        <note>Class = \"NSMenu\"; title = \"Open Recent\"; ObjectID = \"oas-Oc-fiZ\";</note>\n      </trans-unit>\n      <trans-unit id=\"ogc-rX-tC1.title\" xml:space=\"preserve\">\n        <source>Loosen</source>\n        <target>Loosen</target>\n        <note>Class = \"NSMenuItem\"; title = \"Loosen\"; ObjectID = \"ogc-rX-tC1\";</note>\n      </trans-unit>\n      <trans-unit id=\"pa3-QI-u2k.title\" xml:space=\"preserve\">\n        <source>Delete</source>\n        <target>Delete</target>\n        <note>Class = \"NSMenuItem\"; title = \"Delete\"; ObjectID = \"pa3-QI-u2k\";</note>\n      </trans-unit>\n      <trans-unit id=\"pxx-59-PXV.title\" xml:space=\"preserve\">\n        <source>Save…</source>\n        <target>Save…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Save…\"; ObjectID = \"pxx-59-PXV\";</note>\n      </trans-unit>\n      <trans-unit id=\"q09-fT-Sye.title\" xml:space=\"preserve\">\n        <source>Find Next</source>\n        <target>Find Next</target>\n        <note>Class = \"NSMenuItem\"; title = \"Find Next\"; ObjectID = \"q09-fT-Sye\";</note>\n      </trans-unit>\n      <trans-unit id=\"qIS-W8-SiK.title\" xml:space=\"preserve\">\n        <source>Page Setup…</source>\n        <target>Page Setup…</target>\n        <note>Class = \"NSMenuItem\"; title = \"Page Setup…\"; ObjectID = \"qIS-W8-SiK\";</note>\n      </trans-unit>\n      <trans-unit id=\"rbD-Rh-wIN.title\" xml:space=\"preserve\">\n        <source>Check Spelling While Typing</source>\n        <target>Check Spelling While Typing</target>\n        <note>Class = \"NSMenuItem\"; title = \"Check Spelling While Typing\"; ObjectID = \"rbD-Rh-wIN\";</note>\n      </trans-unit>\n      <trans-unit id=\"rgM-f4-ycn.title\" xml:space=\"preserve\">\n        <source>Smart Dashes</source>\n        <target>Smart Dashes</target>\n        <note>Class = \"NSMenuItem\"; title = \"Smart Dashes\"; ObjectID = \"rgM-f4-ycn\";</note>\n      </trans-unit>\n      <trans-unit id=\"snW-S8-Cw5.title\" xml:space=\"preserve\">\n        <source>Show Toolbar</source>\n        <target>Show Toolbar</target>\n        <note>Class = \"NSMenuItem\"; title = \"Show Toolbar\"; ObjectID = \"snW-S8-Cw5\";</note>\n      </trans-unit>\n      <trans-unit id=\"tRr-pd-1PS.title\" xml:space=\"preserve\">\n        <source>Data Detectors</source>\n        <target>Data Detectors</target>\n        <note>Class = \"NSMenuItem\"; title = \"Data Detectors\"; ObjectID = \"tRr-pd-1PS\";</note>\n      </trans-unit>\n      <trans-unit id=\"tXI-mr-wws.title\" xml:space=\"preserve\">\n        <source>Open Recent</source>\n        <target>Open Recent</target>\n        <note>Class = \"NSMenuItem\"; title = \"Open Recent\"; ObjectID = \"tXI-mr-wws\";</note>\n      </trans-unit>\n      <trans-unit id=\"tlD-Oa-oAM.title\" xml:space=\"preserve\">\n        <source>Kern</source>\n        <target>Kern</target>\n        <note>Class = \"NSMenu\"; title = \"Kern\"; ObjectID = \"tlD-Oa-oAM\";</note>\n      </trans-unit>\n      <trans-unit id=\"uQy-DD-JDr.title\" xml:space=\"preserve\">\n        <source>DatWeatherDoe</source>\n        <target>DatWeatherDoe</target>\n        <note>Class = \"NSMenu\"; title = \"DatWeatherDoe\"; ObjectID = \"uQy-DD-JDr\";</note>\n      </trans-unit>\n      <trans-unit id=\"uRl-iY-unG.title\" xml:space=\"preserve\">\n        <source>Cut</source>\n        <target>Cut</target>\n        <note>Class = \"NSMenuItem\"; title = \"Cut\"; ObjectID = \"uRl-iY-unG\";</note>\n      </trans-unit>\n      <trans-unit id=\"vKC-jM-MkH.title\" xml:space=\"preserve\">\n        <source>Paste Style</source>\n        <target>Paste Style</target>\n        <note>Class = \"NSMenuItem\"; title = \"Paste Style\"; ObjectID = \"vKC-jM-MkH\";</note>\n      </trans-unit>\n      <trans-unit id=\"vLm-3I-IUL.title\" xml:space=\"preserve\">\n        <source>Show Ruler</source>\n        <target>Show Ruler</target>\n        <note>Class = \"NSMenuItem\"; title = \"Show Ruler\"; ObjectID = \"vLm-3I-IUL\";</note>\n      </trans-unit>\n      <trans-unit id=\"vNY-rz-j42.title\" xml:space=\"preserve\">\n        <source>Clear Menu</source>\n        <target>Clear Menu</target>\n        <note>Class = \"NSMenuItem\"; title = \"Clear Menu\"; ObjectID = \"vNY-rz-j42\";</note>\n      </trans-unit>\n      <trans-unit id=\"vmV-6d-7jI.title\" xml:space=\"preserve\">\n        <source>Make Upper Case</source>\n        <target>Make Upper Case</target>\n        <note>Class = \"NSMenuItem\"; title = \"Make Upper Case\"; ObjectID = \"vmV-6d-7jI\";</note>\n      </trans-unit>\n      <trans-unit id=\"w0m-vy-SC9.title\" xml:space=\"preserve\">\n        <source>Ligatures</source>\n        <target>Ligatures</target>\n        <note>Class = \"NSMenu\"; title = \"Ligatures\"; ObjectID = \"w0m-vy-SC9\";</note>\n      </trans-unit>\n      <trans-unit id=\"wb2-vD-lq4.title\" xml:space=\"preserve\">\n        <source>Align Right</source>\n        <target>Align Right</target>\n        <note>Class = \"NSMenuItem\"; title = \"Align Right\"; ObjectID = \"wb2-vD-lq4\";</note>\n      </trans-unit>\n      <trans-unit id=\"wpr-3q-Mcd.title\" xml:space=\"preserve\">\n        <source>Help</source>\n        <target>Help</target>\n        <note>Class = \"NSMenuItem\"; title = \"Help\"; ObjectID = \"wpr-3q-Mcd\";</note>\n      </trans-unit>\n      <trans-unit id=\"x3v-GG-iWU.title\" xml:space=\"preserve\">\n        <source>Copy</source>\n        <target>Copy</target>\n        <note>Class = \"NSMenuItem\"; title = \"Copy\"; ObjectID = \"x3v-GG-iWU\";</note>\n      </trans-unit>\n      <trans-unit id=\"xQD-1f-W4t.title\" xml:space=\"preserve\">\n        <source>Use All</source>\n        <target>Use All</target>\n        <note>Class = \"NSMenuItem\"; title = \"Use All\"; ObjectID = \"xQD-1f-W4t\";</note>\n      </trans-unit>\n      <trans-unit id=\"xrE-MZ-jX0.title\" xml:space=\"preserve\">\n        <source>Speech</source>\n        <target>Speech</target>\n        <note>Class = \"NSMenuItem\"; title = \"Speech\"; ObjectID = \"xrE-MZ-jX0\";</note>\n      </trans-unit>\n      <trans-unit id=\"z6F-FW-3nz.title\" xml:space=\"preserve\">\n        <source>Show Substitutions</source>\n        <target>Show Substitutions</target>\n        <note>Class = \"NSMenuItem\"; title = \"Show Substitutions\"; ObjectID = \"z6F-FW-3nz\";</note>\n      </trans-unit>\n    </body>\n  </file>\n</xliff>\n"
  },
  {
    "path": "DatWeatherDoe/Localization/en.xcloc/Source Contents/DatWeatherDoe/Resources/en.lproj/InfoPlist.strings",
    "content": "/* Bundle name */\n\"CFBundleName\" = \"DatWeatherDoe\";\n/* Copyright (human-readable) */\n\"NSHumanReadableCopyright\" = \"Copyright © 2016 Inder Dhir. All rights reserved.\";\n/* Privacy - Location When In Use Usage Description */\n\"NSLocationWhenInUseUsageDescription\" = \"DatWeatherDoe optionally uses your current location to get the weather\";\n"
  },
  {
    "path": "DatWeatherDoe/Localization/en.xcloc/Source Contents/DatWeatherDoe/UI/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"10116\" systemVersion=\"15E65\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"10116\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"DatWeatherDoe\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"window\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"DatWeatherDoe\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"DatWeatherDoe\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About DatWeatherDoe\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide DatWeatherDoe\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit DatWeatherDoe\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"File\" id=\"dMs-cI-mzQ\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"File\" id=\"bib-Uj-vzu\">\n                        <items>\n                            <menuItem title=\"New\" keyEquivalent=\"n\" id=\"Was-JA-tGl\">\n                                <connections>\n                                    <action selector=\"newDocument:\" target=\"-1\" id=\"4Si-XN-c54\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Open…\" keyEquivalent=\"o\" id=\"IAo-SY-fd9\">\n                                <connections>\n                                    <action selector=\"openDocument:\" target=\"-1\" id=\"bVn-NM-KNZ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Open Recent\" id=\"tXI-mr-wws\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Open Recent\" systemMenu=\"recentDocuments\" id=\"oas-Oc-fiZ\">\n                                    <items>\n                                        <menuItem title=\"Clear Menu\" id=\"vNY-rz-j42\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"clearRecentDocuments:\" target=\"-1\" id=\"Daa-9d-B3U\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"m54-Is-iLE\"/>\n                            <menuItem title=\"Close\" keyEquivalent=\"w\" id=\"DVo-aG-piG\">\n                                <connections>\n                                    <action selector=\"performClose:\" target=\"-1\" id=\"HmO-Ls-i7Q\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Save…\" keyEquivalent=\"s\" id=\"pxx-59-PXV\">\n                                <connections>\n                                    <action selector=\"saveDocument:\" target=\"-1\" id=\"teZ-XB-qJY\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Save As…\" keyEquivalent=\"S\" id=\"Bw7-FT-i3A\">\n                                <connections>\n                                    <action selector=\"saveDocumentAs:\" target=\"-1\" id=\"mDf-zr-I0C\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Revert to Saved\" id=\"KaW-ft-85H\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"revertDocumentToSaved:\" target=\"-1\" id=\"iJ3-Pv-kwq\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"aJh-i4-bef\"/>\n                            <menuItem title=\"Page Setup…\" keyEquivalent=\"P\" id=\"qIS-W8-SiK\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" shift=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"runPageLayout:\" target=\"-1\" id=\"Din-rz-gC5\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Print…\" keyEquivalent=\"p\" id=\"aTl-1u-JFS\">\n                                <connections>\n                                    <action selector=\"print:\" target=\"-1\" id=\"qaZ-4w-aoO\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Format\" id=\"jxT-CU-nIS\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Format\" id=\"GEO-Iw-cKr\">\n                        <items>\n                            <menuItem title=\"Font\" id=\"Gi5-1S-RQB\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Font\" systemMenu=\"font\" id=\"aXa-aM-Jaq\">\n                                    <items>\n                                        <menuItem title=\"Show Fonts\" keyEquivalent=\"t\" id=\"Q5e-8K-NDq\">\n                                            <connections>\n                                                <action selector=\"orderFrontFontPanel:\" target=\"YLy-65-1bz\" id=\"WHr-nq-2xA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Bold\" tag=\"2\" keyEquivalent=\"b\" id=\"GB9-OM-e27\">\n                                            <connections>\n                                                <action selector=\"addFontTrait:\" target=\"YLy-65-1bz\" id=\"hqk-hr-sYV\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Italic\" tag=\"1\" keyEquivalent=\"i\" id=\"Vjx-xi-njq\">\n                                            <connections>\n                                                <action selector=\"addFontTrait:\" target=\"YLy-65-1bz\" id=\"IHV-OB-c03\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Underline\" keyEquivalent=\"u\" id=\"WRG-CD-K1S\">\n                                            <connections>\n                                                <action selector=\"underline:\" target=\"-1\" id=\"FYS-2b-JAY\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"5gT-KC-WSO\"/>\n                                        <menuItem title=\"Bigger\" tag=\"3\" keyEquivalent=\"+\" id=\"Ptp-SP-VEL\">\n                                            <connections>\n                                                <action selector=\"modifyFont:\" target=\"YLy-65-1bz\" id=\"Uc7-di-UnL\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smaller\" tag=\"4\" keyEquivalent=\"-\" id=\"i1d-Er-qST\">\n                                            <connections>\n                                                <action selector=\"modifyFont:\" target=\"YLy-65-1bz\" id=\"HcX-Lf-eNd\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"kx3-Dk-x3B\"/>\n                                        <menuItem title=\"Kern\" id=\"jBQ-r6-VK2\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Kern\" id=\"tlD-Oa-oAM\">\n                                                <items>\n                                                    <menuItem title=\"Use Default\" id=\"GUa-eO-cwY\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"useStandardKerning:\" target=\"-1\" id=\"6dk-9l-Ckg\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Use None\" id=\"cDB-IK-hbR\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"turnOffKerning:\" target=\"-1\" id=\"U8a-gz-Maa\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Tighten\" id=\"46P-cB-AYj\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"tightenKerning:\" target=\"-1\" id=\"hr7-Nz-8ro\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Loosen\" id=\"ogc-rX-tC1\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"loosenKerning:\" target=\"-1\" id=\"8i4-f9-FKE\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Ligatures\" id=\"o6e-r0-MWq\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Ligatures\" id=\"w0m-vy-SC9\">\n                                                <items>\n                                                    <menuItem title=\"Use Default\" id=\"agt-UL-0e3\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"useStandardLigatures:\" target=\"-1\" id=\"7uR-wd-Dx6\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Use None\" id=\"J7y-lM-qPV\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"turnOffLigatures:\" target=\"-1\" id=\"iX2-gA-Ilz\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Use All\" id=\"xQD-1f-W4t\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"useAllLigatures:\" target=\"-1\" id=\"KcB-kA-TuK\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Baseline\" id=\"OaQ-X3-Vso\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Baseline\" id=\"ijk-EB-dga\">\n                                                <items>\n                                                    <menuItem title=\"Use Default\" id=\"3Om-Ey-2VK\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"unscript:\" target=\"-1\" id=\"0vZ-95-Ywn\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Superscript\" id=\"Rqc-34-cIF\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"superscript:\" target=\"-1\" id=\"3qV-fo-wpU\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Subscript\" id=\"I0S-gh-46l\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"subscript:\" target=\"-1\" id=\"Q6W-4W-IGz\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Raise\" id=\"2h7-ER-AoG\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"raiseBaseline:\" target=\"-1\" id=\"4sk-31-7Q9\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Lower\" id=\"1tx-W0-xDw\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"lowerBaseline:\" target=\"-1\" id=\"OF1-bc-KW4\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"Ndw-q3-faq\"/>\n                                        <menuItem title=\"Show Colors\" keyEquivalent=\"C\" id=\"bgn-CT-cEk\">\n                                            <connections>\n                                                <action selector=\"orderFrontColorPanel:\" target=\"-1\" id=\"mSX-Xz-DV3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"iMs-zA-UFJ\"/>\n                                        <menuItem title=\"Copy Style\" keyEquivalent=\"c\" id=\"5Vv-lz-BsD\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"copyFont:\" target=\"-1\" id=\"GJO-xA-L4q\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Paste Style\" keyEquivalent=\"v\" id=\"vKC-jM-MkH\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"pasteFont:\" target=\"-1\" id=\"JfD-CL-leO\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Text\" id=\"Fal-I4-PZk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Text\" id=\"d9c-me-L2H\">\n                                    <items>\n                                        <menuItem title=\"Align Left\" keyEquivalent=\"{\" id=\"ZM1-6Q-yy1\">\n                                            <connections>\n                                                <action selector=\"alignLeft:\" target=\"-1\" id=\"zUv-R1-uAa\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Center\" keyEquivalent=\"|\" id=\"VIY-Ag-zcb\">\n                                            <connections>\n                                                <action selector=\"alignCenter:\" target=\"-1\" id=\"spX-mk-kcS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Justify\" id=\"J5U-5w-g23\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"alignJustified:\" target=\"-1\" id=\"ljL-7U-jND\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Align Right\" keyEquivalent=\"}\" id=\"wb2-vD-lq4\">\n                                            <connections>\n                                                <action selector=\"alignRight:\" target=\"-1\" id=\"r48-bG-YeY\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"4s2-GY-VfK\"/>\n                                        <menuItem title=\"Writing Direction\" id=\"H1b-Si-o9J\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Writing Direction\" id=\"8mr-sm-Yjd\">\n                                                <items>\n                                                    <menuItem title=\"Paragraph\" enabled=\"NO\" id=\"ZvO-Gk-QUH\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                    </menuItem>\n                                                    <menuItem id=\"YGs-j5-SAR\">\n                                                        <string key=\"title\">\tDefault</string>\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"makeBaseWritingDirectionNatural:\" target=\"-1\" id=\"qtV-5e-UBP\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem id=\"Lbh-J2-qVU\">\n                                                        <string key=\"title\">\tLeft to Right</string>\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"makeBaseWritingDirectionLeftToRight:\" target=\"-1\" id=\"S0X-9S-QSf\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem id=\"jFq-tB-4Kx\">\n                                                        <string key=\"title\">\tRight to Left</string>\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"makeBaseWritingDirectionRightToLeft:\" target=\"-1\" id=\"5fk-qB-AqJ\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"swp-gr-a21\"/>\n                                                    <menuItem title=\"Selection\" enabled=\"NO\" id=\"cqv-fj-IhA\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                    </menuItem>\n                                                    <menuItem id=\"Nop-cj-93Q\">\n                                                        <string key=\"title\">\tDefault</string>\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"makeTextWritingDirectionNatural:\" target=\"-1\" id=\"lPI-Se-ZHp\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem id=\"BgM-ve-c93\">\n                                                        <string key=\"title\">\tLeft to Right</string>\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"makeTextWritingDirectionLeftToRight:\" target=\"-1\" id=\"caW-Bv-w94\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem id=\"RB4-Sm-HuC\">\n                                                        <string key=\"title\">\tRight to Left</string>\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"makeTextWritingDirectionRightToLeft:\" target=\"-1\" id=\"EXD-6r-ZUu\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"fKy-g9-1gm\"/>\n                                        <menuItem title=\"Show Ruler\" id=\"vLm-3I-IUL\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleRuler:\" target=\"-1\" id=\"FOx-HJ-KwY\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Copy Ruler\" keyEquivalent=\"c\" id=\"MkV-Pr-PK5\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"copyRuler:\" target=\"-1\" id=\"71i-fW-3W2\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Paste Ruler\" keyEquivalent=\"v\" id=\"LVM-kO-fVI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"pasteRuler:\" target=\"-1\" id=\"cSh-wd-qM2\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Show Toolbar\" keyEquivalent=\"t\" id=\"snW-S8-Cw5\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleToolbarShown:\" target=\"-1\" id=\"BXY-wc-z0C\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Customize Toolbar…\" id=\"1UK-8n-QPP\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"runToolbarCustomizationPalette:\" target=\"-1\" id=\"pQI-g3-MTW\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Help\" id=\"wpr-3q-Mcd\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"F2S-fz-NVQ\">\n                        <items>\n                            <menuItem title=\"DatWeatherDoe Help\" keyEquivalent=\"?\" id=\"FKE-Sm-Kum\">\n                                <connections>\n                                    <action selector=\"showHelp:\" target=\"-1\" id=\"y7X-2Q-9no\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n            </items>\n        </menu>\n        <window title=\"DatWeatherDoe\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" visibleAtLaunch=\"NO\" animationBehavior=\"default\" id=\"QvC-M9-y7g\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <windowPositionMask key=\"initialPositionMask\" leftStrut=\"YES\" rightStrut=\"YES\" topStrut=\"YES\" bottomStrut=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"480\" height=\"360\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"1680\" height=\"1027\"/>\n            <view key=\"contentView\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"480\" height=\"360\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "DatWeatherDoe/Localization/en.xcloc/contents.json",
    "content": "{\n  \"developmentRegion\" : \"en\",\n  \"project\" : \"DatWeatherDoe.xcodeproj\",\n  \"targetLocale\" : \"en\",\n  \"toolInfo\" : {\n    \"toolBuildNumber\" : \"12E262\",\n    \"toolID\" : \"com.apple.dt.xcode\",\n    \"toolName\" : \"Xcode\",\n    \"toolVersion\" : \"12.5\"\n  },\n  \"version\" : \"1.0\"\n}"
  },
  {
    "path": "DatWeatherDoe/Reachability/WeatherReachability.swift",
    "content": "//\n//  WeatherReachability.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/11/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport OSLog\nimport Reachability\n\nfinal class NetworkReachability {\n    private let logger: Logger\n    private var reachability: Reachability?\n    private var retryWhenReachable = false\n\n    init(\n        logger: Logger,\n        onBecomingReachable: @escaping () -> Void\n    ) {\n        self.logger = logger\n\n        setup(callback: onBecomingReachable)\n    }\n\n    private func setup(callback: @escaping () -> Void) {\n        do {\n            reachability = try Reachability()\n            try reachability?.startNotifier()\n            updateReachabilityWhenReachable(callback: callback)\n            updateReachabilityWhenUnreachable()\n        } catch {\n            logger.error(\"Reachability error!\")\n        }\n    }\n\n    private func updateReachabilityWhenReachable(callback: @escaping () -> Void) {\n        reachability?.whenReachable = { [weak self] _ in\n            self?.logger.debug(\"Reachability status: Reachable\")\n\n            if self?.retryWhenReachable == true {\n                self?.retryWhenReachable = false\n                callback()\n            }\n        }\n    }\n\n    private func updateReachabilityWhenUnreachable() {\n        reachability?.whenUnreachable = { [weak self] _ in\n            self?.logger.debug(\"Reachability status: Unreachable\")\n\n            self?.retryWhenReachable = true\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"32-1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"64.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"256-1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"512-1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"1024.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "DatWeatherDoe/Resources/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "DatWeatherDoe/Resources/DatWeatherDoe.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift",
    "content": "//\n//  TestData.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/25/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nlet uvIndicesFor24Hours: [HourlyUVIndex] = {\n    var arr: [HourlyUVIndex] = []\n    // swiftlint:disable:next identifier_name\n    for i in 1 ... 24 {\n        arr.append(HourlyUVIndex(uv: 0))\n    }\n    return arr\n}()\n\nlet response = WeatherAPIResponse(\n    locationName: \"New York City\",\n    temperatureData: .init(\n        tempCelsius: 31.1,\n        feelsLikeTempCelsius: 29.1,\n        tempFahrenheit: 88.0,\n        feelsLikeTempFahrenheit: 84.4\n    ),\n    isDay: true,\n    weatherConditionCode: 1000,\n    humidity: 45,\n    windData: .init(speedMph: 12.3, degrees: 305, direction: \"NW\"),\n    uvIndex: 7.0,\n    forecastDayData: .init(\n        temperatureData: .init(\n            maxTempC: 32.8, maxTempF: 91.0, minTempC: 20.6, minTempF: 69.2\n        ),\n        astro: .init(sunrise: \"05:26 AM\", sunset: \"08:31 PM\"),\n        hour: uvIndicesFor24Hours\n    ),\n    airQualityIndex: .good\n)\n"
  },
  {
    "path": "DatWeatherDoe/Resources/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>WEATHER_API_KEY</key>\n\t<string>${WEATHER_API_KEY}</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>LSApplicationCategoryType</key>\n\t<string>public.app-category.weather</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>LSUIElement</key>\n\t<true/>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>Copyright © 2016 Inder Dhir. All rights reserved.</string>\n\t<key>NSLocationWhenInUseUsageDescription</key>\n\t<string>DatWeatherDoe optionally uses your current location to get the weather</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "DatWeatherDoe/Resources/Localization/Localizable.xcstrings",
    "content": "{\n  \"sourceLanguage\" : \"en\",\n  \"strings\" : {\n    \"\" : {\n\n    },\n    \"[latitude],[longitude]\" : {\n      \"comment\" : \"Placeholder hint for entering Lat/Long\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"[Breite],[Länge]\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"[latitude],[longitude]\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"[latitudine],[longitudine]\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"[緯度],[経度]\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"[纬度]、[经度]\"\n          }\n        }\n      }\n    },\n    \"❗️City\" : {\n      \"comment\" : \"City error when fetching weather\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"en\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"❗️City\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"❗️Città\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"市のエラー\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"城市\"\n          }\n        }\n      }\n    },\n    \"❗️Lat/Long\" : {\n      \"comment\" : \"Lat/Long error when fetching weather\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Fehler mit Breite/Länge\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Erreur de Lat/Lon\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"❗️ Lat/Long\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"緯度/経度のエラー\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"经纬度\"\n          }\n        }\n      }\n    },\n    \"❗️Location\" : {\n      \"comment\" : \"Location error when fetching weather\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Positionsfehler\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Erreur d’emplacement\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"❗️Posizione\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"所在地のエラー\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"位置\"\n          }\n        }\n      }\n    },\n    \"5 min\" : {\n      \"comment\" : \"5 min refresh interval\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"5 Min\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"5 min\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"5 min\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"5分\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"5分钟\"\n          }\n        }\n      }\n    },\n    \"15 min\" : {\n      \"comment\" : \"15 min refresh interval\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"15 Min\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"15 min\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"15 min\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"15分\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"15分钟\"\n          }\n        }\n      }\n    },\n    \"30 min\" : {\n      \"comment\" : \"30 min refresh interval\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"30 Min\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"30 min\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"30 min\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"30分\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"30分钟\"\n          }\n        }\n      }\n    },\n    \"60 min\" : {\n      \"comment\" : \"60 min refresh interval\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"60 Min\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"60 min\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"60 min\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"60分\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"60分钟\"\n          }\n        }\n      }\n    },\n    \"After Temperature\" : {\n      \"comment\" : \"Weather condition after temperature\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Nach Temperatur\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Après température\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Dopo la temperatura\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"温度後\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"在温度后\"\n          }\n        }\n      }\n    },\n    \"All\" : {\n      \"comment\" : \"Show all temperature units\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Alle\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Tous\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Entrambe\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"全て\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"全部\"\n          }\n        }\n      }\n    },\n    \"AQI\" : {\n      \"comment\" : \"Air Quality Index\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"空気質指数\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"空气质量\"\n          }\n        }\n      },\n      \"shouldTranslate\" : false\n    },\n    \"Before Temperature\" : {\n      \"comment\" : \"Weather condition before temperature\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Vor der Temperatur\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Avant la température\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Prima della temperatura\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"温度前\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"在温度前\"\n          }\n        }\n      }\n    },\n    \"CFBundleName\" : {\n      \"comment\" : \"Bundle name\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"DatWeatherDoe\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"DatWeatherDoe\"\n          }\n        }\n      },\n      \"shouldTranslate\" : false\n    },\n    \"Clear\" : {\n      \"comment\" : \"Clear at night weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Klar\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Temps clair\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Sereno\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"クリア\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"晴朗\"\n          }\n        }\n      }\n    },\n    \"Cloudy\" : {\n      \"comment\" : \"Cloudy weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Bedeckt\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Nuageux\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Nuvoloso\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"曇り\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"多云\"\n          }\n        }\n      }\n    },\n    \"Configure\" : {\n      \"comment\" : \"Configure app\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Konfigurieren\"\n          }\n        },\n        \"en\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Configure\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Configurer\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Configura\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"設定する\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"配置\"\n          }\n        }\n      }\n    },\n    \"Done\" : {\n      \"comment\" : \"Finish configuring app\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Fertig\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Terminé\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Fatto\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"完了\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"完成\"\n          }\n        }\n      }\n    },\n    \"Fog\" : {\n      \"comment\" : \"Fog weather condition\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Nebbia\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"霧\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"雾\"\n          }\n        }\n      }\n    },\n    \"Freezing rain\" : {\n      \"comment\" : \"Freezing rain weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Gefrierender Regen\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Pluie verglaçante\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Nevischio\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"雨氷\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"冻雨\"\n          }\n        }\n      }\n    },\n    \"Good\" : {\n      \"comment\" : \"Air Quality Index: Good\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Buona\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"良好\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"优\"\n          }\n        }\n      }\n    },\n    \"Hazardous\" : {\n      \"comment\" : \"Air Quality Index: Hazardous\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Pericolosa\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"危険\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"良\"\n          }\n        }\n      }\n    },\n    \"Heavy rain\" : {\n      \"comment\" : \"Heavy rain weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Starkregen\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Forte pluie\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Pioggia abbondante\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"豪雨\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"大雨\"\n          }\n        }\n      }\n    },\n    \"Hide unit ° symbol\" : {\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Nascondi simbolo °\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"°単位記号を非表示にする\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"隐藏单位符号 °\"\n          }\n        }\n      }\n    },\n    \"Hide unit letter\" : {\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Nascondi unità C\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"単位文字を非表示にする\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"隐藏单位字母 C\"\n          }\n        }\n      }\n    },\n    \"Imperial\" : {\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Imperiale\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"帝国単位\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"英制\"\n          }\n        }\n      }\n    },\n    \"Lat/Long\" : {\n      \"comment\" : \"Weather based on Lat/Long\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Breite/Länge\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Lat/Long\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Lat/Long\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"緯度/経度\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"纬度/经度\"\n          }\n        }\n      }\n    },\n    \"Launch at Login\" : {\n      \"comment\" : \"Launch app at login\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Beim Einloggen starten\"\n          }\n        },\n        \"en\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Launch at Login\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Lancer à la connexion\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Avvia al Login\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"ログイン時に起動\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"自动启动\"\n          }\n        }\n      }\n    },\n    \"Light rain\" : {\n      \"comment\" : \"Light rain weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Leichter Regen\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Pluie légère\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Lieve pioggia\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"小雨\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"小雨\"\n          }\n        }\n      }\n    },\n    \"Location\" : {\n      \"comment\" : \"Weather based on location\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Position\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Emplacement\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Posizione corrente\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"所在地\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"定位\"\n          }\n        }\n      }\n    },\n    \"Metric\" : {\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Metrico\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"メートル法\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"公制\"\n          }\n        }\n      }\n    },\n    \"Mist\" : {\n      \"comment\" : \"Mist weather condition\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Foschia\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"靄\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"薄雾\"\n          }\n        }\n      }\n    },\n    \"Moderate\" : {\n      \"comment\" : \"Air Quality Index: Moderate\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Moderata\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"中等度\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"轻度污染\"\n          }\n        }\n      }\n    },\n    \"NSHumanReadableCopyright\" : {\n      \"comment\" : \"Copyright (human-readable)\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Copyright © 2016 Inder Dhir. All rights reserved.\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Copyright © 2016 Inder Dhir. Alle Rechte vorbehalten.\"\n          }\n        }\n      },\n      \"shouldTranslate\" : false\n    },\n    \"NSLocationWhenInUseUsageDescription\" : {\n      \"comment\" : \"Privacy - Location When In Use Usage Description\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"DatWeatherDoeは、リクエストに応じて地理的位置を使用して天気を判断します。\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"DatWeatherDoe根据请求使用您的地理位置来确定天气.\"\n          }\n        }\n      },\n      \"shouldTranslate\" : false\n    },\n    \"Partly cloudy\" : {\n      \"comment\" : \"Partly cloudy weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Wolkig\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Partiellement couvert\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Parzialmente nuvoloso\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"晴れ時々曇り\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"局部多云\"\n          }\n        }\n      }\n    },\n    \"Partly cloudy with rain\" : {\n      \"comment\" : \"Partly cloudy with rain weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Bewölkt mit Regen\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Partiellement couvert avec pluie\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Pioggia, parzialmente nuvoloso\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"部分的に曇り雨\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"多云转阴有雨\"\n          }\n        }\n      }\n    },\n    \"Quit\" : {\n      \"comment\" : \"Quit app\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Beenden\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Quitter\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Esci\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"終了する\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"退出\"\n          }\n        }\n      }\n    },\n    \"Refresh\" : {\n      \"comment\" : \"Refresh weather\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Aktualisieren\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Rafraîchir\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Aggiorna\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"再読み込みする\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"刷新\"\n          }\n        }\n      }\n    },\n    \"Refresh Interval\" : {\n      \"comment\" : \"Weather refresh interval\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Aktualisierungsintervall\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Intervalle de rafraîchissement\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Intervallo Aggiornamento\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"再読み込み間隔\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"刷新间隔\"\n          }\n        }\n      }\n    },\n    \"Round-off Data\" : {\n      \"comment\" : \"Round-off Data (temperature)\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Daten runden\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Arrondir les données\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Arrotonda Dati\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"データを四捨五入する\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"数据四舍五入\"\n          }\n        }\n      }\n    },\n    \"See Full Weather\" : {\n      \"comment\" : \"See Full Weather\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Komplettes Wetter anzeigen\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Voir la météo complète\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Meteo Completo\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"全ての天気情報を見る\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"查看完整天气\"\n          }\n        }\n      }\n    },\n    \"Separate values with\" : {\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Separa valori con\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"〜で値を区切る\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"分隔符号\"\n          }\n        }\n      }\n    },\n    \"Show Humidity\" : {\n      \"comment\" : \"Show humidity\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Luftfeuchtigkeit anzeigen\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Afficher l’humidité\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Mostra Umidità\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"湿度を表示する\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"显示湿度\"\n          }\n        }\n      }\n    },\n    \"Show UV Index\" : {\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Mostra Indice UV\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"UVインデックスを表示する\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"紫外线指数\"\n          }\n        }\n      }\n    },\n    \"Show Weather Icon\" : {\n      \"comment\" : \"\\\"Show Weather Icon\\\"\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Weather Icon anzeigen\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Afficher l'icône météo\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Mostra Icona Meteo\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"天気アイコンを表示する\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"显示天气图标\"\n          }\n        }\n      }\n    },\n    \"Snow\" : {\n      \"comment\" : \"Snow weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Schnee\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Neige\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Neve\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"雪\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"雪\"\n          }\n        }\n      }\n    },\n    \"Sunny\" : {\n      \"comment\" : \"Sunny weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Sonnig\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Ensoleillé\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Sole\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"晴れ\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"阳光\"\n          }\n        }\n      }\n    },\n    \"Thunderstorm\" : {\n      \"comment\" : \"Thunderstorm weather condition\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Gewitter\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Orage\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Temporale\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"雷雨\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"雷雨\"\n          }\n        }\n      }\n    },\n    \"Unhealthy\" : {\n      \"comment\" : \"Air Quality Index: Unhealthy\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Malsana\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"不健康\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"中度污染\"\n          }\n        }\n      }\n    },\n    \"Unhealthy for sensitive groups\" : {\n      \"comment\" : \"Air Quality Index: Unhealthy for sensitive groups\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Malsana per soggetti sensibili\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"敏感な人々にとって不健康\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"重度污染\"\n          }\n        }\n      }\n    },\n    \"Unit\" : {\n      \"comment\" : \"Temperature unit\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Einheit\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Unité\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Sistema\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"単位\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"单位\"\n          }\n        }\n      }\n    },\n    \"Unknown\" : {\n      \"comment\" : \"Unknown location\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Unbekannt\"\n          }\n        },\n        \"en\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Unknown\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Inconnu\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Sconosciuta\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"不明\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"未知\"\n          }\n        }\n      }\n    },\n    \"Unknown Location\" : {\n      \"comment\" : \"Unknown weather location\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Unbekannte Position\"\n          }\n        },\n        \"en\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Unknown Location\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Emplacement inconnu\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Posizione Sconosciuta\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"所在地不明\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"未知地区\"\n          }\n        }\n      }\n    },\n    \"UV Index\" : {\n      \"comment\" : \"UV Index\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Indice UV\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"UVインデックス\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"紫外线指数\"\n          }\n        }\n      }\n    },\n    \"Very unhealthy\" : {\n      \"comment\" : \"Air Quality Index: Very unhealthy\",\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Molto bassa\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"非常に不健康\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"严重污染\"\n          }\n        }\n      }\n    },\n    \"Weather Condition Position\" : {\n      \"localizations\" : {\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Posizione Condizioni Meteo\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"気象条件の位置\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"天气文本位置\"\n          }\n        }\n      }\n    },\n    \"Weather Condition Text\" : {\n      \"comment\" : \"Weather Condition Text\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Aktuelles Wetter Text\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Condition météo texte\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Testo Condizioni Meteo\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"気象条件テキスト\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"显示天气文本信息\"\n          }\n        }\n      }\n    },\n    \"Weather Source\" : {\n      \"comment\" : \"Source for fetching weather\",\n      \"localizations\" : {\n        \"de\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Wetterquelle\"\n          }\n        },\n        \"fr\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Source de la météo\"\n          }\n        },\n        \"it\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"Fonte Meteo\"\n          }\n        },\n        \"ja\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"気象情報源\"\n          }\n        },\n        \"zh-Hans\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"获取天气方式\"\n          }\n        }\n      }\n    }\n  },\n  \"version\" : \"1.0\"\n}"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/ConfigureOptionsView.swift",
    "content": "//\n//  ConfigureOptionsView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 8/3/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct ConfigureOptionsView: View {\n    @ObservedObject var viewModel: ConfigureViewModel\n\n    var body: some View {\n        Grid(verticalSpacing: 16) {\n            HStack {\n                Text(LocalizedStringKey(\"Unit\"))\n                Spacer()\n                Picker(\"\", selection: $viewModel.measurementUnit) {\n                    Text(LocalizedStringKey(\"Metric\")).tag(MeasurementUnit.metric)\n                    Text(LocalizedStringKey(\"Imperial\")).tag(MeasurementUnit.imperial)\n                    Text(LocalizedStringKey(\"All\")).tag(MeasurementUnit.all)\n                }\n                .frame(width: 120)\n            }\n\n            ConfigureWeatherOptionsView(viewModel: viewModel)\n\n            HStack {\n                Text(LocalizedStringKey(\"Refresh Interval\"))\n                Spacer()\n                Picker(\"\", selection: $viewModel.refreshInterval) {\n                    Text(LocalizedStringKey(\"5 min\")).tag(RefreshInterval.fiveMinutes)\n                    Text(LocalizedStringKey(\"15 min\")).tag(RefreshInterval.fifteenMinutes)\n                    Text(LocalizedStringKey(\"30 min\")).tag(RefreshInterval.thirtyMinutes)\n                    Text(LocalizedStringKey(\"60 min\")).tag(RefreshInterval.sixtyMinutes)\n                }\n                .frame(width: 120)\n            }\n\n            HStack {\n                Text(LocalizedStringKey(\"Show Weather Icon\"))\n                Spacer()\n                Toggle(isOn: $viewModel.isShowingWeatherIcon) {}\n            }\n\n            HStack {\n                Text(LocalizedStringKey(\"Show Humidity\"))\n                Spacer()\n                Toggle(isOn: $viewModel.isShowingHumidity) {}\n            }\n\n            HStack {\n                Text(LocalizedStringKey(\"Show UV Index\"))\n                Spacer()\n                Toggle(isOn: $viewModel.isShowingUVIndex) {}\n            }\n\n            HStack {\n                Text(LocalizedStringKey(\"Round-off Data\"))\n                Spacer()\n                Toggle(isOn: $viewModel.isRoundingOffData) {}\n            }\n\n            ConfigureUnitOptionsView(viewModel: viewModel)\n\n            ConfigureValueSeparatorOptionsView(viewModel: viewModel)\n\n            HStack {\n                Text(LocalizedStringKey(\"Weather Condition Text\"))\n                Spacer()\n                Toggle(isOn: $viewModel.isWeatherConditionAsTextEnabled) {}\n            }\n\n            HStack {\n                Text(LocalizedStringKey(\"Weather Condition Position\"))\n                Spacer()\n                Picker(\"\", selection: $viewModel.weatherConditionPosition) {\n                    Text(LocalizedStringKey(\"Before Temperature\"))\n                        .tag(WeatherConditionPosition.beforeTemperature)\n                    Text(LocalizedStringKey(\"After Temperature\"))\n                        .tag(WeatherConditionPosition.afterTemperature)\n                }\n                .frame(maxWidth: 120)\n            }\n            .disabled(!viewModel.isWeatherConditionAsTextEnabled)\n        }\n        .padding()\n    }\n}\n\n#Preview {\n    ConfigureOptionsView(\n        viewModel: .init(configManager: ConfigManager())\n    )\n    .frame(width: 380)\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/ConfigureUnitOptionsView.swift",
    "content": "//\n//  ConfigureUnitOptionsView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 8/8/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct ConfigureUnitOptionsView: View {\n    @ObservedObject var viewModel: ConfigureViewModel\n\n    var body: some View {\n        Group {\n            HStack {\n                Text(LocalizedStringKey(\"Hide unit letter\"))\n                Spacer()\n                Toggle(isOn: $viewModel.isUnitLetterOff) {}\n            }\n\n            HStack {\n                Text(LocalizedStringKey(\"Hide unit ° symbol\"))\n                Spacer()\n                Toggle(isOn: $viewModel.isUnitSymbolOff) {}\n            }\n        }\n    }\n}\n\n#Preview {\n    Grid {\n        ConfigureUnitOptionsView(\n            viewModel: .init(configManager: ConfigManager())\n        )\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/ConfigureValueSeparatorOptionsView.swift",
    "content": "//\n//  ConfigureValueSeparatorOptionsView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 8/8/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct ConfigureValueSeparatorOptionsView: View {\n    @ObservedObject var viewModel: ConfigureViewModel\n    let valueSeparatorPlaceholder = \"\\u{007C}\"\n\n    var body: some View {\n        HStack {\n            Text(LocalizedStringKey(\"Separate values with\"))\n            Spacer()\n            TextField(valueSeparatorPlaceholder, text: $viewModel.valueSeparator)\n                .font(.body)\n                .foregroundColor(.primary)\n                .frame(width: 114)\n        }\n    }\n}\n\n#Preview {\n    ConfigureValueSeparatorOptionsView(\n        viewModel: .init(configManager: ConfigManager())\n    )\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/ConfigureView.swift",
    "content": "//\n//  ConfigureView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 2/18/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct ConfigureView: View {\n    @ObservedObject var viewModel: ConfigureViewModel\n    let version: String\n    let onSave: () -> Void\n    let onQuit: () -> Void\n\n    var body: some View {\n        VStack {\n            ConfigureOptionsView(viewModel: viewModel)\n\n            HStack {\n                Text(version)\n                    .font(.footnote)\n                    .fontWeight(.thin)\n                    .frame(maxWidth: .infinity, alignment: .leading)\n\n                CustomButton(\n                    text: LocalizedStringKey(\"Done\"),\n                    shortcutKey: \"d\",\n                    onClick: onSave\n                )\n                .frame(maxWidth: .infinity, alignment: .center)\n\n                Text(LocalizedStringKey(\"Quit\"))\n                    .foregroundStyle(Color.red)\n                    .onTapGesture(perform: onQuit)\n                    .frame(maxWidth: .infinity, alignment: .trailing)\n            }\n            .frame(maxWidth: .infinity)\n            .padding([.leading, .trailing])\n        }\n        .padding(.bottom)\n        .frame(width: 380)\n    }\n}\n\n#Preview {\n    ConfigureView(\n        viewModel: .init(configManager: ConfigManager()),\n        version: \"5.0.0\",\n        onSave: {},\n        onQuit: {}\n    )\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/ConfigureViewModel.swift",
    "content": "//\n//  ConfigureViewModel.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 3/20/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Combine\nimport Foundation\n\nfinal class ConfigureViewModel: ObservableObject {\n    @Published var measurementUnit: MeasurementUnit {\n        didSet { configManager.measurementUnit = measurementUnit.rawValue }\n    }\n\n    @Published var weatherSource: WeatherSource\n\n    @Published var weatherSourceText: String\n\n    @Published var refreshInterval: RefreshInterval {\n        didSet { configManager.refreshInterval = refreshInterval.rawValue }\n    }\n\n    @Published var isShowingWeatherIcon: Bool {\n        didSet { configManager.isShowingWeatherIcon = isShowingWeatherIcon }\n    }\n\n    @Published var isShowingHumidity: Bool {\n        didSet { configManager.isShowingHumidity = isShowingHumidity }\n    }\n\n    @Published var isShowingUVIndex: Bool {\n        didSet { configManager.isShowingUVIndex = isShowingUVIndex }\n    }\n\n    @Published var isRoundingOffData: Bool {\n        didSet { configManager.isRoundingOffData = isRoundingOffData }\n    }\n\n    @Published var isUnitLetterOff: Bool {\n        didSet { configManager.isUnitLetterOff = isUnitLetterOff }\n    }\n\n    @Published var isUnitSymbolOff: Bool {\n        didSet { configManager.isUnitSymbolOff = isUnitSymbolOff }\n    }\n\n    @Published var valueSeparator = \"|\" {\n        didSet { configManager.valueSeparator = valueSeparator }\n    }\n\n    @Published var isWeatherConditionAsTextEnabled: Bool {\n        didSet { configManager.isWeatherConditionAsTextEnabled = isWeatherConditionAsTextEnabled }\n    }\n\n    @Published var weatherConditionPosition: WeatherConditionPosition {\n        didSet { configManager.weatherConditionPosition = weatherConditionPosition.rawValue }\n    }\n\n    private let configManager: ConfigManagerType\n\n    init(configManager: ConfigManagerType) {\n        self.configManager = configManager\n\n        measurementUnit = configManager.parsedMeasurementUnit\n        \n        weatherSource = WeatherSource(rawValue: configManager.weatherSource) ?? .location\n        weatherSourceText = configManager.weatherSourceText\n\n        switch configManager.refreshInterval {\n        case 300: refreshInterval = .fiveMinutes\n        case 900: refreshInterval = .fifteenMinutes\n        case 1800: refreshInterval = .thirtyMinutes\n        case 3600: refreshInterval = .sixtyMinutes\n        default: refreshInterval = .fifteenMinutes\n        }\n\n        isShowingWeatherIcon = configManager.isShowingWeatherIcon\n        isShowingHumidity = configManager.isShowingHumidity\n        isShowingUVIndex = configManager.isShowingUVIndex\n        isRoundingOffData = configManager.isRoundingOffData\n        isUnitLetterOff = configManager.isUnitLetterOff\n        isUnitSymbolOff = configManager.isUnitSymbolOff\n        isWeatherConditionAsTextEnabled = configManager.isWeatherConditionAsTextEnabled\n        weatherConditionPosition = WeatherConditionPosition(rawValue: configManager.weatherConditionPosition)\n            ?? .beforeTemperature\n    }\n\n    func saveConfig() {\n        configManager.updateWeatherSource(weatherSource, sourceText: weatherSourceText)\n        weatherSourceText = configManager.weatherSourceText\n\n        configManager.setConfigOptions(\n            .init(\n                refreshInterval: refreshInterval,\n                isShowingHumidity: isShowingHumidity,\n                isShowingUVIndex: isShowingUVIndex,\n                isRoundingOffData: isRoundingOffData,\n                isUnitLetterOff: isUnitLetterOff,\n                isUnitSymbolOff: isUnitSymbolOff,\n                valueSeparator: valueSeparator,\n                isWeatherConditionAsTextEnabled: isWeatherConditionAsTextEnabled\n            )\n        )\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/ConfigureWeatherOptionsView.swift",
    "content": "//\n//  ConfigureWeatherOptionsView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 8/8/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct ConfigureWeatherOptionsView: View {\n    @ObservedObject var viewModel: ConfigureViewModel\n\n    var body: some View {\n        Group {\n            HStack {\n                Text(LocalizedStringKey(\"Weather Source\"))\n                Spacer()\n                Picker(\"\", selection: $viewModel.weatherSource) {\n                    Text(LocalizedStringKey(\"Location\")).tag(WeatherSource.location)\n                    Text(LocalizedStringKey(\"Lat/Long\")).tag(WeatherSource.latLong)\n                }\n                .frame(width: 120)\n            }\n\n            HStack {\n                Text(viewModel.weatherSource.textHint)\n                    .font(.caption)\n                    .foregroundColor(.secondary)\n                Spacer()\n                TextField(viewModel.weatherSource.placeholder, text: $viewModel.weatherSourceText)\n                    .font(.caption)\n                    .foregroundColor(.secondary)\n                    .disabled(viewModel.weatherSource == .location)\n                    .frame(width: 114)\n            }\n        }\n    }\n}\n\n#Preview {\n    Grid {\n        ConfigureWeatherOptionsView(\n            viewModel: .init(configManager: ConfigManager())\n        )\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/Options/MeasurementUnit.swift",
    "content": "//\n//  MeasurementUnit.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 2/7/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nenum MeasurementUnit: String, CaseIterable, Identifiable {\n    case metric, imperial, all\n\n    var id: Self { self }\n\n    var temperatureUnit: TemperatureUnit {\n        switch self {\n        case .metric:\n            .celsius\n        case .imperial:\n            .fahrenheit\n        case .all:\n            .all\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/Options/RefreshInterval.swift",
    "content": "//\n//  RefreshInterval.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 5/23/21.\n//  Copyright © 2021 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nenum RefreshInterval: TimeInterval, CaseIterable, Identifiable {\n    case fiveMinutes = 300\n    case fifteenMinutes = 900\n    case thirtyMinutes = 1800\n    case sixtyMinutes = 3600\n\n    var id: Self { self }\n\n    var title: String {\n        switch self {\n        case .fiveMinutes:\n            String(localized: \"5 min\")\n        case .fifteenMinutes:\n            String(localized: \"15 min\")\n        case .thirtyMinutes:\n            String(localized: \"30 min\")\n        case .sixtyMinutes:\n            String(localized: \"60 min\")\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/Options/TemperatureUnit.swift",
    "content": "//\n//  TemperatureUnit.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 8/8/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nenum TemperatureUnit: String, CaseIterable, Identifiable {\n    case fahrenheit, celsius, all\n\n    var id: Self { self }\n\n    var unitString: String {\n        switch self {\n        case .fahrenheit:\n            \"F\"\n        case .celsius:\n            \"C\"\n        case .all:\n            \"All\"\n        }\n    }\n\n    var degreesString: String {\n        \"\\u{00B0}\\(unitString)\"\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/Options/WeatherConditionPosition.swift",
    "content": "//\n//  WeatherConditionPosition.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 3/27/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nenum WeatherConditionPosition: String, Identifiable {\n    case beforeTemperature, afterTemperature\n\n    var id: Self { self }\n\n    var title: String {\n        switch self {\n        case .beforeTemperature:\n            String(localized: \"Before Temperature\")\n        case .afterTemperature:\n            String(localized: \"After Temperature\")\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Configure/Options/WeatherSource.swift",
    "content": "//\n//  WeatherSource.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 5/23/21.\n//  Copyright © 2021 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nenum WeatherSource: String, CaseIterable {\n    case location, latLong\n\n    var title: String {\n        switch self {\n        case .location:\n            String(localized: \"Location\")\n        case .latLong:\n            String(localized: \"Lat/Long\")\n        }\n    }\n\n    var placeholder: String {\n        switch self {\n        case .location:\n            \"\"\n        case .latLong:\n            \"42,42\"\n        }\n    }\n\n    var textHint: String {\n        switch self {\n        case .location:\n            \"\"\n        case .latLong:\n            String(localized: \"[latitude],[longitude]\")\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Condition/WeatherCondition.swift",
    "content": "//\n//  WeatherCondition.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/29/16.\n//  Copyright © 2016 Inder Dhir. All rights reserved.\n//\n\nimport AppKit\nimport Foundation\n\nenum WeatherCondition {\n    case cloudy, partlyCloudy, partlyCloudyNight\n    case sunny, clearNight\n    case snow\n    case heavyRain, freezingRain, lightRain, partlyCloudyRain\n    case thunderstorm\n    case mist, fog\n\n    static func getFallback(isDay: Bool) -> WeatherCondition {\n        isDay ? .sunny : .clearNight\n    }\n\n    var symbolName: String {\n        switch self {\n        case .cloudy:\n            \"cloud\"\n        case .partlyCloudy:\n            \"cloud.sun\"\n        case .partlyCloudyNight:\n            \"cloud.moon\"\n        case .sunny:\n            \"sun.max\"\n        case .clearNight:\n            \"moon\"\n        case .snow:\n            \"cloud.snow\"\n        case .lightRain, .heavyRain, .freezingRain:\n            \"cloud.rain\"\n        case .partlyCloudyRain:\n            \"cloud.sun.rain\"\n        case .thunderstorm:\n            \"cloud.bolt.rain\"\n        case .mist, .fog:\n            \"cloud.fog\"\n        }\n    }\n\n    var accessibilityLabel: String {\n        switch self {\n        case .cloudy:\n            \"Cloudy\"\n        case .partlyCloudy:\n            \"Partly Cloudy\"\n        case .partlyCloudyNight:\n            \"Partly Cloudy\"\n        case .sunny:\n            \"Sunny\"\n        case .clearNight:\n            \"Clear\"\n        case .snow:\n            \"Snow\"\n        case .lightRain, .heavyRain, .freezingRain:\n            \"Rainy\"\n        case .partlyCloudyRain:\n            \"Partly cloudy with rain\"\n        case .thunderstorm:\n            \"Thunderstorm\"\n        case .mist, .fog:\n            \"Cloudy with Fog\"\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Condition/WeatherConditionBuilder.swift",
    "content": "//\n//  WeatherConditionBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/11/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nprotocol WeatherConditionBuilderType {\n    func build() -> WeatherCondition\n}\n\nfinal class WeatherConditionBuilder: WeatherConditionBuilderType {\n    private let response: WeatherAPIResponse\n\n    init(response: WeatherAPIResponse) {\n        self.response = response\n    }\n\n    func build() -> WeatherCondition {\n        switch response.weatherConditionCode {\n        case 1006, 1009:\n            .cloudy\n        case 1003:\n            response.isDay ? .partlyCloudy : .partlyCloudyNight\n        case 1000:\n            response.isDay ? .sunny : .clearNight\n        case 1030:\n            .mist\n        case 1135, 1147:\n            .fog\n        case 1066,\n             1114, 1117,\n             1210, 1213, 1216, 1219, 1222, 1225, 1237, 1249, 1252, 1255, 1258, 1261, 1264, 1279, 1282:\n            .snow\n        case 1192, 1195, 1243, 1246, 1276:\n            .heavyRain\n        case 1069, 1072, 1168, 1171, 1198, 1201, 1204, 1207:\n            .freezingRain\n        case 1063, 1150, 1153, 1180, 1183, 1186, 1189, 1240:\n            response.isDay ? .partlyCloudyRain : .lightRain\n        case 1087, 1273:\n            .thunderstorm\n        default:\n            WeatherCondition.getFallback(isDay: response.isDay)\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Condition/WeatherConditionTextMapper.swift",
    "content": "//\n//  WeatherConditionTextMapper.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/11/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nprotocol WeatherConditionTextMapperType {\n    func map(_ condition: WeatherCondition) -> String\n}\n\nfinal class WeatherConditionTextMapper: WeatherConditionTextMapperType {\n    func map(_ condition: WeatherCondition) -> String {\n        switch condition {\n        case .cloudy:\n            String(localized: \"Cloudy\")\n\n        case .partlyCloudy, .partlyCloudyNight:\n            String(localized: \"Partly cloudy\")\n\n        case .sunny:\n            String(localized: \"Sunny\")\n\n        case .clearNight:\n            String(localized: \"Clear\")\n\n        case .snow:\n            String(localized: \"Snow\")\n\n        case .heavyRain:\n            String(localized: \"Heavy rain\")\n\n        case .freezingRain:\n            String(localized: \"Freezing rain\")\n\n        case .lightRain:\n            String(localized: \"Light rain\")\n\n        case .partlyCloudyRain:\n            String(localized: \"Partly cloudy with rain\")\n\n        case .thunderstorm:\n            String(localized: \"Thunderstorm\")\n\n        case .mist:\n            String(localized: \"Mist\")\n\n        case .fog:\n            String(localized: \"Fog\")\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Text/HumidityTextBuilder.swift",
    "content": "//\n//  HumidityTextBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/15/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\nimport OSLog\n\nprotocol HumidityTextBuilderType {\n    func build() -> String\n}\n\nfinal class HumidityTextBuilder: HumidityTextBuilderType {\n    private let initial: String\n    private let valueSeparator: String\n    private let humidity: Int\n    private let logger: Logger\n    private let percentString = \"\\u{0025}\"\n\n    private let humidityFormatter: NumberFormatter = {\n        let formatter = NumberFormatter()\n        formatter.numberStyle = .none\n        formatter.maximumFractionDigits = 0\n        return formatter\n    }()\n\n    init(\n        initial: String,\n        valueSeparator: String,\n        humidity: Int,\n        logger: Logger\n    ) {\n        self.initial = initial\n        self.valueSeparator = valueSeparator\n        self.humidity = humidity\n        self.logger = logger\n    }\n\n    func build() -> String {\n        guard let humidityString = buildHumidity() else {\n            logger.error(\"Unable to construct humidity string\")\n\n            return initial\n        }\n\n        return \"\\(initial) \\(valueSeparator) \\(humidityString)\"\n    }\n\n    private func buildHumidity() -> String? {\n        guard let formattedString = buildFormattedString() else { return nil }\n\n        return \"\\(formattedString)\\(percentString)\"\n    }\n\n    private func buildFormattedString() -> String? {\n        humidityFormatter.string(from: NSNumber(value: humidity))\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Text/SunriseAndSunsetTextBuilder.swift",
    "content": "//\n//  SunriseAndSunsetTextBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Markus Markus on 2022-07-26.\n//  Copyright © 2022 Markus Mayer.\n//\n\nimport Foundation\n\nprotocol SunriseAndSunsetTextBuilderType {\n    func build() -> String\n}\n\nfinal class SunriseAndSunsetTextBuilder: SunriseAndSunsetTextBuilderType {\n    private let sunset: String\n    private let sunrise: String\n\n    private let upArrowStr = \"⬆\"\n    private let downArrowStr = \"⬇\"\n\n    init(sunset: String, sunrise: String) {\n        self.sunset = sunset\n        self.sunrise = sunrise\n    }\n\n    func build() -> String {\n        \"\\(upArrowStr)\\(sunrise) \\(downArrowStr)\\(sunset)\"\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Text/Temperature/TemperatureForecastTextBuilder.swift",
    "content": "//\n//  TemperatureForecastTextBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/25/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nprotocol TemperatureForecastTextBuilderType {\n    func build() -> String\n}\n\nfinal class TemperatureForecastTextBuilder: TemperatureForecastTextBuilderType {\n    private let temperatureData: TemperatureData\n    private let forecastTemperatureData: ForecastTemperatureData\n    private let options: TemperatureTextBuilder.Options\n    private let upArrowStr = \"⬆\"\n    private let downArrowStr = \"⬇\"\n    private let degreeString = \"\\u{00B0}\"\n\n    init(\n        temperatureData: TemperatureData,\n        forecastTemperatureData: ForecastTemperatureData,\n        options: TemperatureTextBuilder.Options\n    ) {\n        self.temperatureData = temperatureData\n        self.forecastTemperatureData = forecastTemperatureData\n        self.options = options\n    }\n\n    func build() -> String {\n        if options.unit == .all {\n            buildTemperatureForAllUnits()\n        } else {\n            buildTemperatureForUnit(options.unit)\n        }\n    }\n\n    private func buildTemperatureForAllUnits() -> String {\n        let feelsLikeTempFahrenheit = buildFormattedTemperature(\n            temperatureData.feelsLikeTempFahrenheit, unit: .fahrenheit\n        )\n        let feelsLikeTempCelsius = buildFormattedTemperature(\n            temperatureData.feelsLikeTempCelsius, unit: .celsius\n        )\n        let feelsLikeTemperatureCombined = [feelsLikeTempFahrenheit, feelsLikeTempCelsius]\n            .compactMap { $0 }\n            .joined(separator: \" / \")\n\n        let maxTempFahrenheit = buildFormattedTemperature(\n            forecastTemperatureData.maxTempF, unit: .fahrenheit\n        )\n        let maxTempCelsius = buildFormattedTemperature(\n            forecastTemperatureData.maxTempC, unit: .celsius\n        )\n        let maxTempCombined = [maxTempFahrenheit, maxTempCelsius]\n            .compactMap { $0 }\n            .joined(separator: \" / \")\n        let maxTempStr = [upArrowStr, maxTempCombined]\n            .compactMap { $0 }\n            .joined()\n\n        let minTempFahrenheit = buildFormattedTemperature(\n            forecastTemperatureData.minTempF, unit: .fahrenheit\n        )\n        let minTempCelsius = buildFormattedTemperature(\n            forecastTemperatureData.minTempC, unit: .celsius\n        )\n        let minTempCombined = [minTempFahrenheit, minTempCelsius]\n            .compactMap { $0 }\n            .joined(separator: \" / \")\n        let minTempStr = [downArrowStr, minTempCombined]\n            .compactMap { $0 }\n            .joined()\n\n        let maxAndMinTempStr = [maxTempStr, minTempStr]\n            .compactMap { $0 }\n            .joined(separator: \" \")\n        return [feelsLikeTemperatureCombined, maxAndMinTempStr]\n            .compactMap { $0 }\n            .joined(separator: \" - \")\n    }\n\n    private func buildTemperatureForUnit(_ unit: TemperatureUnit) -> String {\n        let maxTemp = unit == .fahrenheit ?\n        forecastTemperatureData.maxTempF : forecastTemperatureData.maxTempC\n        let formattedMaxTemp = buildFormattedTemperature(maxTemp, unit: unit)\n        let maxTempStr = [upArrowStr, formattedMaxTemp]\n            .compactMap { $0 }\n            .joined()\n\n        let minTemp = unit == .fahrenheit ?\n        forecastTemperatureData.minTempF : forecastTemperatureData.minTempC\n        let formatedMinTemp = buildFormattedTemperature(minTemp, unit: unit)\n        let minTempStr = [downArrowStr, formatedMinTemp]\n            .compactMap { $0 }\n            .joined()\n\n        let maxAndMinTempStr = [maxTempStr, minTempStr]\n            .compactMap { $0 }\n            .joined(separator: \" \")\n\n        let feelsLikeTemp = unit == .fahrenheit ?\n            temperatureData.feelsLikeTempFahrenheit :\n            temperatureData.feelsLikeTempCelsius\n        let formattedFeelsLikeTemp = buildFormattedTemperature(feelsLikeTemp, unit: unit)\n        return [formattedFeelsLikeTemp, maxAndMinTempStr]\n            .compactMap { $0 }\n            .joined(separator: \" - \")\n    }\n\n    private func buildFormattedTemperature(\n        _ temperatureForUnit: Double,\n        unit: TemperatureUnit\n    ) -> String? {\n        guard let temperatureString = TemperatureFormatter()\n            .getFormattedTemperatureString(temperatureForUnit, isRoundingOff: options.isRoundingOff)\n        else {\n            return nil\n        }\n\n        return combineTemperatureWithUnitDegrees(\n            temperature: temperatureString,\n            unit: unit.unitString,\n            isUnitLetterOff: options.isUnitLetterOff,\n            isUnitSymbolOff: options.isUnitSymbolOff\n        )\n    }\n\n    private func combineTemperatureWithUnitDegrees(\n        temperature: String,\n        unit: String,\n        isUnitLetterOff: Bool,\n        isUnitSymbolOff: Bool\n    ) -> String {\n        let unitLetter = isUnitLetterOff ? \"\" : unit\n        let unitSymbol = isUnitSymbolOff ? \"\" : degreeString\n        return [temperature, unitLetter].joined(separator: unitSymbol)\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Text/Temperature/TemperatureFormatter.swift",
    "content": "//\n//  TemperatureFormatter.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/12/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nprotocol TemperatureFormatterType {\n    func getFormattedTemperatureString(\n        _ temperature: Double,\n        isRoundingOff: Bool\n    ) -> String?\n}\n\nfinal class TemperatureFormatter: TemperatureFormatterType {\n    private let formatter: NumberFormatter = {\n        let formatter = NumberFormatter()\n        formatter.numberStyle = .decimal\n        formatter.maximumFractionDigits = 1\n        formatter.roundingMode = .halfUp\n        return formatter\n    }()\n\n    func getFormattedTemperatureString(\n        _ temperature: Double,\n        isRoundingOff: Bool\n    ) -> String? {\n        setupTemperatureRounding(isRoundingOff: isRoundingOff)\n        return formatTemperatureString(temperature)\n    }\n\n    private func setupTemperatureRounding(isRoundingOff: Bool) {\n        formatter.maximumFractionDigits = isRoundingOff ? 0 : 1\n    }\n\n    private func formatTemperatureString(_ temperature: Double) -> String? {\n        let formattedTemperature = formatter.string(from: NSNumber(value: temperature))\n        return fixRoundingIssues(formattedTemperature)\n    }\n\n    private func fixRoundingIssues(_ temperature: String?) -> String? {\n        if temperature == \"-0\" {\n            return \"0\"\n        }\n        return temperature\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Text/Temperature/TemperatureTextBuilder.swift",
    "content": "//\n//  TemperatureTextBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/15/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nprotocol TemperatureTextBuilderType {\n    func build() -> String?\n}\n\nfinal class TemperatureTextBuilder: TemperatureTextBuilderType {\n    struct Options {\n        let unit: TemperatureUnit\n        let isRoundingOff: Bool\n        let isUnitLetterOff: Bool\n        let isUnitSymbolOff: Bool\n    }\n\n    private let response: WeatherAPIResponse\n    private let options: Options\n    private let temperatureCreator: TemperatureWithDegreesCreatorType\n    private let degreeString = \"\\u{00B0}\"\n\n    init(\n        response: WeatherAPIResponse,\n        options: Options,\n        temperatureCreator: TemperatureWithDegreesCreatorType\n    ) {\n        self.response = response\n        self.options = options\n        self.temperatureCreator = temperatureCreator\n    }\n\n    func build() -> String? {\n        if options.unit == .all {\n            buildTemperatureTextForAllUnits()\n        } else {\n            buildTemperatureText(for: options.unit)\n        }\n    }\n\n    private func buildTemperatureTextForAllUnits() -> String? {\n        let temperatureWithDegrees = temperatureCreator.getTemperatureWithDegrees(\n            temperatureInMultipleUnits:\n            .init(\n                fahrenheit: response.temperatureData.tempFahrenheit,\n                celsius: response.temperatureData.tempCelsius\n            ),\n            isRoundingOff: options.isRoundingOff,\n            isUnitLetterOff: options.isUnitLetterOff,\n            isUnitSymbolOff: options.isUnitSymbolOff\n        )\n        return temperatureWithDegrees\n    }\n\n    private func buildTemperatureText(for unit: TemperatureUnit) -> String? {\n        let temperatureForUnit = unit == .fahrenheit ?\n            response.temperatureData.tempFahrenheit :\n            response.temperatureData.tempCelsius\n        let temperatureWithDegrees = temperatureCreator.getTemperatureWithDegrees(\n            temperatureForUnit,\n            unit: unit,\n            isRoundingOff: options.isRoundingOff,\n            isUnitLetterOff: options.isUnitLetterOff,\n            isUnitSymbolOff: options.isUnitSymbolOff\n        )\n        return temperatureWithDegrees\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Text/Temperature/TemperatureWithDegreesCreator.swift",
    "content": "//\n//  TemperatureWithDegreesCreator.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 8/9/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct TemperatureInMultipleUnits {\n    let fahrenheit: Double\n    let celsius: Double\n}\n\nprotocol TemperatureWithDegreesCreatorType {\n    var degreeString: String { get }\n\n    func getTemperatureWithDegrees(\n        temperatureInMultipleUnits: TemperatureInMultipleUnits,\n        isRoundingOff: Bool,\n        isUnitLetterOff: Bool,\n        isUnitSymbolOff: Bool\n    ) -> String?\n\n    func getTemperatureWithDegrees(\n        _ temperature: Double,\n        unit: TemperatureUnit,\n        isRoundingOff: Bool,\n        isUnitLetterOff: Bool,\n        isUnitSymbolOff: Bool\n    ) -> String?\n}\n\nfinal class TemperatureWithDegreesCreator: TemperatureWithDegreesCreatorType {\n    let degreeString = \"\\u{00B0}\"\n\n    func getTemperatureWithDegrees(\n        temperatureInMultipleUnits: TemperatureInMultipleUnits,\n        isRoundingOff: Bool,\n        isUnitLetterOff: Bool,\n        isUnitSymbolOff: Bool\n    ) -> String? {\n        guard let fahrenheitString = TemperatureFormatter().getFormattedTemperatureString(\n            temperatureInMultipleUnits.fahrenheit,\n            isRoundingOff: isRoundingOff\n        ) else {\n            return nil\n        }\n        guard let celsiusString = TemperatureFormatter().getFormattedTemperatureString(\n            temperatureInMultipleUnits.celsius,\n            isRoundingOff: isRoundingOff\n        ) else {\n            return nil\n        }\n\n        let formattedFahrenheit = combineTemperatureWithUnitDegrees(\n            temperature: fahrenheitString,\n            unit: .fahrenheit,\n            isUnitLetterOff: isUnitLetterOff,\n            isUnitSymbolOff: isUnitSymbolOff\n        )\n        let formattedCelsius = combineTemperatureWithUnitDegrees(\n            temperature: celsiusString,\n            unit: .celsius,\n            isUnitLetterOff: isUnitLetterOff,\n            isUnitSymbolOff: isUnitSymbolOff\n        )\n\n        return [formattedFahrenheit, formattedCelsius]\n            .joined(separator: \" / \")\n    }\n\n    func getTemperatureWithDegrees(\n        _ temperature: Double,\n        unit: TemperatureUnit,\n        isRoundingOff: Bool,\n        isUnitLetterOff: Bool,\n        isUnitSymbolOff: Bool\n    ) -> String? {\n        guard let temperatureString = TemperatureFormatter().getFormattedTemperatureString(\n            temperature,\n            isRoundingOff: isRoundingOff\n        ) else {\n            return nil\n        }\n\n        return combineTemperatureWithUnitDegrees(\n            temperature: temperatureString,\n            unit: unit,\n            isUnitLetterOff: isUnitLetterOff,\n            isUnitSymbolOff: isUnitSymbolOff\n        )\n    }\n\n    private func combineTemperatureWithUnitDegrees(\n        temperature: String,\n        unit: TemperatureUnit,\n        isUnitLetterOff: Bool,\n        isUnitSymbolOff: Bool\n    ) -> String {\n        let unitLetter = isUnitLetterOff ? \"\" : unit.unitString\n        let unitSymbol = isUnitSymbolOff ? \"\" : degreeString\n        return [temperature, unitLetter].joined(separator: unitSymbol)\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Text/UVIndexTextBuilder.swift",
    "content": "//\n//  UVIndexTextBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 10/18/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nfinal class UVIndexTextBuilder {\n    private let initial: String\n    private let separator: String\n\n    init(initial: String, separator: String) {\n        self.initial = initial\n        self.separator = separator\n    }\n\n    func build(from response: WeatherAPIResponse) -> String {\n        \"\\(initial) \\(separator) \\(constructUVString(from: response))\"\n    }\n\n    func constructUVString(from response: WeatherAPIResponse) -> String {\n        let currentHour = Calendar.current.component(.hour, from: Date())\n        let currentUVIndex = response.getHourlyUVIndex(hour: currentHour)\n        return \"UV Index: \\(currentUVIndex)\"\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/Text/WeatherTextBuilder.swift",
    "content": "//\n//  WeatherTextBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/15/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport OSLog\n\nprotocol WeatherTextBuilderType {\n    func build() -> String\n}\n\nfinal class WeatherTextBuilder: WeatherTextBuilderType {\n    struct Options {\n        let isWeatherConditionAsTextEnabled: Bool\n        let conditionPosition: WeatherConditionPosition\n        let valueSeparator: String\n        let temperatureOptions: TemperatureTextBuilder.Options\n        let isShowingHumidity: Bool\n        let isShowingUVIndex: Bool\n    }\n\n    private let response: WeatherAPIResponse\n    private let options: Options\n    private let logger: Logger\n\n    init(\n        response: WeatherAPIResponse,\n        options: Options,\n        logger: Logger\n    ) {\n        self.response = response\n        self.options = options\n        self.logger = logger\n    }\n\n    func build() -> String {\n        let finalString = appendTemperatureAsText() |>\n            appendUVIndex |>\n            appendHumidityText |>\n            buildWeatherConditionAsText\n        return finalString\n    }\n\n    private func appendTemperatureAsText() -> String {\n        TemperatureTextBuilder(\n            response: response,\n            options: options.temperatureOptions,\n            temperatureCreator: TemperatureWithDegreesCreator()\n        ).build() ?? \"\"\n    }\n\n    private func appendUVIndex(initial: String) -> String {\n        guard options.isShowingUVIndex else { return initial }\n\n        return UVIndexTextBuilder(\n            initial: initial,\n            separator: options.valueSeparator\n        ).build(from: response)\n    }\n\n    private func appendHumidityText(initial: String) -> String {\n        guard options.isShowingHumidity else { return initial }\n\n        return HumidityTextBuilder(\n            initial: initial,\n            valueSeparator: options.valueSeparator,\n            humidity: response.humidity,\n            logger: logger\n        ).build()\n    }\n\n    private func buildWeatherConditionAsText(initial: String) -> String {\n        guard options.isWeatherConditionAsTextEnabled else { return initial }\n\n        let weatherCondition = WeatherConditionBuilder(response: response).build()\n        let weatherConditionText = WeatherConditionTextMapper().map(weatherCondition)\n\n        let combinedString = options.conditionPosition == .beforeTemperature ?\n            [weatherConditionText, initial] :\n            [initial, weatherConditionText.lowercased()]\n\n        return combinedString\n            .compactMap { $0 }\n            .joined(separator: \", \")\n    }\n}\n\nprecedencegroup ForwardPipe {\n    associativity: left\n}\n\ninfix operator |>: ForwardPipe\n\nprivate func |> <T, U>(value: T, function: (T) -> U) -> U {\n    function(value)\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Decorator/WeatherDataBuilder.swift",
    "content": "//\n//  WeatherDataBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 5/22/21.\n//  Copyright © 2021 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\nimport OSLog\n\nprotocol WeatherDataBuilderType: AnyObject {\n    func build() -> WeatherData\n}\n\nfinal class WeatherDataBuilder: WeatherDataBuilderType {\n    struct Options {\n        let unit: MeasurementUnit\n        let showWeatherIcon: Bool\n        let textOptions: WeatherTextBuilder.Options\n    }\n\n    private let response: WeatherAPIResponse\n    private let options: WeatherDataBuilder.Options\n    private let logger: Logger\n\n    init(\n        response: WeatherAPIResponse,\n        options: WeatherDataBuilder.Options,\n        logger: Logger\n    ) {\n        self.response = response\n        self.options = options\n        self.logger = logger\n    }\n\n    func build() -> WeatherData {\n        .init(\n            showWeatherIcon: options.showWeatherIcon,\n            textualRepresentation: buildTextualRepresentation(),\n            weatherCondition: buildWeatherCondition(),\n            response: response\n        )\n    }\n\n    private func buildTextualRepresentation() -> String {\n        WeatherTextBuilder(\n            response: response,\n            options: options.textOptions,\n            logger: logger\n        ).build()\n    }\n\n    private func buildWeatherCondition() -> WeatherCondition {\n        WeatherConditionBuilder(response: response).build()\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Forecaster/WeatherForecaster.swift",
    "content": "//\n//  WeatherForecaster.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/11/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\n\nprotocol WeatherForecasterType {\n    func seeForecastForCity()\n}\n\nfinal class WeatherForecaster: WeatherForecasterType {\n    private let fullWeatherUrl = URL(string: \"https://www.weatherapi.com/weather/\")!\n\n    func seeForecastForCity() {\n        NSWorkspace.shared.open(fullWeatherUrl)\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Menu Bar/CustomButton.swift",
    "content": "//\n//  CustomButton.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/21/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct CustomButton: View {\n    let text: LocalizedStringKey\n    let textColor: Color\n    let shortcutKey: KeyEquivalent\n    let onClick: () -> Void\n\n    init(\n        text: LocalizedStringKey,\n        textColor: Color = Color.primary,\n        shortcutKey: KeyEquivalent,\n        onClick: @escaping () -> Void\n    ) {\n        self.text = text\n        self.textColor = textColor\n        self.shortcutKey = shortcutKey\n        self.onClick = onClick\n    }\n\n    var body: some View {\n        Button(action: onClick) {\n            Text(text)\n                .foregroundStyle(textColor)\n                .frame(width: 110, height: 22)\n        }.keyboardShortcut(KeyboardShortcut(shortcutKey))\n    }\n}\n\n#Preview {\n    CustomButton(text: \"See Full Weather\", shortcutKey: \"f\") {}\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Menu Bar/DropdownIcon.swift",
    "content": "//\n//  DropdownIcon.swift\n//  DatWeatherDoe\n//\n//  Created by Markus Mayer on 2022-11-21.\n//  Copyright © 2022 Markus Mayer.\n//\n\n// Icons used by the dropdown menu\nenum DropdownIcon {\n    case location\n    case thermometer\n    case sun\n    case wind\n    case uvIndexAndAirQualityText\n\n    var symbolName: String {\n        switch self {\n        case .location:\n            \"location.north.circle\"\n        case .thermometer:\n            \"thermometer.snowflake.circle\"\n        case .sun:\n            \"sun.horizon.circle\"\n        case .wind:\n            \"wind.circle\"\n        case .uvIndexAndAirQualityText:\n            \"sun.max.circle\"\n        }\n    }\n\n    var accessibilityLabel: String {\n        switch self {\n        case .location:\n            \"Location\"\n        case .thermometer:\n            \"Temperature\"\n        case .sun:\n            \"Sunrise and Sunset\"\n        case .wind:\n            \"Wind data\"\n        case .uvIndexAndAirQualityText:\n            \"UV Index and Air Quality Index\"\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Menu Bar/MenuOptionsView.swift",
    "content": "//\n//  MenuOptionsView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/21/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct MenuOptionData {\n    let locationText: String\n    let weatherText: String\n    let sunriseSunsetText: String\n    let tempHumidityWindText: String\n    let uvIndexAndAirQualityText: String\n}\n\nstruct MenuOptionsView: View {\n    let data: MenuOptionData?\n    let onSeeWeather: () -> Void\n    let onRefresh: () -> Void\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: 16) {\n            VStack(alignment: .leading) {\n                NonInteractiveMenuOptionView(icon: .location, text: data?.locationText)\n                NonInteractiveMenuOptionView(icon: .thermometer, text: data?.weatherText)\n                NonInteractiveMenuOptionView(icon: .sun, text: data?.sunriseSunsetText)\n                NonInteractiveMenuOptionView(icon: .wind, text: data?.tempHumidityWindText)\n                NonInteractiveMenuOptionView(\n                    icon: .uvIndexAndAirQualityText,\n                    text: data?.uvIndexAndAirQualityText\n                )\n            }\n\n            HStack {\n                CustomButton(\n                    text: LocalizedStringKey(\"See Full Weather\"),\n                    shortcutKey: \"f\",\n                    onClick: onSeeWeather\n                )\n                .frame(maxWidth: .infinity, alignment: .leading)\n\n                Spacer()\n                    .frame(maxWidth: .infinity)\n\n                CustomButton(text: LocalizedStringKey(\"Refresh\"), shortcutKey: \"r\", onClick: onRefresh)\n                    .frame(maxWidth: .infinity, alignment: .trailing)\n            }\n            .frame(maxWidth: .infinity)\n        }\n        .padding()\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Menu Bar/MenuView.swift",
    "content": "//\n//  MenuView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/23/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct MenuView: View {\n    @ObservedObject var viewModel: WeatherViewModel\n    @ObservedObject var configureViewModel: ConfigureViewModel\n    private var menuOptionData: MenuOptionData?\n    private var version: String\n    private var onSeeWeather: () -> Void\n    private var onRefresh: () -> Void\n    private var onSave: () -> Void\n\n    init(\n        viewModel: WeatherViewModel,\n        configureViewModel: ConfigureViewModel,\n        onSeeWeather: @escaping () -> Void,\n        onRefresh: @escaping () -> Void,\n        onSave: @escaping () -> Void\n    ) {\n        self.viewModel = viewModel\n        self.configureViewModel = configureViewModel\n        self.onSeeWeather = onSeeWeather\n        self.onRefresh = onRefresh\n        self.onSave = onSave\n\n        version = (Bundle.main.infoDictionary?[\"CFBundleShortVersionString\"] as? String) ?? \"1.0.0\"\n    }\n\n    var body: some View {\n        VStack {\n            MenuOptionsView(\n                data: viewModel.menuOptionData,\n                onSeeWeather: onSeeWeather,\n                onRefresh: onRefresh\n            )\n\n            Divider()\n\n            ConfigureView(\n                viewModel: configureViewModel,\n                version: version,\n                onSave: onSave,\n                onQuit: {\n                    NSApp.terminate(self)\n                }\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Menu Bar/NonInteractiveMenuOptionView.swift",
    "content": "//\n//  NonInteractiveMenuOptionView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/21/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct NonInteractiveMenuOptionView: View {\n    let icon: DropdownIcon\n    let text: String?\n\n    var body: some View {\n        HStack(spacing: 6) {\n            Image(systemName: icon.symbolName)\n                .renderingMode(.template)\n                .resizable()\n                .frame(width: 24, height: 24)\n                .accessibilityLabel(icon.accessibilityLabel)\n\n            if let text {\n                Text(text)\n                    .foregroundStyle(Color.secondary)\n                    .frame(maxWidth: .infinity, alignment: .leading)\n            }\n        }\n    }\n}\n\n#Preview {\n    NonInteractiveMenuOptionView(\n        icon: .wind,\n        text: \"Test location\"\n    )\n    .frame(width: 300, height: 100, alignment: .leading)\n    .padding()\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Menu Bar/WindSpeedFormatter.swift",
    "content": "//\n//  WindSpeedFormatter.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 10/12/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nstruct WindSpeedInMultipleUnits {\n    let meterPerSec: Double\n    let milesPerHour: Double\n}\n\nprotocol WindSpeedFormatterType {\n    func getFormattedWindSpeedStringForAllUnits(\n        windData: WindData,\n        isRoundingOff: Bool\n    ) -> String\n\n    func getFormattedWindSpeedString(\n        unit: MeasurementUnit,\n        windData: WindData\n    ) -> String\n}\n\nfinal class WindSpeedFormatter: WindSpeedFormatterType {\n    private let degreeString = \"\\u{00B0}\"\n\n    private let formatter: NumberFormatter = {\n        let formatter = NumberFormatter()\n        formatter.numberStyle = .decimal\n        formatter.maximumFractionDigits = 1\n        formatter.roundingMode = .halfUp\n        return formatter\n    }()\n\n    func getFormattedWindSpeedStringForAllUnits(\n        windData: WindData,\n        isRoundingOff _: Bool\n    ) -> String {\n        let mphSpeed = windData.speedMph\n        let mpsSpeed = mpsSpeedFrom(mphSpeed: mphSpeed)\n\n        let mphRounded = formatter.string(from: NSNumber(value: mphSpeed)) ?? \"\"\n        let windSpeedMph = [mphRounded, \"mi/hr\"].joined()\n\n        let mpsRounded = formatter.string(from: NSNumber(value: mpsSpeed)) ?? \"\"\n        let windSpeedMps = [mpsRounded, \"m/s\"].joined()\n\n        let windSpeedStr = [windSpeedMph, windSpeedMps].joined(separator: \" | \")\n\n        return combinedWindString(windData: windData, windSpeed: windSpeedStr)\n    }\n\n    func getFormattedWindSpeedString(\n        unit: MeasurementUnit,\n        windData: WindData\n    ) -> String {\n        let mphSpeed = windData.speedMph\n        let mpsSpeed = mpsSpeedFrom(mphSpeed: mphSpeed)\n\n        let speed = unit == .imperial ? mphSpeed : mpsSpeed\n        let speedRounded = formatter.string(from: NSNumber(value: speed)) ?? \"\"\n        let windSpeedSuffix = unit == .imperial ? \"mi/hr\" : \"m/s\"\n        let windSpeedStr = [speedRounded, windSpeedSuffix].joined()\n\n        return combinedWindString(windData: windData, windSpeed: windSpeedStr)\n    }\n\n    private func combinedWindString(\n        windData: WindData,\n        windSpeed: String\n    ) -> String {\n        let windDegreesStr = [String(windData.degrees), degreeString].joined()\n        let windDirectionStr = \"(\\(windData.direction))\"\n        let windAndDegreesStr = [windSpeed, windDegreesStr].joined(separator: \" - \")\n        let windFullStr = [windAndDegreesStr, windDirectionStr].joined(separator: \" \")\n        return windFullStr\n    }\n\n    private func mpsSpeedFrom(mphSpeed: Double) -> Double {\n        0.4469 * mphSpeed\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/UI/Status Bar/StatusBarView.swift",
    "content": "//\n//  StatusBarView.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/23/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct StatusBarView: View {\n    let weatherResult: Result<WeatherData, Error>?\n\n    var body: some View {\n        if let weatherResult {\n            switch weatherResult {\n            case let .success(success):\n                HStack {\n                    if success.showWeatherIcon {\n                        Image(systemName: success.weatherCondition.symbolName)\n                            .renderingMode(.template)\n                            .accessibilityLabel(success.weatherCondition.accessibilityLabel)\n                    }\n                    if let text = success.textualRepresentation {\n                        Text(text)\n                    }\n                }\n            case let .failure(failure):\n                Text(failure.localizedDescription)\n            }\n        }\n    }\n}\n\n#if DEBUG\n    #Preview {\n        StatusBarView(\n            weatherResult: .success(\n                .init(\n                    showWeatherIcon: true,\n                    textualRepresentation: \"88\",\n                    weatherCondition: .cloudy,\n                    response: response\n                )\n            )\n        )\n        .frame(width: 100, height: 50)\n    }\n\n    #Preview {\n        StatusBarView(weatherResult: .failure(WeatherError.latLongIncorrect))\n            .frame(width: 100, height: 50)\n    }\n#endif\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Parser/CityWeatherResultParser.swift",
    "content": "//\n//  CityWeatherResultParser.swift\n//  DatWeatherDoe\n//\n//  Created by preckrasno on 14.02.2023.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nfinal class CityWeatherResultParser: WeatherResultParser {\n    override func parse() {\n        switch weatherDataResult {\n        case let .success(weatherData):\n            delegate?.didUpdateWeatherData(weatherData)\n        case let .failure(error):\n            guard let weatherError = error as? WeatherError else { return }\n\n            let errorString = weatherError == WeatherError.cityIncorrect ?\n                errorLabels.cityErrorString :\n                errorLabels.networkErrorString\n\n            delegate?.didFailToUpdateWeatherData(errorString)\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Parser/ZipCodeWeatherResultParser.swift",
    "content": "//\n//  ZipCodeWeatherResultParser.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/17/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nfinal class ZipCodeWeatherResultParser: WeatherResultParser {\n    override func parse() {\n        switch weatherDataResult {\n        case let .success(weatherData):\n            delegate?.didUpdateWeatherData(weatherData)\n        case let .failure(error):\n            guard let weatherError = error as? WeatherError else { return }\n\n            let errorString = weatherError == WeatherError.zipCodeIncorrect ?\n                errorLabels.zipCodeErrorString :\n                errorLabels.networkErrorString\n\n            delegate?.didFailToUpdateWeatherData(errorString)\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/Coordinates/LocationCoordinatesWeatherRepository.swift",
    "content": "//\n//  LocationCoordinatesWeatherRepository.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/14/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport CoreLocation\nimport OSLog\n\nfinal class LocationCoordinatesWeatherRepository: WeatherRepositoryType {\n    private let appId: String\n    private let latLong: String\n    private let networkClient: NetworkClientType\n    private let logger: Logger\n\n    init(\n        appId: String,\n        latLong: String,\n        networkClient: NetworkClientType,\n        logger: Logger\n    ) {\n        self.appId = appId\n        self.latLong = latLong\n        self.networkClient = networkClient\n        self.logger = logger\n    }\n\n    func getWeather() async throws -> WeatherAPIResponse {\n        logger.debug(\"Getting weather via lat/long\")\n\n        do {\n            let location = try getLocationCoordinatesFrom(latLong)\n            let url = try WeatherURLBuilder(appId: appId, location: location).build()\n            let data = try await networkClient.performRequest(url: url)\n            return try WeatherAPIResponseParser().parse(data)\n        } catch {\n            logger.error(\"Getting weather via lat/long failed\")\n\n            throw error\n        }\n    }\n\n    private func getLocationCoordinatesFrom(_ latLong: String) throws -> CLLocationCoordinate2D {\n        try LocationValidator(latLong: latLong).validate()\n\n        let latAndlong = try LocationParser().parseCoordinates(latLong)\n        return latAndlong\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/Coordinates/LocationParser.swift",
    "content": "//\n//  LocationParser.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/10/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport CoreLocation\n\nprotocol LocationParserType {\n    func parseCoordinates(_ latLong: String) throws -> CLLocationCoordinate2D\n}\n\nfinal class LocationParser: LocationParserType {\n    func parseCoordinates(_ latLong: String) throws -> CLLocationCoordinate2D {\n        let latLongCombo = latLong.split(separator: \",\")\n        guard latLongCombo.count == 2 else {\n            throw WeatherError.latLongIncorrect\n        }\n\n        return try parseLocationDegrees(\n            possibleLatitude: String(latLongCombo[0]).trim(),\n            possibleLongitude: String(latLongCombo[1]).trim()\n        )\n    }\n\n    private func parseLocationDegrees(\n        possibleLatitude: String,\n        possibleLongitude: String\n    ) throws -> CLLocationCoordinate2D {\n        let lat = CLLocationDegrees(possibleLatitude.trim())\n        let long = CLLocationDegrees(possibleLongitude.trim())\n        guard let lat, let long else {\n            throw WeatherError.latLongIncorrect\n        }\n\n        return .init(latitude: lat, longitude: long)\n    }\n}\n\nprivate extension String {\n    func trim() -> String { trimmingCharacters(in: .whitespacesAndNewlines) }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/Coordinates/LocationValidator.swift",
    "content": "//\n//  LocationValidator.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/13/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nfinal class LocationValidator: WeatherValidatorType {\n    private let latLong: String\n\n    init(latLong: String) {\n        self.latLong = latLong\n    }\n\n    func validate() throws {\n        let coordinates = latLong.split(separator: \",\")\n        let isValid = coordinates.count == 2\n        if !isValid {\n            throw WeatherError.latLongIncorrect\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift",
    "content": "//\n//  SystemLocationFetcher.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/9/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Combine\nimport CoreLocation\nimport Foundation\nimport OSLog\n\nprotocol SystemLocationFetcherType: AnyObject {\n    func getLocation() async throws -> CLLocationCoordinate2D\n}\n\nfinal actor SystemLocationFetcher: NSObject, SystemLocationFetcherType {\n    private let logger: Logger\n    private var cachedLocation: CLLocationCoordinate2D?\n    private var permissionContinuation: CheckedContinuation<Bool, Never>?\n    private var locationUpdateContinuation: CheckedContinuation<CLLocationCoordinate2D, Error>?\n\n    @MainActor\n    private lazy var locationManager: CLLocationManager = {\n        let locationManager = CLLocationManager()\n        locationManager.delegate = self\n        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer\n        locationManager.distanceFilter = 1000\n        return locationManager\n    }()\n\n    init(logger: Logger) {\n        self.logger = logger\n    }\n\n    func getLocation() async throws(WeatherError) -> CLLocationCoordinate2D {\n        guard CLLocationManager.locationServicesEnabled() else {\n            logger.error(\"Location services not enabled\")\n\n            throw WeatherError.locationError\n        }\n\n        switch CLLocationManager().authorizationStatus {\n        case .notDetermined:\n            let isAuthorized = await withCheckedContinuation { continuation in\n                logger.debug(\"Location permission not determined\")\n\n                permissionContinuation = continuation\n\n                Task { @MainActor in\n                    locationManager.requestWhenInUseAuthorization()\n                }\n            }\n\n            permissionContinuation = nil\n\n            logger.debug(\"Location permission changed, isAuthorized?: \\(isAuthorized)\")\n\n            if isAuthorized {\n                return try await requestLatestOrCachedLocation()\n            }\n\n            throw WeatherError.locationError\n\n        case .authorized:\n            return try await requestLatestOrCachedLocation()\n\n        default:\n            logger.error(\"Location permission has NOT been granted\")\n\n            throw WeatherError.locationError\n        }\n    }\n\n    private func updateCachedLocation(_ location: CLLocationCoordinate2D) {\n        cachedLocation = location\n    }\n\n    private func requestLatestOrCachedLocation() async throws(WeatherError) -> CLLocationCoordinate2D {\n        if let cachedLocation = getCachedLocationIfPresent() {\n            return cachedLocation\n        }\n\n        do {\n            let latestLocation = try await Task.retrying {\n                try await self.requestLocation()\n            }.value\n\n            return latestLocation\n        } catch {\n            throw WeatherError.locationError\n        }\n    }\n\n    private func getCachedLocationIfPresent() -> CLLocationCoordinate2D? {\n        if let cachedLocation {\n            logger.debug(\"Sending cached location\")\n\n            return cachedLocation\n        }\n\n        return nil\n    }\n    \n    private func requestLocation() async throws -> CLLocationCoordinate2D {\n        let coordinate = try await withCheckedThrowingContinuation { continuation in\n            locationUpdateContinuation = continuation\n            \n            Task { @MainActor in\n                locationManager.startUpdatingLocation()\n            }\n        }\n        locationUpdateContinuation = nil\n        return coordinate\n    }\n}\n\n// MARK: CLLocationManagerDelegate\n\nextension SystemLocationFetcher: CLLocationManagerDelegate {\n    nonisolated func locationManagerDidChangeAuthorization(_: CLLocationManager) {\n        let isAuthorized = CLLocationManager().authorizationStatus == .authorized\n\n        Task {\n            await permissionContinuation?.resume(returning: isAuthorized)\n        }\n    }\n\n    nonisolated func locationManager(_: CLLocationManager, didFailWithError error: Error) {\n        Task {\n            await locationUpdateContinuation?.resume(throwing: error)\n        }\n    }\n\n    nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations _: [CLLocation]) {\n        let coordinate = manager.location?.coordinate ?? .init(latitude: .zero, longitude: .zero)\n        Task {\n            await updateCachedLocation(coordinate)\n            await locationUpdateContinuation?.resume(returning: coordinate)\n        }\n    }\n}\n\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/System/SystemLocationWeatherRepository.swift",
    "content": "//\n//  SystemLocationWeatherRepository.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/15/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport CoreLocation\nimport OSLog\n\nfinal class SystemLocationWeatherRepository: WeatherRepositoryType {\n    private let appId: String\n    private let location: CLLocationCoordinate2D\n    private let networkClient: NetworkClientType\n    private let logger: Logger\n\n    init(\n        appId: String,\n        location: CLLocationCoordinate2D,\n        networkClient: NetworkClientType,\n        logger: Logger\n    ) {\n        self.appId = appId\n        self.location = location\n        self.networkClient = networkClient\n        self.logger = logger\n    }\n\n    func getWeather() async throws -> WeatherAPIResponse {\n        logger.debug(\"Getting weather via location\")\n\n        do {\n            let url = try WeatherURLBuilder(appId: appId, location: location).build()\n            let data = try await networkClient.performRequest(url: url)\n            return try WeatherAPIResponseParser().parse(data)\n        } catch {\n            logger.error(\"Getting weather via location failed\")\n\n            throw error\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/System/Task+Retry.swift",
    "content": "//\n//  Task+Retry.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 10/13/25.\n//  Copyright © 2025 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nextension Task where Failure == Error {\n    \n    @discardableResult\n    static func retrying(\n        priority: TaskPriority? = nil,\n        maxRetryCount: Int = 3,\n        retryDelay: TimeInterval = 1,\n        operation: @Sendable @escaping () async throws -> Success\n    ) -> Task {\n        Task(priority: priority) {\n            for _ in 0..<maxRetryCount {\n                do {\n                    return try await operation()\n                } catch {\n                    let oneSecond = TimeInterval(1_000_000_000)\n                    let delay = UInt64(oneSecond * retryDelay)\n                    try await Task<Never, Never>.sleep(nanoseconds: delay)\n                    \n                    continue\n                }\n            }\n            \n            try Task<Never, Never>.checkCancellation()\n            \n            return try await operation()\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/WeatherRepositoryFactory.swift",
    "content": "//\n//  WeatherRepositoryFactory.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 8/7/23.\n//  Copyright © 2023 Inder Dhir. All rights reserved.\n//\n\nimport CoreLocation\nimport Foundation\nimport OSLog\n\nprotocol WeatherRepositoryFactoryType {\n    func create(location: CLLocationCoordinate2D) -> WeatherRepositoryType\n    func create(latLong: String) -> WeatherRepositoryType\n}\n\nfinal class WeatherRepositoryFactory: WeatherRepositoryFactoryType {\n    struct Options {\n        let appId: String\n        let networkClient: NetworkClient\n        let logger: Logger\n    }\n\n    private let appId: String\n    private let networkClient: NetworkClientType\n    private let logger: Logger\n\n    init(appId: String, networkClient: NetworkClientType, logger: Logger) {\n        self.appId = appId\n        self.networkClient = networkClient\n        self.logger = logger\n    }\n\n    func create(location: CLLocationCoordinate2D) -> WeatherRepositoryType {\n        SystemLocationWeatherRepository(\n            appId: appId,\n            location: location,\n            networkClient: networkClient,\n            logger: logger\n        )\n    }\n\n    func create(latLong: String) -> WeatherRepositoryType {\n        LocationCoordinatesWeatherRepository(\n            appId: appId,\n            latLong: latLong,\n            networkClient: networkClient,\n            logger: logger\n        )\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/WeatherRepositoryType.swift",
    "content": "////\n////  WeatherRepositoryType.swift\n////  DatWeatherDoe\n////\n////  Created by Inder Dhir on 1/30/16.\n////  Copyright © 2016 Inder Dhir. All rights reserved.\n////\n\nprotocol WeatherRepositoryType: AnyObject {\n    func getWeather() async throws -> WeatherAPIResponse\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/WeatherURLBuilder.swift",
    "content": "//\n//  WeatherURLBuilder.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/10/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport CoreLocation\nimport Foundation\n\nprotocol WeatherURLBuilderType {\n    func build() throws -> URL\n}\n\nfinal class WeatherURLBuilder: WeatherURLBuilderType {\n    private let apiUrlString = \"https://api.weatherapi.com/v1/forecast.json\"\n    private let appId: String\n    private let location: CLLocationCoordinate2D\n\n    init(appId: String, location: CLLocationCoordinate2D) {\n        self.appId = appId\n        self.location = location\n    }\n\n    func build() throws -> URL {\n        let latLonString = \"\\(location.latitude),\\(location.longitude)\"\n\n        let queryItems: [URLQueryItem] = [\n            URLQueryItem(name: \"key\", value: appId),\n            URLQueryItem(name: \"aqi\", value: String(\"yes\")),\n            URLQueryItem(name: \"q\", value: latLonString),\n            URLQueryItem(name: \"dt\", value: parsedDateToday)\n        ]\n\n        var urlComps = URLComponents(string: apiUrlString)\n        urlComps?.queryItems = queryItems\n\n        guard let finalUrl = urlComps?.url else {\n            throw WeatherError.unableToConstructUrl\n        }\n        return finalUrl\n    }\n\n    private var parsedDateToday: String {\n        let dateFormatter = DateFormatter()\n        dateFormatter.dateFormat = \"yyyy-MM-dd\"\n        return dateFormatter.string(from: Date())\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/Repository/WeatherValidatorType.swift",
    "content": "//\n//  WeatherValidatorType.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/10/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nprotocol WeatherValidatorType: AnyObject {\n    func validate() throws\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/WeatherDataFormatter.swift",
    "content": "//\n//  WeatherDataFormatter.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 6/24/24.\n//  Copyright © 2024 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\n\nprotocol WeatherDataFormatterType {\n    func getLocation(for data: WeatherData) -> String\n    func getWeatherText(for data: WeatherData) -> String\n    func getSunriseSunset(for data: WeatherData) -> String\n    func getWindSpeedItem(for data: WeatherData) -> String\n    func getUVIndexAndAirQuality(for data: WeatherData) -> String\n}\n\nfinal class WeatherDataFormatter: WeatherDataFormatterType {\n    let configManager: ConfigManagerType\n\n    init(configManager: ConfigManagerType) {\n        self.configManager = configManager\n    }\n\n    func getLocation(for data: WeatherData) -> String {\n        [\n            data.response.locationName,\n            WeatherConditionTextMapper().map(data.weatherCondition)\n        ]\n        .joined(separator: \" - \")\n    }\n\n    func getWeatherText(for data: WeatherData) -> String {\n        TemperatureForecastTextBuilder(\n            temperatureData: data.response.temperatureData,\n            forecastTemperatureData: data.response.forecastDayData.temperatureData,\n            options: .init(\n                unit: configManager.parsedMeasurementUnit.temperatureUnit,\n                isRoundingOff: configManager.isRoundingOffData,\n                isUnitLetterOff: configManager.isUnitLetterOff,\n                isUnitSymbolOff: configManager.isUnitSymbolOff\n            )\n        ).build()\n    }\n\n    func getSunriseSunset(for data: WeatherData) -> String {\n        SunriseAndSunsetTextBuilder(\n            sunset: data.response.forecastDayData.astro.sunset,\n            sunrise: data.response.forecastDayData.astro.sunrise\n        ).build()\n    }\n\n    func getWindSpeedItem(for data: WeatherData) -> String {\n        if configManager.measurementUnit == MeasurementUnit.all.rawValue {\n            WindSpeedFormatter()\n                .getFormattedWindSpeedStringForAllUnits(\n                    windData: data.response.windData,\n                    isRoundingOff: configManager.isRoundingOffData\n                )\n        } else {\n            WindSpeedFormatter()\n                .getFormattedWindSpeedString(\n                    unit: configManager.parsedMeasurementUnit,\n                    windData: data.response.windData\n                )\n        }\n    }\n\n    func getUVIndexAndAirQuality(for data: WeatherData) -> String {\n        let currentHour = Calendar.current.component(.hour, from: Date())\n        let currentUVIndex = data.response.getHourlyUVIndex(hour: currentHour)\n        let uvIndex = \"UV Index: \\(currentUVIndex)\"\n\n        let airQualityIndex = \"AQI: \\(data.response.airQualityIndex.description)\"\n\n        return [uvIndex, airQualityIndex].joined(separator: \" | \")\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/WeatherViewModel.swift",
    "content": "//\n//  WeatherViewModel.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/9/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Foundation\nimport OSLog\n\nfinal class WeatherViewModel: WeatherViewModelType, ObservableObject {\n    private let locationFetcher: SystemLocationFetcherType\n    private var weatherFactory: WeatherRepositoryFactoryType\n    private let configManager: ConfigManagerType\n    private var dataFormatter: WeatherDataFormatterType!\n    private let logger: Logger\n    private var reachability: NetworkReachability!\n\n    private let forecaster = WeatherForecaster()\n    private var weatherTimerTask: Task<Void, Never>?\n\n    @Published var menuOptionData: MenuOptionData?\n    @Published var weatherResult: Result<WeatherData, Error>?\n\n    init(\n        locationFetcher: SystemLocationFetcher,\n        weatherFactory: WeatherRepositoryFactoryType,\n        configManager: ConfigManagerType,\n        logger: Logger\n    ) {\n        self.locationFetcher = locationFetcher\n        self.configManager = configManager\n        self.weatherFactory = weatherFactory\n        self.logger = logger\n\n        setupReachability()\n    }\n\n    deinit {\n        weatherTimerTask?.cancel()\n    }\n\n    func setup(with formatter: WeatherDataFormatter) {\n        dataFormatter = formatter\n    }\n\n    func getUpdatedWeatherAfterRefresh() {\n        weatherTimerTask?.cancel()\n        weatherTimerTask = Task { [weak self] in\n            guard let self else { return }\n\n            while !Task.isCancelled {\n                await self.getWeatherWithSelectedSource()\n                try? await Task.sleep(for: .seconds(configManager.refreshInterval))\n            }\n        }\n    }\n\n    func seeForecastForCurrentCity() {\n        forecaster.seeForecastForCity()\n    }\n\n    private func setupReachability() {\n        reachability = NetworkReachability(\n            logger: logger,\n            onBecomingReachable: { [weak self] in\n                self?.getUpdatedWeatherAfterRefresh()\n            }\n        )\n    }\n\n    private func getWeatherWithSelectedSource() async {\n        let weatherSource = WeatherSource(rawValue: configManager.weatherSource) ?? .location\n\n        do {\n            let weatherData = switch weatherSource {\n            case .location:\n                try await getWeatherAfterUpdatingLocation()\n            case .latLong:\n                try await getWeatherViaLocationCoordinates()\n            }\n            updateWeatherData(weatherData)\n        } catch {\n            updateWeatherData(error)\n        }\n    }\n\n    private func getWeatherAfterUpdatingLocation() async throws -> WeatherData {\n        let locationFetcher = locationFetcher\n        let location = try await locationFetcher.getLocation()\n\n        return try await getWeather(\n            repository: weatherFactory.create(location: location),\n            unit: configManager.parsedMeasurementUnit\n        )\n    }\n\n    private func getWeatherViaLocationCoordinates() async throws -> WeatherData {\n        let latLong = configManager.weatherSourceText\n        guard !latLong.isEmpty else {\n            throw WeatherError.latLongIncorrect\n        }\n\n        return try await getWeather(\n            repository: weatherFactory.create(latLong: latLong),\n            unit: configManager.parsedMeasurementUnit\n        )\n    }\n\n    private func buildWeatherDataOptions(for unit: MeasurementUnit) -> WeatherDataBuilder.Options {\n        .init(\n            unit: unit,\n            showWeatherIcon: configManager.isShowingWeatherIcon,\n            textOptions: buildWeatherTextOptions(for: unit)\n        )\n    }\n\n    private func buildWeatherTextOptions(for unit: MeasurementUnit) -> WeatherTextBuilder.Options {\n        let conditionPosition = WeatherConditionPosition(rawValue: configManager.weatherConditionPosition)\n            ?? .beforeTemperature\n        return .init(\n            isWeatherConditionAsTextEnabled: configManager.isWeatherConditionAsTextEnabled,\n            conditionPosition: conditionPosition,\n            valueSeparator: configManager.valueSeparator,\n            temperatureOptions: .init(\n                unit: unit.temperatureUnit,\n                isRoundingOff: configManager.isRoundingOffData,\n                isUnitLetterOff: configManager.isUnitLetterOff,\n                isUnitSymbolOff: configManager.isUnitSymbolOff\n            ),\n            isShowingHumidity: configManager.isShowingHumidity,\n            isShowingUVIndex: configManager.isShowingUVIndex\n        )\n    }\n\n    private func getWeather(\n        repository: WeatherRepositoryType,\n        unit: MeasurementUnit\n    ) async throws -> WeatherData {\n        do {\n            let response = try await repository.getWeather()\n            let weatherData = WeatherDataBuilder(\n                response: response,\n                options: buildWeatherDataOptions(for: unit),\n                logger: logger\n            ).build()\n            return weatherData\n        } catch {\n            throw WeatherError.networkError\n        }\n    }\n\n    private func updateWeatherData(_ data: WeatherData) {\n        menuOptionData = MenuOptionData(\n            locationText: dataFormatter.getLocation(for: data),\n            weatherText: dataFormatter.getWeatherText(for: data),\n            sunriseSunsetText: dataFormatter.getSunriseSunset(for: data),\n            tempHumidityWindText: dataFormatter.getWindSpeedItem(for: data),\n            uvIndexAndAirQualityText: dataFormatter.getUVIndexAndAirQuality(for: data)\n        )\n        weatherResult = .success(data)\n    }\n\n    private func updateWeatherData(_ error: Error) {\n        menuOptionData = nil\n        weatherResult = .failure(error)\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoe/ViewModel/WeatherViewModelType.swift",
    "content": "//\n//  WeatherViewModelType.swift\n//  DatWeatherDoe\n//\n//  Created by Inder Dhir on 1/9/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\nimport Combine\n\nprotocol WeatherViewModelType: AnyObject {\n    func setup(with formatter: WeatherDataFormatter)\n    func getUpdatedWeatherAfterRefresh()\n    func seeForecastForCurrentCity()\n}\n"
  },
  {
    "path": "DatWeatherDoe.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t2000D4082AD86DAA0052EDA6 /* WindSpeedFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2000D4072AD86DAA0052EDA6 /* WindSpeedFormatter.swift */; };\n\t\t20012FA6267980EE00553B60 /* Reachability in Frameworks */ = {isa = PBXBuildFile; productRef = 20012FA5267980EE00553B60 /* Reachability */; };\n\t\t200520F62C29426E00006FC0 /* WeatherDataFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 200520F52C29426E00006FC0 /* WeatherDataFormatter.swift */; };\n\t\t2005C055278CB4E40067BBD1 /* WeatherValidatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2005C054278CB4E40067BBD1 /* WeatherValidatorType.swift */; };\n\t\t2005C057278CB5640067BBD1 /* LocationParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2005C056278CB5640067BBD1 /* LocationParser.swift */; };\n\t\t2005C059278CB5FC0067BBD1 /* WeatherURLBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2005C058278CB5FC0067BBD1 /* WeatherURLBuilder.swift */; };\n\t\t2005C05D278CE0350067BBD1 /* WeatherAPIResponseParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2005C05C278CE0350067BBD1 /* WeatherAPIResponseParser.swift */; };\n\t\t201C6C4620262E380065E795 /* WeatherAPIResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201C6C4520262E380065E795 /* WeatherAPIResponse.swift */; };\n\t\t20206F0727BFF3D7004B418F /* ConfigureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20206F0627BFF3D7004B418F /* ConfigureView.swift */; };\n\t\t2023EDA91C4ED09C0087FD67 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2023EDA81C4ED09C0087FD67 /* Assets.xcassets */; };\n\t\t202B1015278D46AB00ED6D42 /* WeatherConditionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 202B1014278D46AB00ED6D42 /* WeatherConditionBuilder.swift */; };\n\t\t202B101E278D4F1900ED6D42 /* WeatherReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 202B101D278D4F1900ED6D42 /* WeatherReachability.swift */; };\n\t\t202B1029278D5A7100ED6D42 /* WeatherForecaster.swift in Sources */ = {isa = PBXBuildFile; fileRef = 202B1028278D5A7100ED6D42 /* WeatherForecaster.swift */; };\n\t\t202B1030278D632800ED6D42 /* APIKeyParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 202B102F278D632800ED6D42 /* APIKeyParser.swift */; };\n\t\t2039B3DF2C278AA4006A6B6D /* SunriseSunsetData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3DE2C278AA4006A6B6D /* SunriseSunsetData.swift */; };\n\t\t2039B3E32C289697006A6B6D /* TemperatureData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3E22C289697006A6B6D /* TemperatureData.swift */; };\n\t\t2039B3E72C2896BD006A6B6D /* WindData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3E62C2896BD006A6B6D /* WindData.swift */; };\n\t\t2039B3E92C2896CF006A6B6D /* ForecastData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3E82C2896CF006A6B6D /* ForecastData.swift */; };\n\t\t2039B3EB2C2896D7006A6B6D /* ForecastTemperatureData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3EA2C2896D7006A6B6D /* ForecastTemperatureData.swift */; };\n\t\t2039B3EF2C28996D006A6B6D /* WeatherCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3EE2C28996D006A6B6D /* WeatherCondition.swift */; };\n\t\t2039B3F92C28D18A006A6B6D /* DatWeatherDoeApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3F82C28D18A006A6B6D /* DatWeatherDoeApp.swift */; };\n\t\t2039B3FB2C28D1B0006A6B6D /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3FA2C28D1B0006A6B6D /* CustomButton.swift */; };\n\t\t2039B3FD2C28D1C0006A6B6D /* MenuOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3FC2C28D1C0006A6B6D /* MenuOptionsView.swift */; };\n\t\t2039B3FF2C28D1D3006A6B6D /* NonInteractiveMenuOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B3FE2C28D1D3006A6B6D /* NonInteractiveMenuOptionView.swift */; };\n\t\t2039B4012C291B5A006A6B6D /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B4002C291B5A006A6B6D /* MenuView.swift */; };\n\t\t2039B4042C291C35006A6B6D /* StatusBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2039B4032C291C35006A6B6D /* StatusBarView.swift */; };\n\t\t2039B4072C2920BF006A6B6D /* MenuBarExtraAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2039B4062C2920BF006A6B6D /* MenuBarExtraAccess */; };\n\t\t2044E91E2867D3CF00AED55B /* TemperatureForecastTextBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2044E91D2867D3CF00AED55B /* TemperatureForecastTextBuilder.swift */; };\n\t\t204597682A84492400CF73CE /* TemperatureWithDegreesCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 204597672A84492400CF73CE /* TemperatureWithDegreesCreator.swift */; };\n\t\t20459C641C5C50DA004D0DC1 /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20459C631C5C50DA004D0DC1 /* ConfigManager.swift */; };\n\t\t206523C826597B120026C506 /* WeatherError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 206523C726597B120026C506 /* WeatherError.swift */; };\n\t\t206523D62659A92B0026C506 /* WeatherDataBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 206523D52659A92B0026C506 /* WeatherDataBuilder.swift */; };\n\t\t206523FB265AD5730026C506 /* WeatherSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 206523FA265AD5730026C506 /* WeatherSource.swift */; };\n\t\t206523FD265AF03E0026C506 /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 206523FC265AF03E0026C506 /* RefreshInterval.swift */; };\n\t\t206E15252A7C4C5C0096D33C /* ConfigOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 206E15242A7C4C5C0096D33C /* ConfigOptions.swift */; };\n\t\t206E152F2A7C544D0096D33C /* ConfigureOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 206E152E2A7C544D0096D33C /* ConfigureOptionsView.swift */; };\n\t\t206FF1BB2BB4BB9400111EAE /* WeatherConditionPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 206FF1BA2BB4BB9400111EAE /* WeatherConditionPosition.swift */; };\n\t\t2074949927A09278002AA589 /* WeatherURLBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2074949827A09278002AA589 /* WeatherURLBuilderTests.swift */; };\n\t\t2077BC52278DF98800E0453C /* WeatherConditionTextMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2077BC51278DF98800E0453C /* WeatherConditionTextMapper.swift */; };\n\t\t209174102A9BDA4A00BB63E0 /* ConfigureViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2085263427E80B9E0017D7F4 /* ConfigureViewModel.swift */; };\n\t\t209482C629934BFF00AF39D4 /* MeasurementUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209482C529934BFF00AF39D4 /* MeasurementUnit.swift */; };\n\t\t209F8A38279136D300EB5C45 /* LocationValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209F8A37279136D300EB5C45 /* LocationValidator.swift */; };\n\t\t209F8A3D27914A5900EB5C45 /* NetworkClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209F8A3C27914A5900EB5C45 /* NetworkClient.swift */; };\n\t\t209F8A4127915DBC00EB5C45 /* LocationCoordinatesWeatherRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209F8A4027915DBC00EB5C45 /* LocationCoordinatesWeatherRepository.swift */; };\n\t\t20B3845F27A1CFE800F85482 /* LocationValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B3845E27A1CFE800F85482 /* LocationValidatorTests.swift */; };\n\t\t20B46814279394FB00FC6050 /* WeatherTextBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B46813279394FB00FC6050 /* WeatherTextBuilder.swift */; };\n\t\t20B468192793989B00FC6050 /* HumidityTextBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B468182793989B00FC6050 /* HumidityTextBuilder.swift */; };\n\t\t20B4681B2793A7E300FC6050 /* TemperatureTextBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B4681A2793A7E300FC6050 /* TemperatureTextBuilder.swift */; };\n\t\t20B468222793B85900FC6050 /* SystemLocationWeatherRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B468212793B85900FC6050 /* SystemLocationWeatherRepository.swift */; };\n\t\t20B857362CC3304C0098DB1D /* UVIndexTextBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B857352CC3304C0098DB1D /* UVIndexTextBuilder.swift */; };\n\t\t20B9CDCD27B8325900C42508 /* WeatherSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B9CDCC27B8325900C42508 /* WeatherSourceTests.swift */; };\n\t\t20B9CDCF27B8335A00C42508 /* TemperatureUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B9CDCE27B8335A00C42508 /* TemperatureUnitTests.swift */; };\n\t\t20B9CDD127B833EE00C42508 /* RefreshIntervalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B9CDD027B833EE00C42508 /* RefreshIntervalTests.swift */; };\n\t\t20BBCDAA278B8A18007DEEB0 /* WeatherViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BBCDA9278B8A18007DEEB0 /* WeatherViewModel.swift */; };\n\t\t20BBCDAD278B8B28007DEEB0 /* WeatherViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BBCDAC278B8B28007DEEB0 /* WeatherViewModelType.swift */; };\n\t\t20BBCDAF278B92A7007DEEB0 /* SystemLocationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BBCDAE278B92A7007DEEB0 /* SystemLocationFetcher.swift */; };\n\t\t20C6722A2E9DEDCB00A577C1 /* Task+Retry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20C672292E9DEDCB00A577C1 /* Task+Retry.swift */; };\n\t\t20CA6E11278F49AC00FFC53A /* TemperatureFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20CA6E10278F49AC00FFC53A /* TemperatureFormatter.swift */; };\n\t\t20D857102A8317F5005727BB /* ConfigureUnitOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D8570F2A8317F5005727BB /* ConfigureUnitOptionsView.swift */; };\n\t\t20D857122A831802005727BB /* ConfigureWeatherOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D857112A831802005727BB /* ConfigureWeatherOptionsView.swift */; };\n\t\t20D857142A831A16005727BB /* ConfigureValueSeparatorOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D857132A831A16005727BB /* ConfigureValueSeparatorOptionsView.swift */; };\n\t\t20D8571B2A831D62005727BB /* WeatherRepositoryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D8571A2A831D62005727BB /* WeatherRepositoryType.swift */; };\n\t\t20D8571D2A831F40005727BB /* WeatherRepositoryFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D8571C2A831F40005727BB /* WeatherRepositoryFactory.swift */; };\n\t\t20D8571F2A832AC6005727BB /* TemperatureUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D8571E2A832AC6005727BB /* TemperatureUnit.swift */; };\n\t\t20E8A1A62C2B3C5A007E8733 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 20E8A1A52C2B3C5A007E8733 /* Localizable.xcstrings */; };\n\t\t20E8A1AB2C2B3FE6007E8733 /* TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20E8A1AA2C2B3FE6007E8733 /* TestData.swift */; };\n\t\t20F0E5F82C33500900434C3A /* AirQuality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F0E5F72C33500900434C3A /* AirQuality.swift */; };\n\t\t20F17D3A26597A02003A164E /* WeatherData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F17D3926597A02003A164E /* WeatherData.swift */; };\n\t\tE3105BA62818805A00FB4C55 /* SunriseAndSunsetTextBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3105BA52818805A00FB4C55 /* SunriseAndSunsetTextBuilder.swift */; };\n\t\tE3BE2C91292C505A00C4F468 /* DropdownIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3BE2C90292C505A00C4F468 /* DropdownIcon.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t61D04A341C7B7BCF00CBE6AE /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 2023ED9B1C4ED09C0087FD67 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 2023EDA21C4ED09C0087FD67;\n\t\t\tremoteInfo = SwiftWeather;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\t2000D4072AD86DAA0052EDA6 /* WindSpeedFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindSpeedFormatter.swift; sourceTree = \"<group>\"; };\n\t\t200520F52C29426E00006FC0 /* WeatherDataFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherDataFormatter.swift; sourceTree = \"<group>\"; };\n\t\t2005C054278CB4E40067BBD1 /* WeatherValidatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherValidatorType.swift; sourceTree = \"<group>\"; };\n\t\t2005C056278CB5640067BBD1 /* LocationParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationParser.swift; sourceTree = \"<group>\"; };\n\t\t2005C058278CB5FC0067BBD1 /* WeatherURLBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherURLBuilder.swift; sourceTree = \"<group>\"; };\n\t\t2005C05C278CE0350067BBD1 /* WeatherAPIResponseParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherAPIResponseParser.swift; sourceTree = \"<group>\"; };\n\t\t201C6C4520262E380065E795 /* WeatherAPIResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherAPIResponse.swift; sourceTree = \"<group>\"; };\n\t\t20206F0627BFF3D7004B418F /* ConfigureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureView.swift; sourceTree = \"<group>\"; };\n\t\t2023EDA31C4ED09C0087FD67 /* DatWeatherDoe.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DatWeatherDoe.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t2023EDA81C4ED09C0087FD67 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t2023EDAD1C4ED09C0087FD67 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t202B1014278D46AB00ED6D42 /* WeatherConditionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherConditionBuilder.swift; sourceTree = \"<group>\"; };\n\t\t202B101D278D4F1900ED6D42 /* WeatherReachability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherReachability.swift; sourceTree = \"<group>\"; };\n\t\t202B1028278D5A7100ED6D42 /* WeatherForecaster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherForecaster.swift; sourceTree = \"<group>\"; };\n\t\t202B102F278D632800ED6D42 /* APIKeyParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIKeyParser.swift; sourceTree = \"<group>\"; };\n\t\t2039B3DE2C278AA4006A6B6D /* SunriseSunsetData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunriseSunsetData.swift; sourceTree = \"<group>\"; };\n\t\t2039B3E22C289697006A6B6D /* TemperatureData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureData.swift; sourceTree = \"<group>\"; };\n\t\t2039B3E62C2896BD006A6B6D /* WindData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindData.swift; sourceTree = \"<group>\"; };\n\t\t2039B3E82C2896CF006A6B6D /* ForecastData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastData.swift; sourceTree = \"<group>\"; };\n\t\t2039B3EA2C2896D7006A6B6D /* ForecastTemperatureData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastTemperatureData.swift; sourceTree = \"<group>\"; };\n\t\t2039B3EE2C28996D006A6B6D /* WeatherCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherCondition.swift; sourceTree = \"<group>\"; };\n\t\t2039B3F82C28D18A006A6B6D /* DatWeatherDoeApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatWeatherDoeApp.swift; sourceTree = \"<group>\"; };\n\t\t2039B3FA2C28D1B0006A6B6D /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = \"<group>\"; };\n\t\t2039B3FC2C28D1C0006A6B6D /* MenuOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuOptionsView.swift; sourceTree = \"<group>\"; };\n\t\t2039B3FE2C28D1D3006A6B6D /* NonInteractiveMenuOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonInteractiveMenuOptionView.swift; sourceTree = \"<group>\"; };\n\t\t2039B4002C291B5A006A6B6D /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = \"<group>\"; };\n\t\t2039B4032C291C35006A6B6D /* StatusBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarView.swift; sourceTree = \"<group>\"; };\n\t\t2044E91D2867D3CF00AED55B /* TemperatureForecastTextBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureForecastTextBuilder.swift; sourceTree = \"<group>\"; };\n\t\t204597672A84492400CF73CE /* TemperatureWithDegreesCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureWithDegreesCreator.swift; sourceTree = \"<group>\"; };\n\t\t20459C631C5C50DA004D0DC1 /* ConfigManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigManager.swift; sourceTree = \"<group>\"; };\n\t\t206523C726597B120026C506 /* WeatherError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherError.swift; sourceTree = \"<group>\"; };\n\t\t206523D52659A92B0026C506 /* WeatherDataBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherDataBuilder.swift; sourceTree = \"<group>\"; };\n\t\t206523FA265AD5730026C506 /* WeatherSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherSource.swift; sourceTree = \"<group>\"; };\n\t\t206523FC265AF03E0026C506 /* RefreshInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshInterval.swift; sourceTree = \"<group>\"; };\n\t\t206E15242A7C4C5C0096D33C /* ConfigOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigOptions.swift; sourceTree = \"<group>\"; };\n\t\t206E152E2A7C544D0096D33C /* ConfigureOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureOptionsView.swift; sourceTree = \"<group>\"; };\n\t\t206FF1BA2BB4BB9400111EAE /* WeatherConditionPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherConditionPosition.swift; sourceTree = \"<group>\"; };\n\t\t2074949827A09278002AA589 /* WeatherURLBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherURLBuilderTests.swift; sourceTree = \"<group>\"; };\n\t\t2077BC51278DF98800E0453C /* WeatherConditionTextMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherConditionTextMapper.swift; sourceTree = \"<group>\"; };\n\t\t207E989B26838D0D00DC2162 /* DatWeatherDoe.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = DatWeatherDoe.xctestplan; sourceTree = \"<group>\"; };\n\t\t2085263427E80B9E0017D7F4 /* ConfigureViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureViewModel.swift; sourceTree = \"<group>\"; };\n\t\t209482C529934BFF00AF39D4 /* MeasurementUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeasurementUnit.swift; sourceTree = \"<group>\"; };\n\t\t209F8A37279136D300EB5C45 /* LocationValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationValidator.swift; sourceTree = \"<group>\"; };\n\t\t209F8A3C27914A5900EB5C45 /* NetworkClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkClient.swift; sourceTree = \"<group>\"; };\n\t\t209F8A4027915DBC00EB5C45 /* LocationCoordinatesWeatherRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationCoordinatesWeatherRepository.swift; sourceTree = \"<group>\"; };\n\t\t20B3845E27A1CFE800F85482 /* LocationValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationValidatorTests.swift; sourceTree = \"<group>\"; };\n\t\t20B46813279394FB00FC6050 /* WeatherTextBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherTextBuilder.swift; sourceTree = \"<group>\"; };\n\t\t20B468182793989B00FC6050 /* HumidityTextBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HumidityTextBuilder.swift; sourceTree = \"<group>\"; };\n\t\t20B4681A2793A7E300FC6050 /* TemperatureTextBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureTextBuilder.swift; sourceTree = \"<group>\"; };\n\t\t20B468212793B85900FC6050 /* SystemLocationWeatherRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemLocationWeatherRepository.swift; sourceTree = \"<group>\"; };\n\t\t20B857352CC3304C0098DB1D /* UVIndexTextBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UVIndexTextBuilder.swift; sourceTree = \"<group>\"; };\n\t\t20B9CDCC27B8325900C42508 /* WeatherSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherSourceTests.swift; sourceTree = \"<group>\"; };\n\t\t20B9CDCE27B8335A00C42508 /* TemperatureUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureUnitTests.swift; sourceTree = \"<group>\"; };\n\t\t20B9CDD027B833EE00C42508 /* RefreshIntervalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshIntervalTests.swift; sourceTree = \"<group>\"; };\n\t\t20BBCDA9278B8A18007DEEB0 /* WeatherViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewModel.swift; sourceTree = \"<group>\"; };\n\t\t20BBCDAC278B8B28007DEEB0 /* WeatherViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewModelType.swift; sourceTree = \"<group>\"; };\n\t\t20BBCDAE278B92A7007DEEB0 /* SystemLocationFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemLocationFetcher.swift; sourceTree = \"<group>\"; };\n\t\t20C672292E9DEDCB00A577C1 /* Task+Retry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Task+Retry.swift\"; sourceTree = \"<group>\"; };\n\t\t20CA6E10278F49AC00FFC53A /* TemperatureFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureFormatter.swift; sourceTree = \"<group>\"; };\n\t\t20CB480B27B83EF90043C60F /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = \"<group>\"; };\n\t\t20D8570F2A8317F5005727BB /* ConfigureUnitOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureUnitOptionsView.swift; sourceTree = \"<group>\"; };\n\t\t20D857112A831802005727BB /* ConfigureWeatherOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureWeatherOptionsView.swift; sourceTree = \"<group>\"; };\n\t\t20D857132A831A16005727BB /* ConfigureValueSeparatorOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureValueSeparatorOptionsView.swift; sourceTree = \"<group>\"; };\n\t\t20D8571A2A831D62005727BB /* WeatherRepositoryType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherRepositoryType.swift; sourceTree = \"<group>\"; };\n\t\t20D8571C2A831F40005727BB /* WeatherRepositoryFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherRepositoryFactory.swift; sourceTree = \"<group>\"; };\n\t\t20D8571E2A832AC6005727BB /* TemperatureUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureUnit.swift; sourceTree = \"<group>\"; };\n\t\t20E8A1A52C2B3C5A007E8733 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = \"<group>\"; };\n\t\t20E8A1AA2C2B3FE6007E8733 /* TestData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestData.swift; sourceTree = \"<group>\"; };\n\t\t20F0E5F72C33500900434C3A /* AirQuality.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AirQuality.swift; sourceTree = \"<group>\"; };\n\t\t20F17D3926597A02003A164E /* WeatherData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherData.swift; sourceTree = \"<group>\"; };\n\t\t20FF84E92456326400FC9DAA /* DatWeatherDoe.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DatWeatherDoe.entitlements; sourceTree = \"<group>\"; };\n\t\t61D04A2F1C7B7BCF00CBE6AE /* DatWeatherDoeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DatWeatherDoeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tE3105BA52818805A00FB4C55 /* SunriseAndSunsetTextBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SunriseAndSunsetTextBuilder.swift; sourceTree = \"<group>\"; };\n\t\tE3BE2C90292C505A00C4F468 /* DropdownIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownIcon.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t2023EDA01C4ED09C0087FD67 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2039B4072C2920BF006A6B6D /* MenuBarExtraAccess in Frameworks */,\n\t\t\t\t20012FA6267980EE00553B60 /* Reachability in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t61D04A2C1C7B7BCF00CBE6AE /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t2005C050278CB4040067BBD1 /* Repository */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B468202793B81700FC6050 /* Coordinates */,\n\t\t\t\t20B4681F2793B81200FC6050 /* System */,\n\t\t\t\t2005C058278CB5FC0067BBD1 /* WeatherURLBuilder.swift */,\n\t\t\t\t20D8571A2A831D62005727BB /* WeatherRepositoryType.swift */,\n\t\t\t\t2005C054278CB4E40067BBD1 /* WeatherValidatorType.swift */,\n\t\t\t\t20D8571C2A831F40005727BB /* WeatherRepositoryFactory.swift */,\n\t\t\t);\n\t\t\tpath = Repository;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2023ED9A1C4ED09C0087FD67 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20CB480B27B83EF90043C60F /* Config.xcconfig */,\n\t\t\t\t2023EDA51C4ED09C0087FD67 /* DatWeatherDoe */,\n\t\t\t\t61D04A301C7B7BCF00CBE6AE /* DatWeatherDoeTests */,\n\t\t\t\t2023EDA41C4ED09C0087FD67 /* Products */,\n\t\t\t\tBF848D6F74093EEA00A37228 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2023EDA41C4ED09C0087FD67 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2023EDA31C4ED09C0087FD67 /* DatWeatherDoe.app */,\n\t\t\t\t61D04A2F1C7B7BCF00CBE6AE /* DatWeatherDoeTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2023EDA51C4ED09C0087FD67 /* DatWeatherDoe */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t206523CE2659838A0026C506 /* API */,\n\t\t\t\t206523CF265983A90026C506 /* Config */,\n\t\t\t\t202B101F278D4F2100ED6D42 /* Reachability */,\n\t\t\t\t206523CC2659835D0026C506 /* UI */,\n\t\t\t\t20BBCDAB278B8B1C007DEEB0 /* ViewModel */,\n\t\t\t\t206523CD265983750026C506 /* Resources */,\n\t\t\t\t2039B3F82C28D18A006A6B6D /* DatWeatherDoeApp.swift */,\n\t\t\t);\n\t\t\tpath = DatWeatherDoe;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t202B1016278D481100ED6D42 /* Condition */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2039B3EE2C28996D006A6B6D /* WeatherCondition.swift */,\n\t\t\t\t202B1014278D46AB00ED6D42 /* WeatherConditionBuilder.swift */,\n\t\t\t\t2077BC51278DF98800E0453C /* WeatherConditionTextMapper.swift */,\n\t\t\t);\n\t\t\tpath = Condition;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t202B101F278D4F2100ED6D42 /* Reachability */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t202B101D278D4F1900ED6D42 /* WeatherReachability.swift */,\n\t\t\t);\n\t\t\tpath = Reachability;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t202B1027278D58B900ED6D42 /* Menu Bar */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2000D4072AD86DAA0052EDA6 /* WindSpeedFormatter.swift */,\n\t\t\t\tE3BE2C90292C505A00C4F468 /* DropdownIcon.swift */,\n\t\t\t\t2039B3FA2C28D1B0006A6B6D /* CustomButton.swift */,\n\t\t\t\t2039B3FC2C28D1C0006A6B6D /* MenuOptionsView.swift */,\n\t\t\t\t2039B3FE2C28D1D3006A6B6D /* NonInteractiveMenuOptionView.swift */,\n\t\t\t\t2039B4002C291B5A006A6B6D /* MenuView.swift */,\n\t\t\t);\n\t\t\tpath = \"Menu Bar\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2039B4022C291C19006A6B6D /* Status Bar */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2039B4032C291C35006A6B6D /* StatusBarView.swift */,\n\t\t\t);\n\t\t\tpath = \"Status Bar\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t206523CC2659835D0026C506 /* UI */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20CA6E0D278F3FDF00FFC53A /* Configure */,\n\t\t\t\t206523D72659CC4C0026C506 /* Decorator */,\n\t\t\t\t209F8A332790EC5000EB5C45 /* Forecaster */,\n\t\t\t\t202B1027278D58B900ED6D42 /* Menu Bar */,\n\t\t\t\t2039B4022C291C19006A6B6D /* Status Bar */,\n\t\t\t);\n\t\t\tpath = UI;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t206523CD265983750026C506 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20E8A1A92C2B3E4A007E8733 /* DevelopmentAssets */,\n\t\t\t\t20652400265B0F870026C506 /* Localization */,\n\t\t\t\t20FF84E92456326400FC9DAA /* DatWeatherDoe.entitlements */,\n\t\t\t\t2023EDA81C4ED09C0087FD67 /* Assets.xcassets */,\n\t\t\t\t2023EDAD1C4ED09C0087FD67 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t206523CE2659838A0026C506 /* API */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t209F8A3927913D0400EB5C45 /* Response */,\n\t\t\t\t209F8A3C27914A5900EB5C45 /* NetworkClient.swift */,\n\t\t\t\t20F17D3926597A02003A164E /* WeatherData.swift */,\n\t\t\t\t206523C726597B120026C506 /* WeatherError.swift */,\n\t\t\t);\n\t\t\tpath = API;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t206523CF265983A90026C506 /* Config */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t206E15242A7C4C5C0096D33C /* ConfigOptions.swift */,\n\t\t\t\t20459C631C5C50DA004D0DC1 /* ConfigManager.swift */,\n\t\t\t\t202B102F278D632800ED6D42 /* APIKeyParser.swift */,\n\t\t\t);\n\t\t\tpath = Config;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t206523D72659CC4C0026C506 /* Decorator */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t202B1016278D481100ED6D42 /* Condition */,\n\t\t\t\t20B468152793958800FC6050 /* Text */,\n\t\t\t\t206523D52659A92B0026C506 /* WeatherDataBuilder.swift */,\n\t\t\t);\n\t\t\tpath = Decorator;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20652400265B0F870026C506 /* Localization */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20E8A1A52C2B3C5A007E8733 /* Localizable.xcstrings */,\n\t\t\t);\n\t\t\tpath = Localization;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2074949627A09263002AA589 /* API */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2074949727A0926E002AA589 /* Repository */,\n\t\t\t);\n\t\t\tpath = API;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2074949727A0926E002AA589 /* Repository */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2074949A27A09337002AA589 /* Location */,\n\t\t\t\t2074949827A09278002AA589 /* WeatherURLBuilderTests.swift */,\n\t\t\t);\n\t\t\tpath = Repository;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2074949A27A09337002AA589 /* Location */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B3845D27A1CFC200F85482 /* Coordinates */,\n\t\t\t);\n\t\t\tpath = Location;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t209F8A332790EC5000EB5C45 /* Forecaster */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t202B1028278D5A7100ED6D42 /* WeatherForecaster.swift */,\n\t\t\t);\n\t\t\tpath = Forecaster;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t209F8A3927913D0400EB5C45 /* Response */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t201C6C4520262E380065E795 /* WeatherAPIResponse.swift */,\n\t\t\t\t2005C05C278CE0350067BBD1 /* WeatherAPIResponseParser.swift */,\n\t\t\t\t2039B3DE2C278AA4006A6B6D /* SunriseSunsetData.swift */,\n\t\t\t\t2039B3E22C289697006A6B6D /* TemperatureData.swift */,\n\t\t\t\t2039B3E62C2896BD006A6B6D /* WindData.swift */,\n\t\t\t\t2039B3E82C2896CF006A6B6D /* ForecastData.swift */,\n\t\t\t\t2039B3EA2C2896D7006A6B6D /* ForecastTemperatureData.swift */,\n\t\t\t\t20F0E5F72C33500900434C3A /* AirQuality.swift */,\n\t\t\t);\n\t\t\tpath = Response;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20AF0D962795257100AA7D18 /* Options */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20D8571E2A832AC6005727BB /* TemperatureUnit.swift */,\n\t\t\t\t209482C529934BFF00AF39D4 /* MeasurementUnit.swift */,\n\t\t\t\t206523FA265AD5730026C506 /* WeatherSource.swift */,\n\t\t\t\t206523FC265AF03E0026C506 /* RefreshInterval.swift */,\n\t\t\t\t206FF1BA2BB4BB9400111EAE /* WeatherConditionPosition.swift */,\n\t\t\t);\n\t\t\tpath = Options;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20B3845D27A1CFC200F85482 /* Coordinates */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B3845E27A1CFE800F85482 /* LocationValidatorTests.swift */,\n\t\t\t);\n\t\t\tpath = Coordinates;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20B468152793958800FC6050 /* Text */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B4681C2793AA0E00FC6050 /* Temperature */,\n\t\t\t\t20B468182793989B00FC6050 /* HumidityTextBuilder.swift */,\n\t\t\t\tE3105BA52818805A00FB4C55 /* SunriseAndSunsetTextBuilder.swift */,\n\t\t\t\t20B46813279394FB00FC6050 /* WeatherTextBuilder.swift */,\n\t\t\t\t20B857352CC3304C0098DB1D /* UVIndexTextBuilder.swift */,\n\t\t\t);\n\t\t\tpath = Text;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20B4681C2793AA0E00FC6050 /* Temperature */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B4681A2793A7E300FC6050 /* TemperatureTextBuilder.swift */,\n\t\t\t\t20CA6E10278F49AC00FFC53A /* TemperatureFormatter.swift */,\n\t\t\t\t2044E91D2867D3CF00AED55B /* TemperatureForecastTextBuilder.swift */,\n\t\t\t\t204597672A84492400CF73CE /* TemperatureWithDegreesCreator.swift */,\n\t\t\t);\n\t\t\tpath = Temperature;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20B4681F2793B81200FC6050 /* System */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B468212793B85900FC6050 /* SystemLocationWeatherRepository.swift */,\n\t\t\t\t20BBCDAE278B92A7007DEEB0 /* SystemLocationFetcher.swift */,\n\t\t\t\t20C672292E9DEDCB00A577C1 /* Task+Retry.swift */,\n\t\t\t);\n\t\t\tpath = System;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20B468202793B81700FC6050 /* Coordinates */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t209F8A4027915DBC00EB5C45 /* LocationCoordinatesWeatherRepository.swift */,\n\t\t\t\t209F8A37279136D300EB5C45 /* LocationValidator.swift */,\n\t\t\t\t2005C056278CB5640067BBD1 /* LocationParser.swift */,\n\t\t\t);\n\t\t\tpath = Coordinates;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20B9CDC927B8323900C42508 /* UI */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B9CDCA27B8324300C42508 /* Configure */,\n\t\t\t);\n\t\t\tpath = UI;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20B9CDCA27B8324300C42508 /* Configure */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B9CDCB27B8324900C42508 /* Options */,\n\t\t\t);\n\t\t\tpath = Configure;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20B9CDCB27B8324900C42508 /* Options */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20B9CDCC27B8325900C42508 /* WeatherSourceTests.swift */,\n\t\t\t\t20B9CDCE27B8335A00C42508 /* TemperatureUnitTests.swift */,\n\t\t\t\t20B9CDD027B833EE00C42508 /* RefreshIntervalTests.swift */,\n\t\t\t);\n\t\t\tpath = Options;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20BBCDAB278B8B1C007DEEB0 /* ViewModel */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2005C050278CB4040067BBD1 /* Repository */,\n\t\t\t\t20BBCDAC278B8B28007DEEB0 /* WeatherViewModelType.swift */,\n\t\t\t\t20BBCDA9278B8A18007DEEB0 /* WeatherViewModel.swift */,\n\t\t\t\t200520F52C29426E00006FC0 /* WeatherDataFormatter.swift */,\n\t\t\t);\n\t\t\tpath = ViewModel;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20CA6E0D278F3FDF00FFC53A /* Configure */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20AF0D962795257100AA7D18 /* Options */,\n\t\t\t\t2085263427E80B9E0017D7F4 /* ConfigureViewModel.swift */,\n\t\t\t\t20206F0627BFF3D7004B418F /* ConfigureView.swift */,\n\t\t\t\t206E152E2A7C544D0096D33C /* ConfigureOptionsView.swift */,\n\t\t\t\t20D857112A831802005727BB /* ConfigureWeatherOptionsView.swift */,\n\t\t\t\t20D8570F2A8317F5005727BB /* ConfigureUnitOptionsView.swift */,\n\t\t\t\t20D857132A831A16005727BB /* ConfigureValueSeparatorOptionsView.swift */,\n\t\t\t);\n\t\t\tpath = Configure;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t20E8A1A92C2B3E4A007E8733 /* DevelopmentAssets */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t20E8A1AA2C2B3FE6007E8733 /* TestData.swift */,\n\t\t\t);\n\t\t\tpath = DevelopmentAssets;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t61D04A301C7B7BCF00CBE6AE /* DatWeatherDoeTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t207E989B26838D0D00DC2162 /* DatWeatherDoe.xctestplan */,\n\t\t\t\t2074949627A09263002AA589 /* API */,\n\t\t\t\t20B9CDC927B8323900C42508 /* UI */,\n\t\t\t);\n\t\t\tpath = DatWeatherDoeTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF848D6F74093EEA00A37228 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t2023EDA21C4ED09C0087FD67 /* DatWeatherDoe */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 2023EDB01C4ED09C0087FD67 /* Build configuration list for PBXNativeTarget \"DatWeatherDoe\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t2023ED9F1C4ED09C0087FD67 /* Sources */,\n\t\t\t\t2023EDA01C4ED09C0087FD67 /* Frameworks */,\n\t\t\t\t2023EDA11C4ED09C0087FD67 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t20CB68BE2A2D9033001C73B9 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = DatWeatherDoe;\n\t\t\tpackageProductDependencies = (\n\t\t\t\t20012FA5267980EE00553B60 /* Reachability */,\n\t\t\t\t2039B4062C2920BF006A6B6D /* MenuBarExtraAccess */,\n\t\t\t);\n\t\t\tproductName = SwiftWeather;\n\t\t\tproductReference = 2023EDA31C4ED09C0087FD67 /* DatWeatherDoe.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\t61D04A2E1C7B7BCF00CBE6AE /* DatWeatherDoeTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 61D04A381C7B7BCF00CBE6AE /* Build configuration list for PBXNativeTarget \"DatWeatherDoeTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t61D04A2B1C7B7BCF00CBE6AE /* Sources */,\n\t\t\t\t61D04A2C1C7B7BCF00CBE6AE /* Frameworks */,\n\t\t\t\t61D04A2D1C7B7BCF00CBE6AE /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t61D04A351C7B7BCF00CBE6AE /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = DatWeatherDoeTests;\n\t\t\tpackageProductDependencies = (\n\t\t\t);\n\t\t\tproductName = DefaultsTests;\n\t\t\tproductReference = 61D04A2F1C7B7BCF00CBE6AE /* DatWeatherDoeTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t2023ED9B1C4ED09C0087FD67 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastSwiftUpdateCheck = 1330;\n\t\t\t\tLastUpgradeCheck = 2600;\n\t\t\t\tORGANIZATIONNAME = \"Inder Dhir\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t2023EDA21C4ED09C0087FD67 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.2;\n\t\t\t\t\t\tLastSwiftMigration = 1000;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 0;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t61D04A2E1C7B7BCF00CBE6AE = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.2.1;\n\t\t\t\t\t\tLastSwiftMigration = 0810;\n\t\t\t\t\t\tTestTargetID = 2023EDA21C4ED09C0087FD67;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 2023ED9E1C4ED09C0087FD67 /* Build configuration list for PBXProject \"DatWeatherDoe\" */;\n\t\t\tcompatibilityVersion = \"Xcode 12.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t\tfr,\n\t\t\t\tde,\n\t\t\t\tit,\n\t\t\t\tja,\n\t\t\t\t\"zh-Hans\",\n\t\t\t);\n\t\t\tmainGroup = 2023ED9A1C4ED09C0087FD67;\n\t\t\tpackageReferences = (\n\t\t\t\t20012FA4267980EE00553B60 /* XCRemoteSwiftPackageReference \"Reachability\" */,\n\t\t\t\t20CB68BC2A2D9029001C73B9 /* XCRemoteSwiftPackageReference \"SwiftLint\" */,\n\t\t\t\t2000D40D2AD88D6B0052EDA6 /* XCRemoteSwiftPackageReference \"SwiftFormat\" */,\n\t\t\t\t2039B4052C2920BF006A6B6D /* XCRemoteSwiftPackageReference \"MenuBarExtraAccess\" */,\n\t\t\t);\n\t\t\tproductRefGroup = 2023EDA41C4ED09C0087FD67 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t2023EDA21C4ED09C0087FD67 /* DatWeatherDoe */,\n\t\t\t\t61D04A2E1C7B7BCF00CBE6AE /* DatWeatherDoeTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t2023EDA11C4ED09C0087FD67 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2023EDA91C4ED09C0087FD67 /* Assets.xcassets in Resources */,\n\t\t\t\t20E8A1A62C2B3C5A007E8733 /* Localizable.xcstrings in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t61D04A2D1C7B7BCF00CBE6AE /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t2023ED9F1C4ED09C0087FD67 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t209174102A9BDA4A00BB63E0 /* ConfigureViewModel.swift in Sources */,\n\t\t\t\t2005C055278CB4E40067BBD1 /* WeatherValidatorType.swift in Sources */,\n\t\t\t\t20B468222793B85900FC6050 /* SystemLocationWeatherRepository.swift in Sources */,\n\t\t\t\t20E8A1AB2C2B3FE6007E8733 /* TestData.swift in Sources */,\n\t\t\t\t2039B3FB2C28D1B0006A6B6D /* CustomButton.swift in Sources */,\n\t\t\t\t20BBCDAF278B92A7007DEEB0 /* SystemLocationFetcher.swift in Sources */,\n\t\t\t\t209482C629934BFF00AF39D4 /* MeasurementUnit.swift in Sources */,\n\t\t\t\t2005C057278CB5640067BBD1 /* LocationParser.swift in Sources */,\n\t\t\t\t2005C05D278CE0350067BBD1 /* WeatherAPIResponseParser.swift in Sources */,\n\t\t\t\t20F0E5F82C33500900434C3A /* AirQuality.swift in Sources */,\n\t\t\t\t2000D4082AD86DAA0052EDA6 /* WindSpeedFormatter.swift in Sources */,\n\t\t\t\t2039B3FF2C28D1D3006A6B6D /* NonInteractiveMenuOptionView.swift in Sources */,\n\t\t\t\t20459C641C5C50DA004D0DC1 /* ConfigManager.swift in Sources */,\n\t\t\t\t2039B3F92C28D18A006A6B6D /* DatWeatherDoeApp.swift in Sources */,\n\t\t\t\t2039B3E32C289697006A6B6D /* TemperatureData.swift in Sources */,\n\t\t\t\t2039B3FD2C28D1C0006A6B6D /* MenuOptionsView.swift in Sources */,\n\t\t\t\t20B4681B2793A7E300FC6050 /* TemperatureTextBuilder.swift in Sources */,\n\t\t\t\t20BBCDAA278B8A18007DEEB0 /* WeatherViewModel.swift in Sources */,\n\t\t\t\t2039B3EF2C28996D006A6B6D /* WeatherCondition.swift in Sources */,\n\t\t\t\t204597682A84492400CF73CE /* TemperatureWithDegreesCreator.swift in Sources */,\n\t\t\t\tE3BE2C91292C505A00C4F468 /* DropdownIcon.swift in Sources */,\n\t\t\t\t202B1030278D632800ED6D42 /* APIKeyParser.swift in Sources */,\n\t\t\t\t209F8A4127915DBC00EB5C45 /* LocationCoordinatesWeatherRepository.swift in Sources */,\n\t\t\t\tE3105BA62818805A00FB4C55 /* SunriseAndSunsetTextBuilder.swift in Sources */,\n\t\t\t\t202B101E278D4F1900ED6D42 /* WeatherReachability.swift in Sources */,\n\t\t\t\t2039B3DF2C278AA4006A6B6D /* SunriseSunsetData.swift in Sources */,\n\t\t\t\t2039B4012C291B5A006A6B6D /* MenuView.swift in Sources */,\n\t\t\t\t20B46814279394FB00FC6050 /* WeatherTextBuilder.swift in Sources */,\n\t\t\t\t20CA6E11278F49AC00FFC53A /* TemperatureFormatter.swift in Sources */,\n\t\t\t\t2077BC52278DF98800E0453C /* WeatherConditionTextMapper.swift in Sources */,\n\t\t\t\t20BBCDAD278B8B28007DEEB0 /* WeatherViewModelType.swift in Sources */,\n\t\t\t\t20D8571F2A832AC6005727BB /* TemperatureUnit.swift in Sources */,\n\t\t\t\t20D857102A8317F5005727BB /* ConfigureUnitOptionsView.swift in Sources */,\n\t\t\t\t20C6722A2E9DEDCB00A577C1 /* Task+Retry.swift in Sources */,\n\t\t\t\t202B1029278D5A7100ED6D42 /* WeatherForecaster.swift in Sources */,\n\t\t\t\t206523C826597B120026C506 /* WeatherError.swift in Sources */,\n\t\t\t\t209F8A38279136D300EB5C45 /* LocationValidator.swift in Sources */,\n\t\t\t\t20D8571B2A831D62005727BB /* WeatherRepositoryType.swift in Sources */,\n\t\t\t\t2044E91E2867D3CF00AED55B /* TemperatureForecastTextBuilder.swift in Sources */,\n\t\t\t\t20D857142A831A16005727BB /* ConfigureValueSeparatorOptionsView.swift in Sources */,\n\t\t\t\t201C6C4620262E380065E795 /* WeatherAPIResponse.swift in Sources */,\n\t\t\t\t206E15252A7C4C5C0096D33C /* ConfigOptions.swift in Sources */,\n\t\t\t\t2039B3E72C2896BD006A6B6D /* WindData.swift in Sources */,\n\t\t\t\t20B857362CC3304C0098DB1D /* UVIndexTextBuilder.swift in Sources */,\n\t\t\t\t2005C059278CB5FC0067BBD1 /* WeatherURLBuilder.swift in Sources */,\n\t\t\t\t20D857122A831802005727BB /* ConfigureWeatherOptionsView.swift in Sources */,\n\t\t\t\t20D8571D2A831F40005727BB /* WeatherRepositoryFactory.swift in Sources */,\n\t\t\t\t20B468192793989B00FC6050 /* HumidityTextBuilder.swift in Sources */,\n\t\t\t\t206523D62659A92B0026C506 /* WeatherDataBuilder.swift in Sources */,\n\t\t\t\t206FF1BB2BB4BB9400111EAE /* WeatherConditionPosition.swift in Sources */,\n\t\t\t\t20206F0727BFF3D7004B418F /* ConfigureView.swift in Sources */,\n\t\t\t\t200520F62C29426E00006FC0 /* WeatherDataFormatter.swift in Sources */,\n\t\t\t\t2039B3EB2C2896D7006A6B6D /* ForecastTemperatureData.swift in Sources */,\n\t\t\t\t20F17D3A26597A02003A164E /* WeatherData.swift in Sources */,\n\t\t\t\t202B1015278D46AB00ED6D42 /* WeatherConditionBuilder.swift in Sources */,\n\t\t\t\t206E152F2A7C544D0096D33C /* ConfigureOptionsView.swift in Sources */,\n\t\t\t\t2039B3E92C2896CF006A6B6D /* ForecastData.swift in Sources */,\n\t\t\t\t209F8A3D27914A5900EB5C45 /* NetworkClient.swift in Sources */,\n\t\t\t\t2039B4042C291C35006A6B6D /* StatusBarView.swift in Sources */,\n\t\t\t\t206523FD265AF03E0026C506 /* RefreshInterval.swift in Sources */,\n\t\t\t\t206523FB265AD5730026C506 /* WeatherSource.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t61D04A2B1C7B7BCF00CBE6AE /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2074949927A09278002AA589 /* WeatherURLBuilderTests.swift in Sources */,\n\t\t\t\t20B9CDCF27B8335A00C42508 /* TemperatureUnitTests.swift in Sources */,\n\t\t\t\t20B3845F27A1CFE800F85482 /* LocationValidatorTests.swift in Sources */,\n\t\t\t\t20B9CDD127B833EE00C42508 /* RefreshIntervalTests.swift in Sources */,\n\t\t\t\t20B9CDCD27B8325900C42508 /* WeatherSourceTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t20CB68BE2A2D9033001C73B9 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tproductRef = 20CB68BD2A2D9033001C73B9 /* SwiftLintPlugin */;\n\t\t};\n\t\t61D04A351C7B7BCF00CBE6AE /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 2023EDA21C4ED09C0087FD67 /* DatWeatherDoe */;\n\t\t\ttargetProxy = 61D04A341C7B7BCF00CBE6AE /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin XCBuildConfiguration section */\n\t\t2023EDAE1C4ED09C0087FD67 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 20CB480B27B83EF90043C60F /* Config.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSTRING_CATALOG_GENERATE_SYMBOLS = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 6.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2023EDAF1C4ED09C0087FD67 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 20CB480B27B83EF90043C60F /* Config.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSTRING_CATALOG_GENERATE_SYMBOLS = YES;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tSWIFT_VERSION = 6.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t2023EDB11C4ED09C0087FD67 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = DatWeatherDoe/Resources/DatWeatherDoe.entitlements;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Mac Developer\";\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 57;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"DatWeatherDoe/Resources/DevelopmentAssets\\\" \\\"DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = Q8X4D3A8MT;\n\t\t\t\tENABLE_APP_SANDBOX = YES;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;\n\t\t\t\tENABLE_RESOURCE_ACCESS_LOCATION = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/DatWeatherDoe/Resources/Info.plist\";\n\t\t\t\tINFOPLIST_KEY_CFBundleDisplayName = DatWeatherDoe;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.weather\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 5.6.2;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DatWeatherDoe.debug;\n\t\t\t\tPRODUCT_NAME = DatWeatherDoe;\n\t\t\t\tPROVISIONING_PROFILE = \"\";\n\t\t\t\tSWIFT_APPROACHABLE_CONCURRENCY = YES;\n\t\t\t\tSWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;\n\t\t\t\tSWIFT_STRICT_CONCURRENCY = complete;\n\t\t\t\tSWIFT_SWIFT3_OBJC_INFERENCE = Default;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2023EDB21C4ED09C0087FD67 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = DatWeatherDoe/Resources/DatWeatherDoe.entitlements;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Mac Developer\";\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 57;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"DatWeatherDoe/Resources/DevelopmentAssets\\\" \\\"DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = Q8X4D3A8MT;\n\t\t\t\tENABLE_APP_SANDBOX = YES;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;\n\t\t\t\tENABLE_RESOURCE_ACCESS_LOCATION = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/DatWeatherDoe/Resources/Info.plist\";\n\t\t\t\tINFOPLIST_KEY_CFBundleDisplayName = DatWeatherDoe;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.weather\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 5.6.2;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DatWeatherDoe;\n\t\t\t\tPRODUCT_NAME = DatWeatherDoe;\n\t\t\t\tPROVISIONING_PROFILE = \"\";\n\t\t\t\tSWIFT_APPROACHABLE_CONCURRENCY = YES;\n\t\t\t\tSWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;\n\t\t\t\tSWIFT_STRICT_CONCURRENCY = complete;\n\t\t\t\tSWIFT_SWIFT3_OBJC_INFERENCE = Default;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t61D04A361C7B7BCF00CBE6AE /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_TEAM = Q8X4D3A8MT;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 14.0;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DefaultsTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/DatWeatherDoe.app/Contents/MacOS/DatWeatherDoe\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t61D04A371C7B7BCF00CBE6AE /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Distribution\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 14.0;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DefaultsTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/DatWeatherDoe.app/Contents/MacOS/DatWeatherDoe\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t2023ED9E1C4ED09C0087FD67 /* Build configuration list for PBXProject \"DatWeatherDoe\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2023EDAE1C4ED09C0087FD67 /* Debug */,\n\t\t\t\t2023EDAF1C4ED09C0087FD67 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t2023EDB01C4ED09C0087FD67 /* Build configuration list for PBXNativeTarget \"DatWeatherDoe\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2023EDB11C4ED09C0087FD67 /* Debug */,\n\t\t\t\t2023EDB21C4ED09C0087FD67 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t61D04A381C7B7BCF00CBE6AE /* Build configuration list for PBXNativeTarget \"DatWeatherDoeTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t61D04A361C7B7BCF00CBE6AE /* Debug */,\n\t\t\t\t61D04A371C7B7BCF00CBE6AE /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCRemoteSwiftPackageReference section */\n\t\t2000D40D2AD88D6B0052EDA6 /* XCRemoteSwiftPackageReference \"SwiftFormat\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/nicklockwood/SwiftFormat\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 0.60.0;\n\t\t\t};\n\t\t};\n\t\t20012FA4267980EE00553B60 /* XCRemoteSwiftPackageReference \"Reachability\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/ashleymills/Reachability.swift\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 5.2.4;\n\t\t\t};\n\t\t};\n\t\t2039B4052C2920BF006A6B6D /* XCRemoteSwiftPackageReference \"MenuBarExtraAccess\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/orchetect/MenuBarExtraAccess\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.3.0;\n\t\t\t};\n\t\t};\n\t\t20CB68BC2A2D9029001C73B9 /* XCRemoteSwiftPackageReference \"SwiftLint\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/realm/SwiftLint\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 0.63.2;\n\t\t\t};\n\t\t};\n/* End XCRemoteSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\t20012FA5267980EE00553B60 /* Reachability */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 20012FA4267980EE00553B60 /* XCRemoteSwiftPackageReference \"Reachability\" */;\n\t\t\tproductName = Reachability;\n\t\t};\n\t\t2039B4062C2920BF006A6B6D /* MenuBarExtraAccess */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 2039B4052C2920BF006A6B6D /* XCRemoteSwiftPackageReference \"MenuBarExtraAccess\" */;\n\t\t\tproductName = MenuBarExtraAccess;\n\t\t};\n\t\t20CB68BD2A2D9033001C73B9 /* SwiftLintPlugin */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 20CB68BC2A2D9029001C73B9 /* XCRemoteSwiftPackageReference \"SwiftLint\" */;\n\t\t\tproductName = \"plugin:SwiftLintPlugin\";\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = 2023ED9B1C4ED09C0087FD67 /* Project object */;\n}\n"
  },
  {
    "path": "DatWeatherDoe.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "DatWeatherDoe.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "DatWeatherDoe.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "content": "{\n  \"originHash\" : \"cf72def9a17d22a974c73e8b0f0a4d80bccc1e736e0ce0e89a85e671a37bb5b6\",\n  \"pins\" : [\n    {\n      \"identity\" : \"collectionconcurrencykit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/JohnSundell/CollectionConcurrencyKit.git\",\n      \"state\" : {\n        \"revision\" : \"b4f23e24b5a1bff301efc5e70871083ca029ff95\",\n        \"version\" : \"0.2.0\"\n      }\n    },\n    {\n      \"identity\" : \"cryptoswift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/krzyzanowskim/CryptoSwift.git\",\n      \"state\" : {\n        \"revision\" : \"e45a26384239e028ec87fbcc788f513b67e10d8f\",\n        \"version\" : \"1.9.0\"\n      }\n    },\n    {\n      \"identity\" : \"menubarextraaccess\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/orchetect/MenuBarExtraAccess\",\n      \"state\" : {\n        \"revision\" : \"33bb0e4b1e407feac791e047dcaaf9c69b25fd26\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"reachability.swift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/ashleymills/Reachability.swift\",\n      \"state\" : {\n        \"revision\" : \"21d1dc412cfecbe6e34f1f4c4eb88d3f912654a6\",\n        \"version\" : \"5.2.4\"\n      }\n    },\n    {\n      \"identity\" : \"sourcekitten\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/jpsim/SourceKitten.git\",\n      \"state\" : {\n        \"revision\" : \"731ffe6a35344a19bab00cdca1c952d5b4fee4d8\",\n        \"version\" : \"0.37.2\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser.git\",\n      \"state\" : {\n        \"revision\" : \"309a47b2b1d9b5e991f36961c983ecec72275be3\",\n        \"version\" : \"1.6.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-filename-matcher\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/ileitch/swift-filename-matcher\",\n      \"state\" : {\n        \"revision\" : \"eef5ac0b6b3cdc64b3039b037bed2def8a1edaeb\",\n        \"version\" : \"2.0.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-syntax\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swiftlang/swift-syntax.git\",\n      \"state\" : {\n        \"revision\" : \"65b02a90ad2cc213e09309faeb7f6909e0a8577a\",\n        \"version\" : \"604.0.0-prerelease-2026-01-20\"\n      }\n    },\n    {\n      \"identity\" : \"swiftformat\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/nicklockwood/SwiftFormat\",\n      \"state\" : {\n        \"revision\" : \"f098405c8e97cb2bde1779fdd3ac72880ff32fa3\",\n        \"version\" : \"0.60.0\"\n      }\n    },\n    {\n      \"identity\" : \"swiftlint\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/realm/SwiftLint\",\n      \"state\" : {\n        \"revision\" : \"88952528a590ed366c6f76f6bfb980b5ebdcefc1\",\n        \"version\" : \"0.63.2\"\n      }\n    },\n    {\n      \"identity\" : \"swiftytexttable\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/scottrhoyt/SwiftyTextTable.git\",\n      \"state\" : {\n        \"revision\" : \"c6df6cf533d120716bff38f8ff9885e1ce2a4ac3\",\n        \"version\" : \"0.9.0\"\n      }\n    },\n    {\n      \"identity\" : \"swxmlhash\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/drmohundro/SWXMLHash.git\",\n      \"state\" : {\n        \"revision\" : \"a853604c9e9a83ad9954c7e3d2a565273982471f\",\n        \"version\" : \"7.0.2\"\n      }\n    },\n    {\n      \"identity\" : \"yams\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/jpsim/Yams.git\",\n      \"state\" : {\n        \"revision\" : \"d41ba4e7164c0838c6d48351f7575f7f762151fe\",\n        \"version\" : \"6.1.0\"\n      }\n    }\n  ],\n  \"version\" : 3\n}\n"
  },
  {
    "path": "DatWeatherDoe.xcodeproj/xcshareddata/xcschemes/DatWeatherDoe.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"2600\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"2023EDA21C4ED09C0087FD67\"\n               BuildableName = \"DatWeatherDoe.app\"\n               BlueprintName = \"DatWeatherDoe\"\n               ReferencedContainer = \"container:DatWeatherDoe.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <TestPlans>\n         <TestPlanReference\n            reference = \"container:DatWeatherDoeTests/DatWeatherDoe.xctestplan\"\n            default = \"YES\">\n         </TestPlanReference>\n      </TestPlans>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"2023EDA21C4ED09C0087FD67\"\n            BuildableName = \"DatWeatherDoe.app\"\n            BlueprintName = \"DatWeatherDoe\"\n            ReferencedContainer = \"container:DatWeatherDoe.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"2023EDA21C4ED09C0087FD67\"\n            BuildableName = \"DatWeatherDoe.app\"\n            BlueprintName = \"DatWeatherDoe\"\n            ReferencedContainer = \"container:DatWeatherDoe.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "DatWeatherDoeTests/API/Repository/Location/Coordinates/LocationValidatorTests.swift",
    "content": "//\n//  LocationValidatorTests.swift\n//  DatWeatherDoeTests\n//\n//  Created by Inder Dhir on 1/26/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\n@testable import DatWeatherDoe\nimport Testing\n\nstruct LocationValidatorTests {\n    \n    @Test\n    func testLocation_empty() async throws {\n        #expect(throws: (any Error).self) {\n            try LocationValidator(latLong: \"\").validate()\n        }\n    }\n\n    @Test\n    func testLocation_correct() async throws {\n        #expect(throws: Never.self) {\n            try LocationValidator(latLong: \"12,24\").validate()\n        }\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoeTests/API/Repository/WeatherURLBuilderTests.swift",
    "content": "//\n//  WeatherURLBuilderTests.swift\n//  DatWeatherDoeTests\n//\n//  Created by Inder Dhir on 1/25/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\n@testable import DatWeatherDoe\nimport Foundation\nimport Testing\n\nstruct WeatherURLBuilderTests {\n    \n    @Test func testBuild() async throws {\n        let urlString = try WeatherURLBuilder(\n            appId: \"123456\",\n            location: .init(latitude: 42, longitude: 42)\n        ).build().absoluteString\n        #expect(urlString == \"https://api.weatherapi.com/v1/forecast.json?key=123456&aqi=yes&q=42.0,42.0&dt=\\(parsedDateToday)\")\n    }\n\n    private var parsedDateToday: String {\n        let dateFormatter = DateFormatter()\n        dateFormatter.dateFormat = \"yyyy-MM-dd\"\n        return dateFormatter.string(from: Date())\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoeTests/DatWeatherDoe.xctestplan",
    "content": "{\n  \"configurations\" : [\n    {\n      \"id\" : \"8E1BA5A7-5E49-4CF1-8D13-2F0DA4BF0476\",\n      \"name\" : \"Main Config\",\n      \"options\" : {\n\n      }\n    }\n  ],\n  \"defaultOptions\" : {\n    \"codeCoverage\" : false,\n    \"targetForVariableExpansion\" : {\n      \"containerPath\" : \"container:DatWeatherDoe.xcodeproj\",\n      \"identifier\" : \"2023EDA21C4ED09C0087FD67\",\n      \"name\" : \"DatWeatherDoe\"\n    },\n    \"testExecutionOrdering\" : \"random\"\n  },\n  \"testTargets\" : [\n    {\n      \"parallelizable\" : true,\n      \"target\" : {\n        \"containerPath\" : \"container:DatWeatherDoe.xcodeproj\",\n        \"identifier\" : \"61D04A2E1C7B7BCF00CBE6AE\",\n        \"name\" : \"DatWeatherDoeTests\"\n      }\n    }\n  ],\n  \"version\" : 1\n}\n"
  },
  {
    "path": "DatWeatherDoeTests/UI/Configure/Options/RefreshIntervalTests.swift",
    "content": "//\n//  RefreshIntervalTests.swift\n//  DatWeatherDoeTests\n//\n//  Created by Inder Dhir on 2/12/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\n@testable import DatWeatherDoe\nimport Testing\n\nstruct RefreshIntervalTests {\n    \n    @Test\n    func refreshIntervalTimes() {\n        #expect(RefreshInterval.fiveMinutes.rawValue == 300)\n        #expect(RefreshInterval.fifteenMinutes.rawValue == 900)\n        #expect(RefreshInterval.thirtyMinutes.rawValue == 1800)\n        #expect(RefreshInterval.sixtyMinutes.rawValue == 3600)\n    }\n\n    @Test\n    func refreshintervalStrings() {\n        #expect(RefreshInterval.fiveMinutes.title == \"5 min\")\n        #expect(RefreshInterval.fifteenMinutes.title == \"15 min\")\n        #expect(RefreshInterval.thirtyMinutes.title == \"30 min\")\n        #expect(RefreshInterval.sixtyMinutes.title == \"60 min\")\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoeTests/UI/Configure/Options/TemperatureUnitTests.swift",
    "content": "//\n//  TemperatureUnitTests.swift\n//  DatWeatherDoeTests\n//\n//  Created by Inder Dhir on 2/12/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\n@testable import DatWeatherDoe\nimport Testing\n\nstruct TemperatureUnitTests {\n    \n    @Test\n    func fahrenheit() {\n        let fahrenheitUnit = TemperatureUnit.fahrenheit\n\n        #expect(fahrenheitUnit.unitString == \"F\")\n        #expect(fahrenheitUnit.degreesString == \"\\u{00B0}F\")\n    }\n\n    @Test\n    func celsius() {\n        let fahrenheitUnit = TemperatureUnit.celsius\n\n        #expect(fahrenheitUnit.unitString == \"C\")\n        #expect(fahrenheitUnit.degreesString == \"\\u{00B0}C\")\n    }\n\n    @Test\n    func all() {\n        let fahrenheitUnit = TemperatureUnit.all\n\n        #expect(fahrenheitUnit.unitString == \"All\")\n        #expect(fahrenheitUnit.degreesString == \"\\u{00B0}All\")\n    }\n}\n"
  },
  {
    "path": "DatWeatherDoeTests/UI/Configure/Options/WeatherSourceTests.swift",
    "content": "//\n//  WeatherSourceTests.swift\n//  DatWeatherDoeTests\n//\n//  Created by Inder Dhir on 2/12/22.\n//  Copyright © 2022 Inder Dhir. All rights reserved.\n//\n\n@testable import DatWeatherDoe\nimport Testing\n\nstruct WeatherSourceTests {\n    \n    @Test\n    func locationSource() {\n        let locationSource = WeatherSource.location\n\n        #expect(locationSource.title == \"Location\")\n        #expect(locationSource.placeholder == \"\")\n        #expect(locationSource.textHint == \"\")\n    }\n\n    @Test\n    func latLongSource() {\n        let latLongSource = WeatherSource.latLong\n\n        #expect(latLongSource.title == \"Lat/Long\")\n        #expect(latLongSource.placeholder == \"42,42\")\n        #expect(latLongSource.textHint == \"[latitude],[longitude]\")\n    }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License,\nVersion 2.0 Apache License Version 2.0,\nJanuary 2004 http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright 2016 Inder Dhir\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# [<img src=\"logo.png\" width=\"20\"/>](image.png) DatWeatherDoe\n\n> **Note**\nOpenWeatherMap API 2.5 support is ending in June 2024. The app uses WeatherAPI going forward with location support.\n\n- Fetch weather using:\n  - Location services\n  - Latitude / Longitude\n- Configurable polling interval\n- Dark mode support\n- Supports MacOS 13.0+\n\n## Screenshots\n\n![Screenshot 1](screenshot_1.png)\\\n![Screenshot 2](screenshot_2.png)\n\n## Installation\n\n### Homebrew Cask\n\n`brew install --cask datweatherdoe`\n\n### Manual\n\n<https://github.com/inderdhir/DatWeatherDoe/releases/latest>\n\n## Using Location Services\n\nIf using location, please make sure that the app has permission to access location services on macOS.\n\n`System Preferences > Security & Privacy > Privacy > Location Services`\n\n![Location services screenshot 1](location_services_1.png)\n![Location services screenshot 2](location_services_2.png)\n\n## Developer Setup\n\n- Get your personal API key for WeatherAPI [here](https://www.weatherapi.com)\n- Add the following in \"Config.xcconfig\":\n\n```env\nWEATHER_API_KEY=YOUR_KEY\n```\n\n## Donate\n\nBuy me a coffee to support the development of this project.\n\n[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Y8Y211O253)\n\n## Contributing\n\nPlease see CONTRIBUTING.md\n"
  }
]