Skip to content

Mobile-130: Add operationsDomain#701

Merged
justSmK merged 15 commits intodevelopfrom
mobile-130
Apr 29, 2026
Merged

Mobile-130: Add operationsDomain#701
justSmK merged 15 commits intodevelopfrom
mobile-130

Conversation

@justSmK
Copy link
Copy Markdown
Collaborator

@justSmK justSmK commented Apr 28, 2026

justSmK added 10 commits April 28, 2026 14:01
Adds optional `operationsDomain` to `MBConfiguration` so projects served
via a CDP anonymizer can route operations and track-visit requests to
a separate host, avoiding personal-data leaks to `api.mindbox.ru`.

Affected routes (now resolve to `operationsDomain` when configured):
  - `/v3/operations/{async,sync,async-custom}`
  - `/v1.1/customer/mobile-track-visit`
  - SDK logs upload
Config-fetch (`/mobile/byendpoint/*.json`) and `/geo` keep using `domain`.

Priority of resolution:
  1. `settings.baseAddresses.operations` from the mobile JSON config
  2. `operationsDomain` passed to `Mindbox.initialization(...)`
  3. `nil` → fall back to `MBConfiguration.domain` (existing behavior)

The JSON-sourced value is persisted in `UserDefaults` so operations stay
pinned to the anonymizer across restarts. An explicit `null` / empty
value in a fresh config clears the cache (rollback channel); a
format-broken value preserves the previous good one.

Backwards compatibility:
  - `operationsDomain` has a default value of `nil`
  - `softReset()` preserves `operationsDomainFromConfig` (PD safety on
    migration resets)
  - Legacy `MBConfiguration` JSON without the new key decodes fine

Tests cover `URLRequestBuilder` host resolution, `MBConfiguration`
validation, `Settings` JSON decoding (incl. `null`/empty rollback),
`ConfigValidation` behavior, `MBNetworkFetcher` priority resolution,
`OperationsDomainConfigPolicy` decision matrix, persistence lifecycle,
and legacy-config decoding.

Follow-ups (separate commits):
  - Drop dead `SDKLogsRoute`
  - Rewrite `URLValidator` (hardcoded TLD list, `&` bug)
  - Allow optional `https://` scheme in `domain` / `operationsDomain` inputs
Extends `SettingsConfigParsingTests` with `baseAddresses` cases from
the Pkl error stubs (`Error`, `TypeError`, `OperationsError`,
`OperationsTypeError`) and a positive assertion in
`test_SettingsConfig_shouldParseSuccessfully`. Updates JSON fixtures
under `SettingsJsonStubs/` to carry the new `baseAddresses` block
(sourced from `pkl-mobile-config`); adds `BaseAddressesError/` with
four fixtures and wires them into the test bundle via
`Mindbox.xcodeproj/project.pbxproj`.

Switches `Settings.BaseAddresses` to a custom `init(from:)` with
per-field `try?` so a type error on `operations` nils only that
field — matching `FeatureToggles`/`SlidingExpiration`/`InAppSettings`.

Drops two parsing tests from `OperationsURLRoutingTests` whose
coverage is now duplicated (positive parse, missing `baseAddresses`).
Rollback-channel cases (`null` and empty string) stay there — they
exercise feature behavior, not schema parsing.
Adds `MindboxTests/Configuration/MBConfigurationTests.swift` (Swift
Testing, `.mbConfiguration` tag) — a single suite covering the full
behavior of `MBConfiguration`: programmatic init validation
(`domain`, `endpoint`, `operationsDomain`, UUID handling for
`previousInstallationId`/`previousDeviceUUID`), defaults, plist init,
Codable (legacy / new / decoder validation), and
`ConfigValidation.compare` across the whole input space (identity,
`nil` handling, `.rest`-affecting fields, `.shouldCreateCustomer`
priority, fields that must not trigger any change).

Consolidates from two sources:
  - Plist init coverage from `MBConfigurationTestCase.swift` (XCTest,
    deleted) — folded into a single parametrised section.
  - Seven `MBConfiguration`-shaped tests previously living in
    `OperationsURLRoutingTests.swift` (init validation, two
    `ConfigValidation.compare` cases, legacy JSON decoding).

Adds `Tag.mbConfiguration` and registers the new file under a new
`Configuration/` group in `Mindbox.xcodeproj/project.pbxproj`.
`SDKLogsRoute` had no references in production code: SDK logs flow
through `SDKLogsManager.sendLogs` → `EventRepository.send` →
`EventRoute.asyncEvent` (`MBEventRepository.makeRoute` for
`.sdkLogs`).

Removes the type from `SDKLogsRequest.swift`. `SDKLogsRequest` (the
Codable body) stays — it is the request payload encoded into
`Event.body`.

Updates `OperationsURLRoutingTests` to assert routing for
`.sdkLogs` through the actual `EventRoute.asyncEvent` path. The
dedicated `sdkLogsRouteUsesOperationsDomain` test is removed
(subsumed by the extended `eventRoutesUseOperationsDomain` and
`noOperationsDomainFallsBackToDomain` cases).
`MBConfiguration.domain` and `MBConfiguration.operationsDomain` now
accept `host`, `https://host`, and `http://host` (with or without
trailing slash). When a scheme is present in the input, requests are
built with that scheme; otherwise `https://` is used as before.
Mirrors the Android SDK's `SdkValidation.extractHost` / `toBaseUrl`
pair, so both platforms accept the same inputs (e.g. a value pasted
straight from the dashboard URL).

Adds `Mindbox/Network/Helpers/HostNormalizer.swift`:
  - `extractHost` — strips scheme (case-insensitive), whitespace, and
    trailing slashes.
  - `toBaseURLString` — preserves an existing scheme or prepends
    `https://`.
  - `isValidHost` — runs the extracted host through `URLValidator`.
    Encapsulates the awkward `URL(string: "https://" + host)` +
    `URLValidator` pattern previously duplicated across three call
    sites.

`URLRequestBuilder` builds the base URL via
`HostNormalizer.toBaseURLString(...)` before adding `path` / `queryItems`,
so the resolved scheme propagates to every route. Same value also
flows through `OperationsDomainConfigPolicy` when validating the
operations host coming from the JSON config.

Tests:
  - `HostNormalizerTests` (Swift Testing) — extract / base-URL /
    validation across scheme, case, trailing slash, whitespace.
  - `MBConfigurationTests` — accepts `domain` / `operationsDomain`
    with `https://`, `http://`, trailing slash. Replaces the previous
    "scheme throws" invariant.
  - `OperationsURLRoutingTests` — bare host → https default,
    `https://` / `http://` preserved end-to-end, trailing slash
    stripped before path append.

Backwards compatible: integrators who pass a bare host get exactly
the same URLs as before.
Replaces the old full-URL regex (hardcoded TLD list missing
`.app`/`.dev`/`.io`, `&` HTML-escape bug, per-call regex compile
via `try!`) with an RFC 1123-style host validator. New TLDs are
accepted automatically by structure — analogous to Android's
`PatternsCompat.DOMAIN_NAME`.

API: `URLValidator.isValidHost(_ host: String) -> Bool` (static, no
instance). Drops the previous `URLValidator(url:).evaluate()` shape;
callers (`MBConfiguration`, `OperationsDomainConfigPolicy`) are
updated.

Implementation is plain Swift (no regex, no `try!`):
  - Each label: 1..63 chars, alnum + hyphen, hyphen not at edges.
  - Hosts joined by `.`; single-label (`localhost`) and IPv4 literals
    accepted (digits-only labels are valid alnum).
  - Total length capped at 253 (RFC 1035), constants explicit.
  - ASCII-only contract: punycode (`xn--…`) passes, Unicode literals
    don't (callers must convert IDN to ACE).

Removes the thin `HostNormalizer.isValidHost` wrapper — it was a
trivial composition of `extractHost` + validation that didn't belong
in a normalizer. Call sites now spell out the chain explicitly:
`URLValidator.isValidHost(HostNormalizer.extractHost(value))`.

Tests:
  - New `URLValidatorTests` (Swift Testing) with 22 cases covering
    passing inputs (modern TLDs, localhost, IPv4, hyphenated and
    single-char labels, mixed case, punycode, max label/host
    length boundaries) and failing inputs (empty, whitespace,
    underscore, edge-hyphen, empty labels, embedded scheme,
    path/query, special characters, Unicode literals, length
    overflow).
  - Removed `testURLValidator` from `ValidatorsTestCase` — the
    full-URL contract no longer applies.
  - Removed redundant `isValidHost` tests from `HostNormalizerTests`
    (covered by `URLValidatorTests` now).
…clusion

Extract `applyDownloadedConfig(_:rawData:)` and `sendMonitoringLogsIfNeeded(_:)`
so the .data branch reads as decode → assign → apply.

Group the three `SessionTemporaryStorage` writes inside `setupSettingsFromConfig`
into `applySessionStorageSettings(_:)` so the dispatcher reads as four steps.

Move the rationale for excluding `operationsDomainFromConfig` from `softReset()`
to the property's docstring, where it belongs.
The new parameter now sits right after `domain` instead of at the end —
they're conceptually paired (main host vs its anonymizer override).
Source-compatible: callers that don't pass `operationsDomain` are
unaffected; the few that do (tests, ios-app) already listed it adjacent
to `domain` and only need a follow-up reorder of their own.
…me://host form

Backend may send `http://` for some anonymizer setups; previous logic stored
the raw string verbatim, so trailing slashes / scheme-vs-host mismatches caused
spurious re-saves. Normalize via HostNormalizer.toBaseURLString on save: scheme
preserved (http/https), trailing slash stripped, missing scheme defaults to
https. Idempotent — first config fetch after upgrade rewrites legacy values
once, then `.keep` stably.

Adapts existing Policy tests to the canonical form and adds coverage for the
trailing-slash case from JSON config, http preservation, canonical-equality
keep, and the legacy-value upgrade path.
… and routing tests

Routing tests had three scheme/slash cases for `domain` (bare host, https
preserved, http preserved, trailing-slash stripped) but only `https://` for
`operationsDomain`. Same asymmetry in MBConfiguration init tests. Add the
missing http/trailing-slash cases for `operationsDomain` in both, plus an
end-to-end check that the canonical `scheme://host` form written by
OperationsDomainConfigPolicy routes correctly through URLRequestBuilder.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for an operationsDomain that can be sourced from the mobile JSON config (settings.baseAddresses.operations) and used to route “operations” traffic to a separate host while keeping non-operations routes on the main domain.

Changes:

  • Introduces operationsDomain on MBConfiguration, persists a config-sourced operations host, and resolves routing priority (config JSON > init > fallback to domain).
  • Adds Route.baseURLKind + URLRequestBuilder host resolution to route EventRoute via the operations host when present.
  • Replaces URL validation with a host-only validator + adds host normalization utilities; adds/updates tests and JSON stubs for parsing coverage.

Reviewed changes

Copilot reviewed 55 out of 59 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
MindboxTests/Validators/ValidatorsTestCase.swift Removes the old URL validator XCTest coverage (keeps UDID tests).
MindboxTests/Validators/URLValidatorTests.swift Adds Swift Testing suite for the new host-only URLValidator behavior.
MindboxTests/Network/OperationsURLRoutingTests.swift Adds end-to-end routing, policy, and persistence lifecycle tests for operationsDomain.
MindboxTests/Network/HostNormalizerTests.swift Adds tests for scheme/trailing-slash/whitespace normalization helpers.
MindboxTests/Mock/MockPersistenceStorage.swift Adds operationsDomainFromConfig to the persistence storage mock.
MindboxTests/MBConfigurationTestCase.swift Deletes legacy XCTest-based MBConfiguration test case.
MindboxTests/InApp/Tests/InAppConfigResponseTests/InappTTLTests.swift Updates Settings construction to include the new baseAddresses field.
MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppConfigStub.swift Updates Settings stub to include baseAddresses: nil.
MindboxTests/Extensions/Tag+Extensions.swift Adds new Testing tags used by new suites.
MindboxTests/Configuration/MBConfigurationTests.swift Adds Swift Testing suite for MBConfiguration init/decoding/validation behaviors incl. operationsDomain.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/TtlErrors/SettingsTtlTypeError.json Adds baseAddresses.operations to TTL error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/TtlErrors/SettingsTtlInappsTypeError.json Adds baseAddresses.operations to TTL error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/TtlErrors/SettingsTtlInappsError.json Adds baseAddresses.operations to TTL error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/TtlErrors/SettingsTtlError.json Adds baseAddresses.operations to TTL error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/SlidingExpirationsError/SettingsSlidingExpirationTypeError.json Adds baseAddresses.operations to sliding expiration error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/SlidingExpirationsError/SettingsSlidingExpirationPushTokenKeepaliveTypeError.json Adds baseAddresses.operations to sliding expiration error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/SlidingExpirationsError/SettingsSlidingExpirationPushTokenKeepaliveError.json Adds baseAddresses.operations to sliding expiration error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/SlidingExpirationsError/SettingsSlidingExpirationError.json Adds baseAddresses.operations to sliding expiration error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/SlidingExpirationsError/SettingsSlidingExpirationConfigTypeError.json Adds baseAddresses.operations to sliding expiration error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/SlidingExpirationsError/SettingsSlidingExpirationConfigError.json Adds baseAddresses.operations to sliding expiration error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/SettingsConfig.json Adds baseAddresses.operations to the “happy path” Settings config stub.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewProductTypeError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewProductSystemNameTypeError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewProductSystemNameError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewProductError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewCategoryAndSetCartTypeError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewCategoryAndSetCartSystemNameTypeError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewCategoryAndSetCartSystemNameMixedError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewCategoryAndSetCartSystemNameError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsViewCategoryAndSetCartError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsTypeError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsOperationsError.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsAllOperationsWithTypeErrors.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/OperationsErrors/SettingsAllOperationsWithErrors.json Adds baseAddresses.operations to operations error stub to match new schema.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/FeatureTogglesError/SettingsFeatureTogglesTypeError.json Re-formats JSON (indentation) without behavior change.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/FeatureTogglesError/SettingsFeatureTogglesShouldSendInAppShowErrorTypeError.json Re-formats JSON (indentation) without behavior change.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/FeatureTogglesError/SettingsFeatureTogglesShouldSendInAppShowErrorMissing.json Re-formats JSON (indentation) without behavior change.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/FeatureTogglesError/SettingsFeatureTogglesShouldSendInAppShowErrorFalse.json Re-formats JSON (indentation) without behavior change.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/FeatureTogglesError/SettingsFeatureTogglesError.json Re-formats JSON (indentation) without behavior change.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/BaseAddressesError/SettingsBaseAddressesTypeError.json Adds new stub for baseAddresses top-level type error.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/BaseAddressesError/SettingsBaseAddressesOperationsTypeError.json Adds new stub for baseAddresses.operations type error.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/BaseAddressesError/SettingsBaseAddressesOperationsError.json Adds new stub for missing/incorrect operations key under baseAddresses.
MindboxTests/ConfigParsing/stubs/Settings/SettingsJsonStubs/BaseAddressesError/SettingsBaseAddressesError.json Adds new stub for missing/incorrect baseAddresses key.
MindboxTests/ConfigParsing/Settings/SettingsConfigParsingTests.swift Extends parsing tests to validate baseAddresses.operations and new error stubs.
Mindbox/Validators/URLValidator.swift Replaces URL regex validator with host-only RFC-style label validation.
Mindbox/PersistenceStorage/PersistenceStorage.swift Adds operationsDomainFromConfig and ensures hard reset clears it (softReset preserves it).
Mindbox/PersistenceStorage/MBPersistenceStorage.swift Persists operationsDomainFromConfig via UserDefaults wrapper and key.
Mindbox/NetworkRepository/Event/EventRoute.swift Marks event routes as .operations base URL kind.
Mindbox/Network/MBNetworkFetcher.swift Resolves operations domain (config JSON > init) and passes it to URLRequestBuilder.
Mindbox/Network/Helpers/URLRequestBuilder.swift Adds host resolution + base URL parsing to support scheme/host swapping.
Mindbox/Network/Helpers/HostNormalizer.swift Adds helpers to extract host and build canonical base URL strings.
Mindbox/Network/Abstract/Route.swift Adds RouteBaseURL + baseURLKind with .domain default.
Mindbox/MindboxLogger/SDKLogsRequest.swift Removes unused SDKLogsRoute (logs routed via EventRoute flow).
Mindbox/MBConfiguration.swift Adds operationsDomain, validates it, and wires decoding through init validation.
Mindbox/InAppMessages/Models/Config/SettingsModel.swift Adds decoding support for settings.baseAddresses.
Mindbox/InAppMessages/Models/Config/BaseAddressesModel.swift Introduces DTO for settings.baseAddresses.operations.
Mindbox/InAppMessages/Configuration/Services/OperationsDomainConfigPolicy.swift Adds unit-testable policy for save/clear/keep decisions + canonicalization.
Mindbox/InAppMessages/Configuration/InAppConfigurationManager.swift Persists operationsDomainFromConfig based on decoded config (policy-driven).
Mindbox.xcodeproj/project.pbxproj Wires new Swift files/tests/stubs into the Xcode project and removes deleted test file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Mindbox/MBConfiguration.swift Outdated
Comment thread Mindbox/InAppMessages/Configuration/InAppConfigurationManager.swift Outdated
…ject path

- MBConfiguration decoder: drop the `try?` on `decodeIfPresent` so the result
  is `String?` not `String??`. Type errors propagate, matching how `endpoint`,
  `domain`, and the bool fields are decoded.
- OperationsDomainConfigPolicy: split `.keep` into `.keep` and `.rejected(String)`.
  Previously `.keep` collapsed both "already in canonical form" and
  "invalid format — fall back". After the canonicalization fix this caused
  spurious "Invalid domain" error logs whenever a legacy raw value (e.g.
  `x.ru`) matched a stored canonical form (`https://x.ru`).
- InAppConfigurationManager: handle the new `.rejected` case explicitly,
  drop the brittle `current != raw` condition that motivated the bug.
- Regression test covers the legacy-vs-canonical match no longer being
  mis-logged as rejected.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 55 out of 59 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Mindbox/Validators/URLValidator.swift Outdated
Comment thread Mindbox/Network/Helpers/URLRequestBuilder.swift
…uild safety

- URLValidator: enforce IPv4 octet ranges (0..255) for four-pure-digit-label
  inputs, matching Android's PatternsCompat.DOMAIN_NAME. `999.999.999.999`
  and `256.0.0.0` are now rejected; structural hostname rules unchanged.
- URLRequestBuilder: stop silently falling back to an empty URLComponents
  when `URLComponents(string:)` fails — that produced relative URLs that
  passed the existing `components.url != nil` guard and hit the network
  with a bogus target. Throw `URLError.badURL` at the source.
- Tests cover IPv4 overflow rejection, hostname pass-through for non-IPv4
  numeric counts (3 / 5 labels), and the new fail-fast path.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

justSmK added 2 commits April 29, 2026 13:20
`JSONSerialization.data(withJSONObject:options:)` was using `.prettyPrinted`
for the trackVisit request body — wasted whitespace and indentation on every
mobile-track-visit call. Server parses both forms identically; the change
just trims a few dozen bytes per request and matches the codebase convention
for network-path serialization. Default `options` value (`[]`) is fine, so
the parameter is dropped entirely.
…lizer

Replace `value.lowercased().hasPrefix(...)` with
`value.range(of:options:[.caseInsensitive, .anchored])` so the scheme check
no longer allocates a lowercased copy of the input on every call. Identical
behaviour, cleaner intent (prefix-with-options vs. transform-then-check),
and the prefix literals are pulled out as `static let` so `dropFirst(...)`
no longer carries hard-coded length math. Adds a tiny `hasSchemePrefix`
helper for `toBaseURLString` to avoid duplicating both range checks inline.
Copilot AI review requested due to automatic review settings April 29, 2026 10:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@justSmK justSmK merged commit 1612179 into develop Apr 29, 2026
11 of 12 checks passed
@justSmK justSmK deleted the mobile-130 branch April 29, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants