Skip to content

feat: adds workspace support#44

Merged
pandeymangg merged 4 commits into
epic/v5from
feat/workspace-support
Apr 30, 2026
Merged

feat: adds workspace support#44
pandeymangg merged 4 commits into
epic/v5from
feat/workspace-support

Conversation

@pandeymangg
Copy link
Copy Markdown
Contributor

Adds workspace support to the ios sdk and also makes sure that environmentId still works (backwards compat)

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Walkthrough

The PR refactors the SDK to replace "environment" terminology with "workspace" terminology throughout. It changes environmentId to workspaceId as the primary identifier while maintaining backward compatibility through deprecated aliases and computed properties. API endpoints, models, request/response types, and service methods are updated to use workspace naming and identifiers. Legacy code paths are preserved via fallback decoding logic, migration helpers, and dual-notification posting. Tests are migrated to the new terminology with additional coverage for backward-compatibility scenarios and cache migration.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: adds workspace support' clearly and concisely summarizes the primary change of introducing workspace support to the iOS SDK.
Description check ✅ Passed The description accurately reflects the changeset by stating that workspace support is being added while maintaining backward compatibility with environmentId.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/FormbricksSDK/Formbricks.swift (1)

61-80: ⚠️ Potential issue | 🟡 Minor

Avoid leaving workspace state after failed setup validation.

Line 62 assigns workspaceId before appUrl validation. If setup aborts for an invalid/non-HTTPS URL, the SDK remains uninitialized but still exposes stale appUrl, workspaceId, logger, and apiQueue state. Move identifier assignment after validation, or clean up before returning.

Proposed fix
-        self.appUrl = config.appUrl
-        self.workspaceId = config.workspaceId
         self.logger?.logLevel = config.logLevel
 
 
         if config.usedDeprecatedEnvironmentId {
             Formbricks.logger?.debug("environmentId is deprecated and will be removed in a future version. Please use workspaceId instead.")
@@
        guard url.scheme?.lowercased() == "https" else {
            let errorMessage = "HTTP requests are blocked for security. Only HTTPS URLs are allowed. Provided app url: \(config.appUrl). SDK setup aborted."
            Formbricks.logger?.error(errorMessage)
            return
        }
+
+        self.appUrl = config.appUrl
+        self.workspaceId = config.workspaceId
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/FormbricksSDK/Formbricks.swift` around lines 61 - 80, The setup
assigns self.appUrl, self.workspaceId and mutates self.logger/apiQueue before
validating the provided URL, leaving partial/invalid SDK state if validation
fails; move the assignments of appUrl and workspaceId (and any logger/apiQueue
initialization tied to config) to after the URL validation and HTTPS check in
the initializer (or, alternatively, on validation failure revert/clear those
properties), ensuring any early return from the initializer does not leave stale
state; update references to Formbricks.logger, self.appUrl, self.workspaceId and
apiQueue initialization points to be executed only after URL validation
succeeds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/FormbricksSDK/Helpers/ConfigBuilder.swift`:
- Around line 5-17: The workspaceId property is internal so external Swift/Obj-C
consumers can't read it; make it a public (and `@objc` for Objective-C consumers)
property so callers can access the canonical value while keeping the deprecated
environmentId alias. Change the declaration of workspaceId from "let
workspaceId: String" to "@objc public let workspaceId: String" (or "public let
workspaceId: String" if Obj-C bridging isn't required), leaving the `@available`
deprecated environmentId computed property and usedDeprecatedEnvironmentId
behavior intact.

In `@Sources/FormbricksSDK/Manager/SurveyManager.swift`:
- Around line 147-153: The early return in refreshWorkspaceIfNeeded prevents
startRefreshTimer from being scheduled when a persisted, non-expired
workspaceResponse is used; update refreshWorkspaceIfNeeded to call
startRefreshTimer() (after calling filterSurveys()) before returning for the
cached valid path (i.e., inside the if that checks
workspaceResponse.data.expiresAt.timeIntervalSinceNow > 0 && !force) so the
refresh timer is scheduled to run when that cached response later expires.
- Around line 250-262: The getter in SurveyManager currently returns nil if data
for workspaceResponseObjectKey exists but fails to decode and it migrates legacy
data before validating it; change the flow so you first try decoding the new key
and if that succeeds return it, but if decoding the new key fails then attempt
to decode legacyEnvironmentResponseObjectKey and only if that legacy blob
successfully decodes to WorkspaceResponse migrate it (defaults.set(..., forKey:
SurveyManager.workspaceResponseObjectKey) and defaults.removeObject(forKey:
SurveyManager.legacyEnvironmentResponseObjectKey)) and return it; also ensure
you do not overwrite the new key with an invalid legacy blob and only remove the
legacy key after a successful decode/migration.

In `@Sources/FormbricksSDK/Model/Workspace/Surveys/Segment.swift`:
- Line 199: Remove the redundant explicit raw value on the enum case by changing
`case isPrivate = "isPrivate"` to the implicit form `case isPrivate` so it
matches the synthesized raw value used by the other cases; update the enum (the
CodingKeys/Coding enum around the `case isPrivate = "isPrivate"`) to fold this
into the implicit-cases section above.

In `@Sources/FormbricksSDK/Networking/Base/APIClient.swift`:
- Around line 80-84: Replace the forced cast "body = workspace as!
Request.Response" with a safe conditional cast and fallback: after decoding into
var body and checking "if var workspace = body as? WorkspaceResponse", set
workspace.responseString and then attempt "if let castBody = workspace as?
Request.Response { body = castBody }" else leave the original body unchanged (or
handle error); this removes the force-cast on WorkspaceResponse ->
Request.Response while preserving existing behavior in the APIClient decoding
block.

In `@Tests/FormbricksSDKTests/FormbricksSDKTests.swift`:
- Around line 222-226: Tests mutate global UserDefaults.standard by writing
corrupt/migrated data under SurveyManager.workspaceResponseObjectKey and
SurveyManager.legacyEnvironmentResponseObjectKey and do not reliably clean them
up, causing cross-test leakage; update the failing test (and other tests that
touch these keys) to remove those keys before and after the test (or switch to
an isolated UserDefaults via UserDefaults(suiteName:) for the test) and ensure
Formbricks.cleanup() does not rely on persisted workspace cache—specifically add
removal calls for SurveyManager.workspaceResponseObjectKey and
SurveyManager.legacyEnvironmentResponseObjectKey in the test's setup/tearDown or
replace usages of UserDefaults.standard with an isolated suite so
workspaceResponse is deterministic across tests.

---

Outside diff comments:
In `@Sources/FormbricksSDK/Formbricks.swift`:
- Around line 61-80: The setup assigns self.appUrl, self.workspaceId and mutates
self.logger/apiQueue before validating the provided URL, leaving partial/invalid
SDK state if validation fails; move the assignments of appUrl and workspaceId
(and any logger/apiQueue initialization tied to config) to after the URL
validation and HTTPS check in the initializer (or, alternatively, on validation
failure revert/clear those properties), ensuring any early return from the
initializer does not leave stale state; update references to Formbricks.logger,
self.appUrl, self.workspaceId and apiQueue initialization points to be executed
only after URL validation succeeds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bec6e3fa-5148-4378-99e0-2a3611c295d5

📥 Commits

Reviewing files that changed from the base of the PR and between 52a3175 and 0e9eb11.

📒 Files selected for processing (30)
  • Sources/FormbricksSDK/Formbricks.swift
  • Sources/FormbricksSDK/Helpers/ConfigBuilder.swift
  • Sources/FormbricksSDK/Helpers/FormbricksWorkspace.swift
  • Sources/FormbricksSDK/Manager/PresentSurveyManager.swift
  • Sources/FormbricksSDK/Manager/SurveyManager.swift
  • Sources/FormbricksSDK/Model/Environment/EnvironmentData.swift
  • Sources/FormbricksSDK/Model/Environment/EnvironmentResponseData.swift
  • Sources/FormbricksSDK/Model/Workspace/ActionClass/ActionClass.swift
  • Sources/FormbricksSDK/Model/Workspace/Common/LocalizedText.swift
  • Sources/FormbricksSDK/Model/Workspace/Settings/BrandColor.swift
  • Sources/FormbricksSDK/Model/Workspace/Settings/Settings.swift
  • Sources/FormbricksSDK/Model/Workspace/Settings/Styling.swift
  • Sources/FormbricksSDK/Model/Workspace/Survey.swift
  • Sources/FormbricksSDK/Model/Workspace/Surveys/ActionClassReference.swift
  • Sources/FormbricksSDK/Model/Workspace/Surveys/Segment.swift
  • Sources/FormbricksSDK/Model/Workspace/Surveys/Trigger.swift
  • Sources/FormbricksSDK/Model/Workspace/WorkspaceData.swift
  • Sources/FormbricksSDK/Model/Workspace/WorkspaceResponse.swift
  • Sources/FormbricksSDK/Model/Workspace/WorkspaceResponseData.swift
  • Sources/FormbricksSDK/Networking/Base/APIClient.swift
  • Sources/FormbricksSDK/Networking/ClientAPI/Endpoints/Environment/GetEnvironmentRequest.swift
  • Sources/FormbricksSDK/Networking/ClientAPI/Endpoints/User/PostUserRequest.swift
  • Sources/FormbricksSDK/Networking/ClientAPI/Endpoints/Workspace/GetWorkspaceRequest.swift
  • Sources/FormbricksSDK/Networking/Service/FormbricksService.swift
  • Sources/FormbricksSDK/WebView/FormbricksViewModel.swift
  • Tests/FormbricksSDKTests/FormbricksSDKTests.swift
  • Tests/FormbricksSDKTests/FormbricksWorkspaceTests.swift
  • Tests/FormbricksSDKTests/MockFormbricksService/MockFormbricksService.swift
  • Tests/FormbricksSDKTests/Networking/APIClientTests.swift
  • Tests/FormbricksSDKTests/Networking/ClientAPIEndpointsTests.swift
💤 Files with no reviewable changes (3)
  • Sources/FormbricksSDK/Model/Environment/EnvironmentResponseData.swift
  • Sources/FormbricksSDK/Networking/ClientAPI/Endpoints/Environment/GetEnvironmentRequest.swift
  • Sources/FormbricksSDK/Model/Environment/EnvironmentData.swift

Comment thread Sources/FormbricksSDK/Helpers/ConfigBuilder.swift
Comment thread Sources/FormbricksSDK/Manager/SurveyManager.swift
Comment thread Sources/FormbricksSDK/Manager/SurveyManager.swift
Comment thread Sources/FormbricksSDK/Model/Workspace/Surveys/Segment.swift
Comment thread Sources/FormbricksSDK/Networking/Base/APIClient.swift
Comment thread Tests/FormbricksSDKTests/FormbricksSDKTests.swift
@pandeymangg pandeymangg requested a review from Dhruwang April 23, 2026 13:12
@pandeymangg pandeymangg changed the base branch from main to epic/v5 April 24, 2026 06:24
pandeymangg and others added 3 commits April 24, 2026 15:28
- Getter: fall back to legacy key when new-key blob fails to decode, and
  only migrate/delete legacy after it decodes as WorkspaceResponse so a
  corrupt blob can't poison the new key.
- refreshWorkspaceIfNeeded: schedule startRefreshTimer on the cached-valid
  path so refresh still fires when the cached response later expires.
- FormbricksConfig.workspaceId: expose as @objc public to match the
  deprecated environmentId alias visibility.
- Tests: clear persisted workspace cache keys in setUp/tearDown to avoid
  cross-test leakage (Formbricks.cleanup() intentionally leaves them).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
macos-15 runners no longer ship the iPhone SE (3rd generation) iOS 18.5
simulator runtime, so the pinned destination fails with "no available
devices matched the request". Switch to iPhone 16 with OS=latest so the
runner picks whatever simulator runtime is installed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous run on macos-15 reported zero available iOS Simulator devices
(only placeholders), meaning xcode-select was pointing at an Xcode without
any installed simulator runtimes. Use maxim-lobanov/setup-xcode to pin to
the latest stable Xcode (which ships with simulators), and print the sim
device list so future failures are self-diagnosing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

@pandeymangg pandeymangg merged commit 138dd6c into epic/v5 Apr 30, 2026
4 of 5 checks passed
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.

2 participants