Skip to content

Commit 7d4855c

Browse files
authored
fix: removes callbacks and fixes segment and sonarqube issues (#9)
* fix: removes callbacks from the ios sdk and other bug fixes * fix: removes recaptcha type as its not longer needed * fix: sonar issues * fix: sonar issues
1 parent bf0aef4 commit 7d4855c

File tree

19 files changed

+375
-57
lines changed

19 files changed

+375
-57
lines changed

FormbricksSDK.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "FormbricksSDK"
3-
s.version = "1.0.1"
3+
s.version = "1.1.0"
44
s.summary = "iOS SDK for Formbricks"
55
s.homepage = "https://github.com/formbricks/ios"
66
s.license = { :type => "MIT", :file => "LICENSE" }

Sources/FormbricksSDK/Extension/Error+Message.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Foundation
22

3-
extension Error {
3+
public extension Error {
44
var message: String {
55
if let error = self as? RuntimeError {
66
return error.message

Sources/FormbricksSDK/Formbricks.swift

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,15 @@ import Network
4141
*/
4242
@objc public static func setup(with config: FormbricksConfig, force: Bool = false) {
4343
logger = Logger()
44+
apiQueue = OperationQueue()
4445

45-
if (force == true) {
46+
if force {
4647
isInitialized = false
4748
}
49+
4850
guard !isInitialized else {
49-
Formbricks.logger?.error(FormbricksSDKError(type: .sdkIsAlreadyInitialized).message)
51+
let error = FormbricksSDKError(type: .sdkIsAlreadyInitialized)
52+
Formbricks.logger?.error(error.message)
5053
return
5154
}
5255

@@ -87,7 +90,8 @@ import Network
8790
*/
8891
@objc public static func setUserId(_ userId: String) {
8992
guard Formbricks.isInitialized else {
90-
Formbricks.logger?.error(FormbricksSDKError(type: .sdkIsNotInitialized).message)
93+
let error = FormbricksSDKError(type: .sdkIsNotInitialized)
94+
Formbricks.logger?.error(error.message)
9195
return
9296
}
9397

@@ -110,7 +114,8 @@ import Network
110114
*/
111115
@objc public static func setAttribute(_ attribute: String, forKey key: String) {
112116
guard Formbricks.isInitialized else {
113-
Formbricks.logger?.error(FormbricksSDKError(type: .sdkIsNotInitialized).message)
117+
let error = FormbricksSDKError(type: .sdkIsNotInitialized)
118+
Formbricks.logger?.error(error.message)
114119
return
115120
}
116121

@@ -128,7 +133,8 @@ import Network
128133
*/
129134
@objc public static func setAttributes(_ attributes: [String : String]) {
130135
guard Formbricks.isInitialized else {
131-
Formbricks.logger?.error(FormbricksSDKError(type: .sdkIsNotInitialized).message)
136+
let error = FormbricksSDKError(type: .sdkIsNotInitialized)
137+
Formbricks.logger?.error(error.message)
132138
return
133139
}
134140

@@ -146,7 +152,8 @@ import Network
146152
*/
147153
@objc public static func setLanguage(_ language: String) {
148154
guard Formbricks.isInitialized else {
149-
Formbricks.logger?.error(FormbricksSDKError(type: .sdkIsNotInitialized).message)
155+
let error = FormbricksSDKError(type: .sdkIsNotInitialized)
156+
Formbricks.logger?.error(error.message)
150157
return
151158
}
152159

@@ -169,7 +176,8 @@ import Network
169176
*/
170177
@objc public static func track(_ action: String) {
171178
guard Formbricks.isInitialized else {
172-
Formbricks.logger?.error(FormbricksSDKError(type: .sdkIsNotInitialized).message)
179+
let error = FormbricksSDKError(type: .sdkIsNotInitialized)
180+
Formbricks.logger?.error(error.message)
173181
return
174182
}
175183

@@ -194,7 +202,8 @@ import Network
194202
*/
195203
@objc public static func logout() {
196204
guard Formbricks.isInitialized else {
197-
Formbricks.logger?.error(FormbricksSDKError(type: .sdkIsNotInitialized).message)
205+
let error = FormbricksSDKError(type: .sdkIsNotInitialized)
206+
Formbricks.logger?.error(error.message)
198207
return
199208
}
200209

@@ -235,6 +244,7 @@ import Network
235244
}
236245

237246
private static func performCleanup() {
247+
userManager?.logout()
238248
userManager?.cleanupUpdateQueue()
239249
presentSurveyManager?.dismissView()
240250
presentSurveyManager = nil

Sources/FormbricksSDK/Helpers/AnyCodable/AnyCodable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,4 @@ extension AnyCodable: Hashable {
144144
break
145145
}
146146
}
147-
}
147+
}

Sources/FormbricksSDK/Helpers/AnyCodable/AnyDecodable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,4 @@ extension AnyDecodable: Hashable {
186186
break
187187
}
188188
}
189-
}
189+
}

Sources/FormbricksSDK/Helpers/AnyCodable/AnyEncodable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,4 @@ extension AnyEncodable: Hashable {
289289
break
290290
}
291291
}
292-
}
292+
}
Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11
import Foundation
22

3-
class FormbricksEnvironment {
4-
public static let baseApiUrl: String = Formbricks.appUrl ?? "http://localhost:3000"
5-
public static let surveyScriptUrl: String = "\(baseApiUrl)/js/surveys.umd.cjs"
6-
/// Endpoint for getting environment data. Replace {environmentId} with the actual environment ID.
7-
public static let getEnvironmentRequestEndpoint: String = "/api/v2/client/{environmentId}/environment"
8-
/// Endpoint for posting user data. Replace {environmentId} with the actual environment ID.
9-
public static let postUserRequestEndpoint: String = "/api/v2/client/{environmentId}/user"
3+
internal enum FormbricksEnvironment {
4+
5+
/// Only `appUrl` is user-supplied. Crash early if it’s missing.
6+
fileprivate static var baseApiUrl: String {
7+
guard let url = Formbricks.appUrl else {
8+
fatalError("Formbricks.setup must be called before using the SDK.")
9+
}
10+
return url
11+
}
12+
13+
/// Returns the full survey‐script URL as a String
14+
static var surveyScriptUrlString: String {
15+
let path = "/" + ["js", "surveys.umd.cjs"].joined(separator: "/") // NOSONAR we can hard-code "/" here
16+
return baseApiUrl + path
17+
}
18+
19+
/// Returns the full environment‐fetch URL as a String for the given ID
20+
static var getEnvironmentRequestEndpoint: String {
21+
let path = "/" + ["api", "v2", "client", "{environmentId}", "environment"] // NOSONAR we can hard-code "/" here
22+
.joined(separator: "/")
23+
return path
24+
}
25+
26+
/// Returns the full post-user URL as a String for the given ID
27+
static var postUserRequestEndpoint: String {
28+
let path = "/" + ["api", "v2", "client", "{environmentId}", "user"] // NOSONAR we can hard-code "/" here
29+
.joined(separator: "/")
30+
return path
31+
}
1032
}

Sources/FormbricksSDK/Manager/SurveyManager.swift

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,19 @@ final class SurveyManager {
6262

6363
// Display percentage
6464
let shouldDisplay = shouldDisplayBasedOnPercentage(firstSurveyWithActionClass?.displayPercentage)
65-
65+
let isMultiLangSurvey = firstSurveyWithActionClass?.languages?.count ?? 0 > 1
66+
67+
if isMultiLangSurvey {
68+
guard let survey = firstSurveyWithActionClass else {return}
69+
let currentLanguage = Formbricks.language
70+
guard let languageCode = getLanguageCode(survey: survey, language: currentLanguage) else {
71+
Formbricks.logger?.error("Survey \(survey.name) is not available in language “\(currentLanguage)”. Skipping.")
72+
return
73+
}
74+
75+
Formbricks.language = languageCode
76+
}
77+
6678
// Display and delay it if needed
6779
if let surveyId = firstSurveyWithActionClass?.id, shouldDisplay {
6880
isShowingSurvey = true
@@ -93,7 +105,8 @@ extension SurveyManager {
93105
self?.filterSurveys()
94106
case .failure:
95107
self?.hasApiError = true
96-
Formbricks.logger?.error(FormbricksSDKError(type: .unableToRefreshEnvironment).message)
108+
let error = FormbricksSDKError(type: .unableToRefreshEnvironment)
109+
Formbricks.logger?.error(error.message)
97110
self?.startErrorTimer()
98111
}
99112
}
@@ -171,7 +184,8 @@ extension SurveyManager {
171184
if let data = UserDefaults.standard.data(forKey: SurveyManager.environmentResponseObjectKey) {
172185
return try? JSONDecoder().decode(EnvironmentResponse.self, from: data)
173186
} else {
174-
Formbricks.logger?.error(FormbricksSDKError(type: .unableToRetrieveEnvironment).message)
187+
let error = FormbricksSDKError(type: .unableToRetrieveEnvironment)
188+
Formbricks.logger?.error(error.message)
175189
return nil
176190
}
177191
}
@@ -180,7 +194,8 @@ extension SurveyManager {
180194
UserDefaults.standard.set(data, forKey: SurveyManager.environmentResponseObjectKey)
181195
backingEnvironmentResponse = newValue
182196
} else {
183-
Formbricks.logger?.error(FormbricksSDKError(type: .unableToPersistEnvironment).message)
197+
let error = FormbricksSDKError(type: .unableToPersistEnvironment)
198+
Formbricks.logger?.error(error.message)
184199
}
185200
}
186201
}
@@ -212,7 +227,8 @@ private extension SurveyManager {
212227
}
213228

214229
default:
215-
Formbricks.logger?.error(FormbricksSDKError(type: .invalidDisplayOption).message)
230+
let error = FormbricksSDKError(type: .invalidDisplayOption)
231+
Formbricks.logger?.error(error.message)
216232
return false
217233
}
218234

@@ -234,6 +250,47 @@ private extension SurveyManager {
234250
}
235251
}
236252

253+
func getLanguageCode(
254+
survey: Survey,
255+
language: String?
256+
) -> String? {
257+
// 1) Collect all codes
258+
let availableLanguageCodes = survey.languages?
259+
.map { $0.language.code }
260+
261+
// 2) If no language was passed or it's the explicit "default" token → default
262+
guard let raw = language?.lowercased(), !raw.isEmpty else {
263+
return "default"
264+
}
265+
266+
if raw == "default" {
267+
return "default"
268+
}
269+
270+
// 3) Find matching entry by code or alias
271+
let selected = survey.languages?.first { entry in
272+
entry.language.code.lowercased() == raw ||
273+
entry.language.alias?.lowercased() == raw
274+
}
275+
276+
// 4) If that entry is marked default → default
277+
if selected?.isDefault == true {
278+
return "default"
279+
}
280+
281+
// 5) If no entry, or not enabled, or code not in the available list → nil
282+
guard
283+
let entry = selected,
284+
entry.enabled,
285+
availableLanguageCodes?.contains(entry.language.code) == true
286+
else {
287+
return nil
288+
}
289+
290+
// 6) Otherwise return its code
291+
return entry.language.code
292+
}
293+
237294
/// Filters the surveys based on the user's segments.
238295
func filterSurveysBasedOnSegments(_ surveys: [Survey], segments: [String]) -> [Survey] {
239296
return surveys.filter { survey in

Sources/FormbricksSDK/Manager/UserManager.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ final class UserManager: UserManagerSyncable {
132132
backingLastDisplayedAt = nil
133133
backingExpiresAt = nil
134134
Formbricks.language = "default"
135-
updateQueue?.reset()
135+
136+
syncTimer?.invalidate()
137+
syncTimer = nil
138+
updateQueue?.cleanup()
136139

137140
if isUserIdDefined {
138141
Formbricks.logger?.debug("Successfully logged out user and reset the user state.")

Sources/FormbricksSDK/Model/Environment/EnvironmentData.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ struct EnvironmentData: Codable {
22
let surveys: [Survey]?
33
let actionClasses: [ActionClass]?
44
let project: Project
5+
let recaptchaSiteKey: String?
56
}

0 commit comments

Comments
 (0)