surveysparrow-ionic-plugin 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Package.swift CHANGED
@@ -10,7 +10,6 @@ let package = Package(
10
10
  targets: ["SurveySparrowIonicPluginPlugin"])
11
11
  ],
12
12
  dependencies: [
13
- .package(url: "https://github.com/surveysparrow/surveysparrow-ios-sdk.git", exact: "1.0.6"),
14
13
  .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0")
15
14
  ],
16
15
  targets: [
@@ -18,8 +17,7 @@ let package = Package(
18
17
  name: "SurveySparrowIonicPluginPlugin",
19
18
  dependencies: [
20
19
  .product(name: "Capacitor", package: "capacitor-swift-pm"),
21
- .product(name: "Cordova", package: "capacitor-swift-pm"),
22
- .product(name: "SurveySparrowSdk", package: "surveysparrow-ios-sdk")
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
23
21
  ],
24
22
  path: "ios/Sources/SurveySparrowIonicPluginPlugin"),
25
23
  .testTarget(
@@ -0,0 +1,16 @@
1
+ //
2
+ // SsSurveyDelegate.swift
3
+ // SurveySparrowSdk
4
+ //
5
+ // Created by Gokulkrishna raju on 09/02/24.
6
+ // Copyright © 2020 SurveySparrow. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+
11
+ public protocol SsSurveyDelegate {
12
+ func handleSurveyResponse(response: [String: AnyObject])
13
+ func handleSurveyLoaded(response: [String: AnyObject])
14
+ func handleSurveyValidation(response: [String: AnyObject])
15
+ func handleCloseButtonTap()
16
+ }
@@ -0,0 +1,179 @@
1
+ //
2
+ // SsSurveyHelper.swift
3
+ // SurveySparrowSdk
4
+ //
5
+ // Created by Gokulkrishna raju on 09/02/24.
6
+ // Copyright © 2020 SurveySparrow. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+
11
+ func validateSurvey(
12
+ domain: String? = nil,
13
+ token: String? = nil,
14
+ params: [String: String]? = [:],
15
+ group: DispatchGroup,
16
+ completion: @escaping ([String: Any]) -> Void
17
+ ) {
18
+ struct User: Codable {
19
+ let email: String
20
+ }
21
+ struct ValidationResponse: Codable {
22
+ let active: Bool
23
+ let reason: String
24
+ let widgetContactId: Int64?
25
+ }
26
+ var email: String = ""
27
+ var active: Bool = false
28
+ if let unwrappedParams = params {
29
+ for (key, value) in unwrappedParams {
30
+ if key == "emailaddress" {
31
+ email = value
32
+ break
33
+ }
34
+ }
35
+ }
36
+ DispatchQueue.global().async {
37
+ let parameters = User(email: email)
38
+ var urlComponent = URLComponents()
39
+ urlComponent.scheme = "https"
40
+ urlComponent.host = domain!.trimmingCharacters(in: CharacterSet.whitespaces)
41
+ urlComponent.path =
42
+ "/sdk/validate-survey/\(token!.trimmingCharacters(in: CharacterSet.whitespaces))"
43
+ var validationUrl: URLRequest
44
+
45
+ if let url = urlComponent.url {
46
+ validationUrl = URLRequest(url: url)
47
+ makeHTTPRequest(urlString: validationUrl.description, parameters: parameters, method: "POST")
48
+ { result in
49
+ defer {
50
+ group.leave()
51
+ }
52
+
53
+ switch result {
54
+ case .success(let data):
55
+ if let data = data, let stringResponse = String(data: data, encoding: .utf8) {
56
+ guard let jsonData = stringResponse.data(using: .utf8) else {
57
+ print("Failed to convert string to data")
58
+ return
59
+ }
60
+ do {
61
+ let responseData = try JSONDecoder().decode(ValidationResponse.self, from: jsonData)
62
+ active = responseData.active
63
+ let result = [
64
+ "active": responseData.active,
65
+ "reason": responseData.reason,
66
+ "widgetContactId": responseData.widgetContactId ?? 0,
67
+ ]
68
+ completion(result)
69
+ return
70
+ } catch {
71
+ print("Error decoding JSON: \(error)")
72
+ }
73
+ return
74
+ }
75
+ case .failure(let error):
76
+ print("Error: \(error)")
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ func closeSurvey(
84
+ domain: String? = nil, widgetContactId: Int64? = nil,
85
+ params: [String: String]? = [:],
86
+ group: DispatchGroup,
87
+ completion: @escaping ([String: Any]) -> Void
88
+ ) {
89
+ struct ThrottleData: Codable {
90
+ let throttledOn: String
91
+ }
92
+ struct ThrottelingResponse: Codable {
93
+ let id: Int64
94
+ let contact_id: Int64
95
+ }
96
+ DispatchQueue.global().async {
97
+ let dateFormatter = DateFormatter()
98
+ dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxx"
99
+ dateFormatter.timeZone = TimeZone(secondsFromGMT: 19800) // Adjust timezone offset as needed
100
+ let currentDateString = dateFormatter.string(from: Date())
101
+ let parameters = ThrottleData(throttledOn: currentDateString)
102
+ var urlComponent = URLComponents()
103
+ urlComponent.scheme = "https"
104
+ urlComponent.host = domain!.trimmingCharacters(in: CharacterSet.whitespaces)
105
+ if let unwrappedId = widgetContactId, unwrappedId != 0 {
106
+ urlComponent.path = "/nps/widget/contact/\(unwrappedId)"
107
+ } else {
108
+ let result = [
109
+ "success": true
110
+ ]
111
+ completion(result)
112
+ return
113
+ }
114
+ var validationUrl: URLRequest
115
+
116
+ if let url = urlComponent.url {
117
+ validationUrl = URLRequest(url: url)
118
+ makeHTTPRequest(urlString: validationUrl.description, parameters: parameters, method: "PUT") {
119
+ result in
120
+ defer {
121
+ group.leave()
122
+ }
123
+ switch result {
124
+ case .success(let data):
125
+ if let data = data, let stringResponse = String(data: data, encoding: .utf8) {
126
+ guard let jsonData = stringResponse.data(using: .utf8) else {
127
+ print("Failed to convert string to data")
128
+ return
129
+ }
130
+ do {
131
+ _ = try JSONDecoder().decode(ThrottelingResponse.self, from: jsonData)
132
+ let result = [
133
+ "success": true
134
+ ]
135
+ completion(result)
136
+ return
137
+ } catch {
138
+ print("Error decoding JSON: \(error)")
139
+ }
140
+ return
141
+ }
142
+ case .failure(let error):
143
+ print("Error: \(error)")
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+
150
+ func makeHTTPRequest(
151
+ urlString: String, parameters: Encodable, method: String,
152
+ completion: @escaping (Result<Data?, Error>) -> Void
153
+ ) {
154
+ // Create the URL
155
+ guard let url = URL(string: urlString) else {
156
+ completion(
157
+ .failure(
158
+ NSError(domain: urlString, code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
159
+ return
160
+ }
161
+ var request = URLRequest(url: url)
162
+ request.httpMethod = method
163
+ do {
164
+ let jsonData = try JSONEncoder().encode(parameters)
165
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
166
+ request.httpBody = jsonData
167
+ } catch {
168
+ completion(.failure(error))
169
+ return
170
+ }
171
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
172
+ if let error = error {
173
+ completion(.failure(error))
174
+ return
175
+ }
176
+ completion(.success(data))
177
+ }
178
+ task.resume()
179
+ }
@@ -0,0 +1,314 @@
1
+ //
2
+ // SsSurveyView.swift
3
+ // SurveySparrowSdk
4
+ //
5
+ // Created by Gokulkrishna raju on 09/02/24.
6
+ // Copyright © 2020 SurveySparrow. All rights reserved.
7
+ //
8
+
9
+ #if canImport(UIKit)
10
+ import UIKit
11
+ import WebKit
12
+
13
+ @available(iOS 13.0, *)
14
+ @IBDesignable public class SsSurveyView: UIView, WKScriptMessageHandler, WKNavigationDelegate {
15
+
16
+ // MARK: Properties
17
+ private var ssWebView: WKWebView = WKWebView()
18
+ private let surveyResponseHandler = WKUserContentController()
19
+ private let loader: UIActivityIndicatorView = UIActivityIndicatorView()
20
+ private var surveyLoaded: String = "surveyLoadStarted"
21
+ private var surveyCompleted: String = "surveyCompleted"
22
+ private static var _widgetContactId: Int64?
23
+
24
+ public static var widgetContactId: Int64? {
25
+ get {
26
+ return _widgetContactId
27
+ }
28
+ set {
29
+ _widgetContactId = newValue
30
+ }
31
+ }
32
+ public var params: [String: String] = [:]
33
+ public var surveyType: SurveySparrow.SurveyType = .CLASSIC
34
+ public var getSurveyLoadedResponse: Bool = false
35
+
36
+ @IBInspectable public var domain: String?
37
+ @IBInspectable public var token: String?
38
+
39
+ public var surveyDelegate: SsSurveyDelegate!
40
+
41
+ var properties: [String: Any]?
42
+
43
+ // MARK: Initialization
44
+ public init(properties: [String: Any]) {
45
+ super.init(frame: .zero)
46
+ self.properties = properties
47
+ addFeedbackView()
48
+ }
49
+
50
+ required init?(coder: NSCoder) {
51
+ super.init(coder: coder)
52
+ addFeedbackView()
53
+ }
54
+ var closeButton = UIButton(type: .system)
55
+ // MARK: Private methods
56
+ private func addFeedbackView() {
57
+ let config = WKWebViewConfiguration()
58
+ config.preferences.javaScriptEnabled = true
59
+ config.userContentController = surveyResponseHandler
60
+ ssWebView = WKWebView(frame: bounds, configuration: config)
61
+ surveyResponseHandler.add(self, name: "surveyResponse")
62
+ ssWebView.navigationDelegate = self
63
+ ssWebView.backgroundColor = .gray
64
+ ssWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
65
+ addSubview(ssWebView)
66
+
67
+ let isCloseButtonEnabled = properties?["isCloseButtonEnabled"] as? Bool
68
+
69
+ if isCloseButtonEnabled ?? true == true {
70
+
71
+ let closeButtonWrapper = UIView()
72
+ ssWebView.addSubview(closeButtonWrapper)
73
+
74
+ closeButton.setImage(UIImage(systemName: "xmark"), for: .normal)
75
+ closeButton.tintColor = .black
76
+ closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside)
77
+
78
+ closeButtonWrapper.addSubview(closeButton)
79
+ closeButtonWrapper.translatesAutoresizingMaskIntoConstraints = false
80
+ closeButtonWrapper.backgroundColor = .white
81
+ closeButtonWrapper.layer.cornerRadius = 4
82
+ closeButtonWrapper.clipsToBounds = true
83
+
84
+ NSLayoutConstraint.activate([
85
+
86
+ closeButtonWrapper.topAnchor.constraint(equalTo: ssWebView.safeAreaLayoutGuide.topAnchor, constant: 16),
87
+ closeButtonWrapper.trailingAnchor.constraint(equalTo: ssWebView.safeAreaLayoutGuide.trailingAnchor, constant: -16),
88
+ closeButtonWrapper.widthAnchor.constraint(equalToConstant: 35),
89
+ closeButtonWrapper.heightAnchor.constraint(equalToConstant: 35),
90
+
91
+ closeButton.centerXAnchor.constraint(equalTo: closeButtonWrapper.centerXAnchor),
92
+ closeButton.centerYAnchor.constraint(equalTo: closeButtonWrapper.centerYAnchor),
93
+ closeButton.widthAnchor.constraint(equalToConstant: 14),
94
+ closeButton.heightAnchor.constraint(equalToConstant: 14)
95
+ ])
96
+ }
97
+ ssWebView.addSubview(loader)
98
+ ssWebView.navigationDelegate = self
99
+ loader.translatesAutoresizingMaskIntoConstraints = false
100
+ loader.centerXAnchor.constraint(equalTo: ssWebView.centerXAnchor).isActive = true
101
+ loader.centerYAnchor.constraint(equalTo: ssWebView.centerYAnchor).isActive = true
102
+ loader.hidesWhenStopped = true
103
+ }
104
+
105
+ @objc func closeButtonTapped() {
106
+ var isSuccess = false
107
+ // Check if widgetContactId is valid and not 0
108
+ if let unwrappedId = SsSurveyView.widgetContactId, unwrappedId != 0 {
109
+ let group = DispatchGroup()
110
+ group.enter()
111
+ let completion: ([String: Any]) -> Void = { result in
112
+ if let success = result["success"] as? Bool {
113
+ isSuccess = success
114
+ }
115
+ }
116
+ closeSurvey(
117
+ domain: domain, widgetContactId: unwrappedId, params: params, group: group,
118
+ completion: completion)
119
+
120
+ group.wait()
121
+ }
122
+ // Close the survey
123
+ closeSurveyUI(isSuccess: isSuccess)
124
+
125
+ if surveyDelegate != nil {
126
+ surveyDelegate.handleCloseButtonTap()
127
+ }
128
+ }
129
+
130
+ func closeSurveyUI(isSuccess: Bool) {
131
+ let emptyHTML = "<html><body></body></html>"
132
+ ssWebView.loadHTMLString(emptyHTML, baseURL: nil)
133
+ closeButton.isHidden = true
134
+
135
+ if let parentViewController = findParentViewController() {
136
+ parentViewController.dismiss(animated: true, completion: nil)
137
+ }
138
+ }
139
+
140
+ private func findParentViewController() -> UIViewController? {
141
+ var responder: UIResponder? = self
142
+ while let currentResponder = responder {
143
+ if let viewController = currentResponder as? UIViewController {
144
+ return viewController
145
+ }
146
+ responder = currentResponder.next
147
+ }
148
+ return nil
149
+ }
150
+
151
+ public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
152
+ if navigationAction.navigationType == .linkActivated {
153
+ if let url = navigationAction.request.url {
154
+ UIApplication.shared.open(url)
155
+ decisionHandler(.cancel)
156
+ return
157
+ }
158
+ }
159
+ decisionHandler(.allow)
160
+ }
161
+
162
+ public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error){
163
+ print("Failed to load web page: \(error.localizedDescription)")
164
+ }
165
+
166
+ public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
167
+ closeButton.translatesAutoresizingMaskIntoConstraints = false
168
+ loader.stopAnimating()
169
+
170
+ let isCloseButtonEnabled = properties?["isCloseButtonEnabled"] as? Bool
171
+
172
+ if isCloseButtonEnabled ?? true == true {
173
+ let jsCode = """
174
+ const styleTag = document.createElement("style");
175
+ styleTag.innerHTML = `.ss-language-selector--wrapper { margin-right: 45px; }`;
176
+ document.body.appendChild(styleTag);
177
+ """
178
+
179
+ webView.evaluateJavaScript(jsCode, completionHandler: { (result, error) in
180
+ if let error = error {
181
+ // print(error)
182
+ }
183
+ })
184
+ }
185
+ }
186
+
187
+ public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
188
+ loader.stopAnimating()
189
+ }
190
+
191
+ public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
192
+ if surveyDelegate != nil {
193
+ let response = message.body as! [String: AnyObject]
194
+ let responseType = response["type"] as! String
195
+ if(responseType == surveyLoaded){
196
+ if surveyDelegate != nil {
197
+ surveyDelegate.handleSurveyLoaded(response: response)
198
+ }
199
+ }
200
+ if(responseType == surveyCompleted){
201
+ if surveyDelegate != nil {
202
+ surveyDelegate.handleSurveyResponse(response: response)
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ public func loadFullscreenSurvey(parent: UIViewController,delegate:SsSurveyDelegate, domain: String? = nil, token: String? = nil, params: [String: String]? = [:] ) {
209
+ let ssSurveyViewController = SsSurveyViewController()
210
+ ssSurveyViewController.domain = domain
211
+ ssSurveyViewController.token = token
212
+ ssSurveyViewController.properties = self.properties ?? [:]
213
+ if(params != nil){
214
+ ssSurveyViewController.params = params ?? [:]
215
+ }
216
+ ssSurveyViewController.getSurveyLoadedResponse = true
217
+ if domain != nil && token != nil {
218
+ ssSurveyViewController.surveyDelegate = delegate
219
+ var isActive: Bool = false
220
+ var reason: String = ""
221
+ let group = DispatchGroup()
222
+ group.enter()
223
+ let completion: ([String: Any]) -> Void = { result in
224
+ if let active = result["active"] as? Bool {
225
+ isActive = active
226
+ }
227
+ if let reasonData = result["reason"] as? String {
228
+ reason = reasonData
229
+ }
230
+ if let widgetContactIdData = result["widgetContactId"] as? Int64 {
231
+ SsSurveyView.widgetContactId = widgetContactIdData
232
+ }
233
+ }
234
+ validateSurvey(domain:domain,token:token,params: params, group: group,completion:completion);
235
+ group.wait()
236
+ if isActive == true {
237
+ parent.present(ssSurveyViewController, animated: true)
238
+ } else {
239
+ ssSurveyViewController.surveyDelegate.handleSurveyValidation(response: [
240
+ "active": String(isActive),
241
+ "reason": reason,
242
+ ] as [String: AnyObject])
243
+ }
244
+ }
245
+ }
246
+
247
+ public func loadEmbedSurvey(domain: String? = nil, token: String? = nil, params: [String: String]? = [:]) {
248
+ self.domain = domain != nil ? domain! : self.domain
249
+ self.token = token != nil ? token! : self.token
250
+ if self.domain != nil && self.token != nil {
251
+ var isActive: Bool = false
252
+ var reason: String = ""
253
+ let group = DispatchGroup()
254
+ group.enter()
255
+ let completion: ([String: Any]) -> Void = { result in
256
+ if let active = result["active"] as? Bool {
257
+ isActive = active
258
+ }
259
+ if let reasonData = result["reason"] as? String {
260
+ reason = reasonData
261
+ }
262
+ if let widgetContactIdData = result["widgetContactId"] as? Int64 {
263
+ SsSurveyView.widgetContactId = widgetContactIdData
264
+ }
265
+ }
266
+ validateSurvey(domain:domain,token:token,params: params,group: group,completion:completion);
267
+ group.wait()
268
+ if isActive == true {
269
+ if(params != nil){
270
+ self.params = params ?? [:]
271
+ }
272
+ loadSurvey(domain:domain,token:token)
273
+ closeButton.isHidden = false ;
274
+ } else {
275
+ self.handleSurveyValidation(response: [
276
+ "active": String(isActive),
277
+ "reason": reason,
278
+ ] as [String: AnyObject])
279
+ }
280
+ }
281
+ }
282
+
283
+ // MARK: Public method
284
+ public func loadSurvey(domain: String? = nil, token: String? = nil) {
285
+ self.domain = domain != nil ? domain! : self.domain
286
+ self.token = token != nil ? token! : self.token
287
+ if self.domain != nil && self.token != nil {
288
+ loader.startAnimating()
289
+ var urlComponent = URLComponents()
290
+ urlComponent.scheme = "https"
291
+ urlComponent.host = self.domain!.trimmingCharacters(in: CharacterSet.whitespaces)
292
+ urlComponent.path = "/\(surveyType == .NPS ? "n" : "s")/ios/\(self.token!.trimmingCharacters(in: CharacterSet.whitespaces))"
293
+ if(getSurveyLoadedResponse){
294
+ params["isSurveyLoaded"] = "true"
295
+ }
296
+ urlComponent.queryItems = params.map {
297
+ URLQueryItem(name: $0.key, value: $0.value)
298
+ }
299
+ urlComponent.queryItems?.append(URLQueryItem(name: "sparrowLang", value: properties?["sparrowLang"] as? String))
300
+ if let url = urlComponent.url {
301
+ let request = URLRequest(url: url)
302
+ ssWebView.load(request)
303
+ }
304
+ } else {
305
+ print("Error: Domain or token is nil")
306
+ }
307
+ }
308
+
309
+ func handleSurveyValidation(response: [String : AnyObject]) {
310
+ print(response)
311
+ }
312
+ }
313
+
314
+ #endif
@@ -0,0 +1,81 @@
1
+ //
2
+ // SsSurveyViewController.swift
3
+ // SurveySparrowSdk
4
+ //
5
+ // Created by Gokulkrishna raju on 09/02/24.
6
+ // Copyright © 2020 SurveySparrow. All rights reserved.
7
+ //
8
+
9
+ #if canImport(UIKit)
10
+ import UIKit
11
+
12
+ @available(iOS 13.0, *)
13
+ @IBDesignable
14
+ public class SsSurveyViewController: UIViewController, SsSurveyDelegate {
15
+ // MARK: Properties
16
+ public var surveyDelegate: SsSurveyDelegate!
17
+
18
+ public var params: [String: String] = [:]
19
+ public var widgetContactId: Int64 = 0
20
+ public var surveyType: SurveySparrow.SurveyType = .CLASSIC
21
+ public var getSurveyLoadedResponse: Bool = false
22
+
23
+ @IBInspectable public var domain: String?
24
+ @IBInspectable public var token: String?
25
+ @IBInspectable public var properties: [String: Any] = [:]
26
+ @IBInspectable public var thankyouTimeout: Double = 3.0
27
+
28
+ // MARK: Initialize
29
+ public override func viewDidLoad() {
30
+ super.viewDidLoad()
31
+
32
+ view.backgroundColor = view.backgroundColor == nil ? .white : view.backgroundColor
33
+ if domain != nil && token != nil {
34
+ let ssSurveyView = SsSurveyView(properties: properties)
35
+ ssSurveyView.surveyDelegate = self
36
+ ssSurveyView.params = params
37
+ ssSurveyView.getSurveyLoadedResponse = getSurveyLoadedResponse
38
+
39
+ ssSurveyView.frame = view.bounds
40
+ ssSurveyView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
41
+
42
+ ssSurveyView.loadSurvey(domain: domain, token: token)
43
+ view.addSubview(ssSurveyView)
44
+ } else {
45
+ print("Error: Domain or token is nil")
46
+ }
47
+ }
48
+
49
+ // MARK: Delegate
50
+ public func handleSurveyResponse(response: [String : AnyObject]) {
51
+ if surveyDelegate != nil {
52
+ surveyDelegate.handleSurveyResponse(response: response)
53
+ }
54
+ DispatchQueue.main.asyncAfter(deadline: .now() + thankyouTimeout) {
55
+ self.dismiss(animated: true, completion: nil)
56
+ }
57
+ }
58
+
59
+ public func handleSurveyLoaded(response: [String : AnyObject]){
60
+ if surveyDelegate != nil {
61
+ surveyDelegate.handleSurveyLoaded(response: response)
62
+ }
63
+ }
64
+
65
+ public func handleCloseButtonTap() {
66
+ if surveyDelegate != nil {
67
+ surveyDelegate.handleCloseButtonTap()
68
+ }
69
+ }
70
+
71
+ public func handleSurveyValidation(response: [String : AnyObject]) {
72
+ if surveyDelegate != nil {
73
+ surveyDelegate.handleSurveyValidation(response: response)
74
+ }
75
+ DispatchQueue.main.asyncAfter(deadline: .now() + thankyouTimeout) {
76
+ self.dismiss(animated: true, completion: nil)
77
+ }
78
+ }
79
+ }
80
+
81
+ #endif
@@ -0,0 +1,158 @@
1
+ //
2
+ // SurveySparrow.swift
3
+ // SurveySparrowSdk
4
+ //
5
+ // Created by Gokulkrishna raju on 09/02/24.
6
+ // Copyright © 2020 SurveySparrow. All rights reserved.
7
+ //
8
+
9
+ #if canImport(UIKit)
10
+ import UIKit
11
+
12
+ @available(iOS 13.0, *)
13
+ public class SurveySparrow: SsSurveyDelegate {
14
+ // MARK: Properties
15
+ private var dataStore = NSUbiquitousKeyValueStore()
16
+ private var domain: String
17
+ private var token: String
18
+ private var properties: [String: String] = [:]
19
+
20
+ public var surveyType: SurveyType = .CLASSIC
21
+ public var params: [String: String] = [:]
22
+ public var thankyouTimout: Double = 3.0
23
+ public var surveyDelegate: SsSurveyDelegate!
24
+ public var alertTitle: String = "Rate us"
25
+ public var alertMessage: String = "Share your feedback and let us know how we are doing"
26
+ public var alertPositiveButton: String = "Rate Now"
27
+ public var alertNegativeButton: String = "Later"
28
+ public var isConnectedToNetwork: Bool = true
29
+ public var startAfter: Int64 = 259200000 // 3 days
30
+ public var repeatInterval: Int64 = 432000000 // 5 days
31
+ public var incrementalRepeat: Bool = false
32
+ public var repeatSurvey: Bool = false
33
+ public var getSurveyLoadedResponse: Bool = false
34
+
35
+ private var isAlreadyTakenKey = "isAlreadyTaken_"
36
+ private var promptTimeKey = "promptTime_"
37
+ private var incrementMultiplierKey = "incrementMultiplier_"
38
+
39
+ // MARK: Initialization
40
+ public init(domain: String, token: String, properties: [String: String]) {
41
+ self.domain = domain
42
+ self.token = token
43
+ self.properties = properties
44
+
45
+ isAlreadyTakenKey += token
46
+ promptTimeKey += token
47
+ incrementMultiplierKey += token
48
+ }
49
+
50
+ // MARK: Data Type
51
+ public enum SurveyType {
52
+ case CLASSIC
53
+ case CHAT
54
+ case NPS
55
+ }
56
+
57
+ // MARK: Public methods
58
+ public func scheduleSurvey(parent: UIViewController) {
59
+ let currentTime = Int64(Date().timeIntervalSince1970 * 1000)
60
+ let isAlreadyTaken = UserDefaults.standard.bool(forKey: isAlreadyTakenKey)
61
+ let promptTime = UserDefaults.standard.integer(forKey: promptTimeKey)
62
+ var incrementMultiplier = UserDefaults.standard.integer(forKey: incrementMultiplierKey)
63
+ incrementMultiplier = incrementMultiplier == 0 ? 1 : incrementMultiplier
64
+
65
+ if promptTime == 0 {
66
+ let nextPrompt = currentTime + startAfter
67
+ UserDefaults.standard.set(nextPrompt, forKey: promptTimeKey)
68
+ dataStore.set(1, forKey: incrementMultiplierKey)
69
+ return
70
+ }
71
+ if self.domain != nil && self.token != nil {
72
+ if isConnectedToNetwork && (!isAlreadyTaken || repeatSurvey) && (promptTime < currentTime) {
73
+ var isActive: Bool = false
74
+ var reason: String = ""
75
+ let group = DispatchGroup()
76
+ var widgetContactId: Int64 = 0 ;
77
+ group.enter()
78
+ let completion: ([String: Any]) -> Void = { result in
79
+ if let active = result["active"] as? Bool {
80
+ isActive = active
81
+ }
82
+ if let reasonData = result["reason"] as? String {
83
+ reason = reasonData
84
+ }
85
+ if let widgetContactIdData = result["widgetContactId"] as? Int64 {
86
+ widgetContactId = widgetContactIdData
87
+ }
88
+ }
89
+ validateSurvey(domain:domain,token:token,params:self.params, group: group,completion:completion);
90
+ group.wait()
91
+ if isActive == true {
92
+
93
+ let alertDialog = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: UIAlertController.Style.alert)
94
+ alertDialog.addAction(UIAlertAction(title: alertPositiveButton, style: UIAlertAction.Style.default, handler: {action in
95
+ let ssSurveyViewController = SsSurveyViewController()
96
+ ssSurveyViewController.domain = self.domain
97
+ ssSurveyViewController.token = self.token
98
+ ssSurveyViewController.params = self.params
99
+ ssSurveyViewController.properties = self.properties
100
+ ssSurveyViewController.widgetContactId = widgetContactId
101
+ ssSurveyViewController.getSurveyLoadedResponse = self.getSurveyLoadedResponse
102
+ ssSurveyViewController.thankyouTimeout = self.thankyouTimout
103
+ ssSurveyViewController.surveyDelegate = self
104
+ parent.present(ssSurveyViewController, animated: true, completion: nil)
105
+ }))
106
+ alertDialog.addAction(UIAlertAction(title: alertNegativeButton, style: UIAlertAction.Style.cancel, handler: nil))
107
+ parent.present(alertDialog, animated: true)
108
+
109
+ UserDefaults.standard.set(incrementalRepeat ? incrementMultiplier * 2 : 1, forKey: self.incrementMultiplierKey)
110
+ let timeTillNext = repeatInterval * Int64(incrementMultiplier)
111
+ let nextPrompt = currentTime + timeTillNext
112
+ UserDefaults.standard.set(nextPrompt, forKey: promptTimeKey)
113
+ } else {
114
+ self.handleSurveyValidation(response: [
115
+ "active": String(isActive),
116
+ "reason": reason,
117
+ ] as [String: AnyObject])
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ public func clearSchedule() {
124
+ UserDefaults.standard.removeObject(forKey: incrementMultiplierKey)
125
+ UserDefaults.standard.removeObject(forKey: isAlreadyTakenKey)
126
+ UserDefaults.standard.removeObject(forKey: promptTimeKey)
127
+ }
128
+
129
+ // MARK: Delegate
130
+ public func handleSurveyResponse(response: [String : AnyObject]) {
131
+ UserDefaults.standard.set(true, forKey: isAlreadyTakenKey)
132
+ if surveyDelegate != nil {
133
+ self.surveyDelegate.handleSurveyResponse(response: response)
134
+ }
135
+ }
136
+
137
+ public func handleSurveyLoaded(response: [String : AnyObject]){
138
+ if surveyDelegate != nil {
139
+ self.surveyDelegate.handleSurveyResponse(response: response)
140
+ }
141
+ }
142
+
143
+ public func handleSurveyValidation(response: [String : AnyObject]) {
144
+ UserDefaults.standard.set(true, forKey: isAlreadyTakenKey)
145
+ if surveyDelegate != nil {
146
+ self.surveyDelegate.handleSurveyResponse(response: response)
147
+ }
148
+ }
149
+
150
+ public func handleCloseButtonTap() {
151
+ if surveyDelegate != nil {
152
+ surveyDelegate.handleCloseButtonTap()
153
+ }
154
+ }
155
+
156
+ }
157
+
158
+ #endif
@@ -1,7 +1,6 @@
1
1
 
2
2
  import Foundation
3
3
  import SwiftUI
4
- import SurveySparrowSdk
5
4
 
6
5
  @objc public class SurveySparrowIonicPlugin: NSObject {
7
6
 
@@ -30,7 +29,7 @@ struct FullScreenSurveyView: UIViewControllerRepresentable {
30
29
  ssSurveyViewController.params = params
31
30
  ssSurveyViewController.properties = properties
32
31
  ssSurveyViewController.getSurveyLoadedResponse = true
33
- ssSurveyViewController.surveyDelegate = SurveyDelegate()
32
+ ssSurveyViewController.surveyDelegate = SsDelegate()
34
33
  return ssSurveyViewController
35
34
  }
36
35
 
@@ -55,7 +54,7 @@ struct FullScreenSurveyWithValidation {
55
54
  let surveyView = SsSurveyView(properties: properties)
56
55
  surveyView.loadFullscreenSurvey(
57
56
  parent: rootViewController,
58
- delegate: SurveyDelegate(),
57
+ delegate: SsDelegate(),
59
58
  domain: domain,
60
59
  token: token,
61
60
  params: params
@@ -1,4 +1,5 @@
1
1
  import XCTest
2
+ import Capacitor
2
3
  @testable import SurveySparrowIonicPluginPlugin
3
4
 
4
5
  class SurveySparrowIonicPluginPluginTests: XCTestCase {
@@ -32,9 +33,11 @@ class SurveySparrowIonicPluginPluginTests: XCTestCase {
32
33
  "token": token,
33
34
  "params": params,
34
35
  "properties": properties
35
- ]) { result, _ in
36
+ ], success: { result, _ in
36
37
  XCTAssertNotNil(result)
37
- }
38
+ }, error: { error in
39
+ XCTAssertNil(error)
40
+ })
38
41
 
39
42
  plugin.loadFullScreenSurvey(mockCall)
40
43
 
@@ -56,9 +59,11 @@ class SurveySparrowIonicPluginPluginTests: XCTestCase {
56
59
  "token": token,
57
60
  "params": params,
58
61
  "properties": properties
59
- ]) { result, _ in
62
+ ], success: { result, _ in
60
63
  XCTAssertNotNil(result)
61
- }
64
+ }, error: { error in
65
+ XCTAssertNil(error)
66
+ })
62
67
 
63
68
  plugin.loadFullScreenSurveyWithValidation(mockCall)
64
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "surveysparrow-ionic-plugin",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "SurveySparrow SDK enables you to collect feedback from your mobile app. Embed the Classic, Chat & NPS surveys in your ionic application seamlessly with few lines of code.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",