Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/FormbricksSDK/Extension/Calendar+DaysBetween.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ extension Calendar {
let numberOfDays = dateComponents([.day], from: fromDate, to: toDate)

guard let day = numberOfDays.day else { return 0 }
return abs(day + 1)
return max(0, day)
}
}
8 changes: 6 additions & 2 deletions Sources/FormbricksSDK/Manager/PresentSurveyManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ final class PresentSurveyManager {
private weak var viewController: UIViewController?

/// Present the webview
func present(environmentResponse: EnvironmentResponse, id: String) {
func present(environmentResponse: EnvironmentResponse, id: String, completion: ((Bool) -> Void)? = nil) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
if let window = UIApplication.safeKeyWindow {
Expand All @@ -25,7 +25,11 @@ final class PresentSurveyManager {
presentationController.detents = [.large()]
}
self.viewController = vc
window.rootViewController?.present(vc, animated: true, completion: nil)
window.rootViewController?.present(vc, animated: true, completion: {
completion?(true)
})
} else {
completion?(false)
}
}
}
Expand Down
42 changes: 33 additions & 9 deletions Sources/FormbricksSDK/Manager/SurveyManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,21 @@ final class SurveyManager {

let actionClasses = environmentResponse?.data.data.actionClasses ?? []
let codeActionClasses = actionClasses.filter { $0.type == "code" }
let actionClass = codeActionClasses.first { $0.key == action }
guard let actionClass = codeActionClasses.first(where: { $0.key == action }) else {
Formbricks.logger?.error("\(action) action unknown. Please add this action in Formbricks first in order to use it in your code.")
return
}

let firstSurveyWithActionClass = filteredSurveys.first { survey in
return survey.triggers?.contains(where: { $0.actionClass?.name == actionClass?.name }) ?? false
return survey.triggers?.contains(where: { $0.actionClass?.name == actionClass.name }) ?? false
}

// Display percentage
let shouldDisplay = shouldDisplayBasedOnPercentage(firstSurveyWithActionClass?.displayPercentage)
if let survey = firstSurveyWithActionClass, !shouldDisplay {
Formbricks.logger?.info("Skipping survey \(survey.name) due to display percentage restriction.")
return
}
let isMultiLangSurvey = firstSurveyWithActionClass?.languages?.count ?? 0 > 1

if isMultiLangSurvey {
Expand All @@ -103,12 +111,25 @@ final class SurveyManager {
}

// Display and delay it if needed
if let surveyId = firstSurveyWithActionClass?.id, shouldDisplay {
if let survey = firstSurveyWithActionClass, shouldDisplay {
isShowingSurvey = true
let timeout = firstSurveyWithActionClass?.delay ?? 0
let timeout = survey.delay ?? 0
if timeout > 0 {
Formbricks.logger?.info("Delaying survey \(survey.name) by \(timeout) seconds")
}
DispatchQueue.global().asyncAfter(deadline: .now() + Double(timeout)) { [weak self] in
self?.showSurvey(withId: surveyId)
completion?()
guard let self = self else { return }
if let environmentResponse = self.environmentResponse {
self.presentSurveyManager.present(environmentResponse: environmentResponse, id: survey.id) { success in
if !success {
self.isShowingSurvey = false
}
completion?()
}
} else {
self.isShowingSurvey = false
completion?()
}
}
}
}
Expand Down Expand Up @@ -199,8 +220,9 @@ private extension SurveyManager {
/// Decides if the survey should be displayed based on the display percentage.
internal func shouldDisplayBasedOnPercentage(_ displayPercentage: Double?) -> Bool {
guard let displayPercentage = displayPercentage else { return true }
let randomNum = Double(Int.random(in: 0..<10000)) / 100.0
return randomNum <= displayPercentage
let clampedPercentage = min(max(displayPercentage, 0), 100)
let draw = Double.random(in: 0..<100)
return draw < clampedPercentage
}
}

Expand Down Expand Up @@ -273,7 +295,9 @@ extension SurveyManager {
let recontactDays = survey.recontactDays ?? defaultRecontactDays

if let recontactDays = recontactDays {
return Calendar.current.numberOfDaysBetween(Date(), and: lastDisplayedAt) >= recontactDays
let secondsElapsed = Date().timeIntervalSince(lastDisplayedAt)
let daysBetween = Int(secondsElapsed / 86_400)
return daysBetween >= recontactDays
}

return true
Expand Down
19 changes: 19 additions & 0 deletions Sources/FormbricksSDK/Model/Environment/Survey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ struct LanguageDetail: Codable {
let projectId: String
}

// MARK: - New types for projectOverwrites

enum Placement: String, Codable {
case topLeft = "topLeft"
case topRight = "topRight"
case bottomLeft = "bottomLeft"
case bottomRight = "bottomRight"
case center = "center"
}

struct ProjectOverwrites: Codable {
let brandColor: String?
let highlightBorderColor: String?
let placement: Placement?
let clickOutsideClose: Bool?
let darkOverlay: Bool?
}

struct Survey: Codable {
let id: String
let name: String
Expand All @@ -37,4 +55,5 @@ struct Survey: Codable {
let segment: Segment?
let styling: Styling?
let languages: [SurveyLanguage]?
let projectOverwrites: ProjectOverwrites?
}
20 changes: 15 additions & 5 deletions Sources/FormbricksSDK/WebView/FormbricksViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ private extension FormbricksViewModel {
onClose,
onOpenExternalURL,
};

window.formbricksSurveys.renderSurvey(surveyProps);
}

Expand All @@ -92,23 +91,34 @@ private class WebViewData {
var data: [String: Any] = [:]

init(environmentResponse: EnvironmentResponse, surveyId: String) {
let matchedSurvey = environmentResponse.data.data.surveys?.first(where: {$0.id == surveyId})
let project = environmentResponse.data.data.project

data["survey"] = environmentResponse.getSurveyJson(forSurveyId: surveyId)
data["appUrl"] = Formbricks.appUrl
data["environmentId"] = Formbricks.environmentId
data["contactId"] = Formbricks.userManager?.contactId
data["isWebEnvironment"] = false
data["isBrandingEnabled"] = environmentResponse.data.data.project.inAppSurveyBranding ?? true
data["isBrandingEnabled"] = project.inAppSurveyBranding ?? true

if let placementEnum = matchedSurvey?.projectOverwrites?.placement {
data["placement"] = placementEnum.rawValue
} else {
data["placement"] = project.placement
}

data["darkOverlay"] = matchedSurvey?.projectOverwrites?.darkOverlay ?? project.darkOverlay

let isMultiLangSurvey = environmentResponse.data.data.surveys?.first(where: { $0.id == surveyId })?.languages?.count ?? 0 > 1
let isMultiLangSurvey = (matchedSurvey?.languages?.count ?? 0) > 1

if isMultiLangSurvey {
data["languageCode"] = Formbricks.language
} else {
data["languageCode"] = "default"
}

let hasCustomStyling = environmentResponse.data.data.surveys?.first(where: { $0.id == surveyId })?.styling != nil
let enabled = environmentResponse.data.data.project.styling?.allowStyleOverwrite ?? false
let hasCustomStyling = matchedSurvey?.styling != nil
let enabled = project.styling?.allowStyleOverwrite ?? false

data["styling"] = hasCustomStyling && enabled ? environmentResponse.getSurveyStylingJson(forSurveyId: surveyId): environmentResponse.getProjectStylingJson()
}
Expand Down
Loading