Skip to content

Commit aeb7c07

Browse files
Merge branch 'stable'
# Conflicts: # Iterable-iOS-AppExtensions.podspec # Iterable-iOS-SDK.podspec # Tests/swift-sdk-swift-tests/IterableInAppNotificationTests.swift # swift-sdk/ITBConsts.swift # swift-sdk/IterableAPI.swift
2 parents 6b10ba1 + ba37825 commit aeb7c07

File tree

7 files changed

+142
-139
lines changed

7 files changed

+142
-139
lines changed

CHANGELOG.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5-
## [Unreleased]
6-
#### Added
7-
- nothing yet
8-
9-
#### Removed
10-
- nothing yet
5+
## [6.0.7](https://github.com/Iterable/swift-sdk/releases/tag/6.0.7)
6+
#### Fixed
7+
- XCode 10.2 Warnings
8+
- URL Query parameters encoding bug
119

12-
#### Changed
13-
- nothing yet
10+
## [6.0.6](https://github.com/Iterable/swift-sdk/releases/tag/6.0.6)
11+
#### Added
12+
- Update to Swift 4.2
1413

14+
## [6.0.5](https://github.com/Iterable/swift-sdk/releases/tag/6.0.5)
1515
#### Fixed
16-
- nothing yet
16+
- Carthage support
1717

1818
## 6.1.0-beta1
1919
#### Added
@@ -66,3 +66,16 @@ This project adheres to [Semantic Versioning](http://semver.org/).
6666
#### Fixed
6767
- Fixed issue that affects clients who are upgrading from Objective C Iterable SDK to Swift SDK. If you have attribution info stored in the previous Objective C SDK, it was not being deserialized in Swift SDK.
6868

69+
## [Unreleased]
70+
#### Added
71+
- nothing yet
72+
73+
#### Removed
74+
- nothing yet
75+
76+
#### Changed
77+
- nothing yet
78+
79+
#### Fixed
80+
- nothing yet
81+

Tests/swift-sdk-swift-tests/IterableRequestUtilTests.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ class IterableRequestUtilTests: XCTestCase {
5757
TestUtils.validate(request: request, requestType: .get, apiEndPoint: apiEndPoint, path: path, queryParams: queryParams)
5858
}
5959

60+
func testGetRequestWithPlusSignInEmail() {
61+
let apiEndPoint = "https://somewhere.com/"
62+
let path = "path"
63+
let args = ["email" : "[email protected]"]
64+
let request = IterableRequestUtil.createGetRequest(forApiEndPoint: apiEndPoint, path: path, args: args)!
65+
let requestString = String(describing: request)
66+
XCTAssertEqual(requestString, "https://somewhere.com/path?email=user%[email protected]")
67+
}
68+
6069
func testPostRequest() {
6170
let apiEndPoint = "https://somewhere.com/"
6271
let path = "path"
@@ -77,11 +86,4 @@ class IterableRequestUtilTests: XCTestCase {
7786
TestUtils.validateElementPresent(withName: "var1", andValue: "val1", inDictionary: bodyFromRequest)
7887
TestUtils.validateElementPresent(withName: "var2", andValue: "val2", inDictionary: bodyFromRequest)
7988
}
80-
81-
func testEncodeUrlQueryParam() {
82-
let encoded = IterableRequestUtil.encodeURLParam("[email protected]")
83-
XCTAssertEqual(encoded!, "you%[email protected]")
84-
XCTAssertEqual(IterableRequestUtil.encodeURLParam(""), "")
85-
XCTAssertNil(IterableRequestUtil.encodeURLParam(nil))
86-
}
8789
}

swift-sdk/ITBConsts.swift

Lines changed: 101 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -8,156 +8,156 @@ import Foundation
88

99
// Iterable API Endpoints
1010
public extension String {
11-
public static let ITBL_ENDPOINT_API = apiHostname + "/api/"
12-
public static let ITBL_ENDPOINT_LINKS = linksHostname + "/"
11+
static let ITBL_ENDPOINT_API = apiHostname + "/api/"
12+
static let ITBL_ENDPOINT_LINKS = linksHostname + "/"
1313

1414
private static let apiHostname = "https://api.iterable.com"
1515
private static let linksHostname = "https://links.iterable.com"
1616
}
1717

1818
// API Paths (Offset from base)
1919
public extension String {
20-
public static let ITBL_PATH_COMMERCE_TRACK_PURCHASE = "commerce/trackPurchase"
21-
public static let ITBL_PATH_DISABLE_DEVICE = "users/disableDevice"
22-
public static let ITBL_PATH_GET_INAPP_MESSAGES = "inApp/getMessages"
23-
public static let ITBL_PATH_INAPP_CONSUME = "events/inAppConsume"
24-
public static let ITBL_PATH_REGISTER_DEVICE_TOKEN = "users/registerDeviceToken"
25-
public static let ITBL_PATH_TRACK = "events/track"
26-
public static let ITBL_PATH_TRACK_INAPP_CLICK = "events/trackInAppClick"
27-
public static let ITBL_PATH_TRACK_INAPP_OPEN = "events/trackInAppOpen"
28-
public static let ITBL_PATH_TRACK_PUSH_OPEN = "events/trackPushOpen"
29-
public static let ITBL_PATH_UPDATE_USER = "users/update"
30-
public static let ITBL_PATH_UPDATE_EMAIL = "users/updateEmail"
31-
public static let ITBL_PATH_UPDATE_SUBSCRIPTIONS = "users/updateSubscriptions"
32-
public static let ITBL_PATH_DDL_MATCH = "a/matchFp" //DDL = Deferred Deep Linking
20+
static let ITBL_PATH_COMMERCE_TRACK_PURCHASE = "commerce/trackPurchase"
21+
static let ITBL_PATH_DISABLE_DEVICE = "users/disableDevice"
22+
static let ITBL_PATH_GET_INAPP_MESSAGES = "inApp/getMessages"
23+
static let ITBL_PATH_INAPP_CONSUME = "events/inAppConsume"
24+
static let ITBL_PATH_REGISTER_DEVICE_TOKEN = "users/registerDeviceToken"
25+
static let ITBL_PATH_TRACK = "events/track"
26+
static let ITBL_PATH_TRACK_INAPP_CLICK = "events/trackInAppClick"
27+
static let ITBL_PATH_TRACK_INAPP_OPEN = "events/trackInAppOpen"
28+
static let ITBL_PATH_TRACK_PUSH_OPEN = "events/trackPushOpen"
29+
static let ITBL_PATH_UPDATE_USER = "users/update"
30+
static let ITBL_PATH_UPDATE_EMAIL = "users/updateEmail"
31+
static let ITBL_PATH_UPDATE_SUBSCRIPTIONS = "users/updateSubscriptions"
32+
static let ITBL_PATH_DDL_MATCH = "a/matchFp" //DDL = Deferred Deep Linking
3333
}
3434

3535
// Keys
3636
public extension AnyHashable {
37-
public static let ITBL_KEY_API_KEY = "api_key"
38-
public static let ITBL_KEY_APPLICATION_NAME = "applicationName"
39-
public static let ITBL_KEY_CAMPAIGN_ID = "campaignId"
40-
public static let ITBL_KEY_COUNT = "count"
41-
public static let ITBL_KEY_CURRENT_EMAIL = "currentEmail"
42-
public static let ITBL_KEY_CURRENT_USER_ID = "currentUserId"
43-
public static let ITBL_KEY_DATA_FIELDS = "dataFields"
44-
public static let ITBL_KEY_DEVICE = "device"
45-
public static let ITBL_KEY_EMAIL = "email"
46-
public static let ITBL_KEY_EMAIL_LIST_IDS = "emailListIds"
47-
public static let ITBL_KEY_EVENT_NAME = "eventName"
48-
public static let ITBL_KEY_ITEMS = "items"
49-
public static let ITBL_KEY_MERGE_NESTED = "mergeNestedObjects"
50-
public static let ITBL_KEY_MESSAGE_ID = "messageId"
51-
public static let ITBL_KEY_NEW_EMAIL = "newEmail"
52-
public static let ITBL_KEY_PLATFORM = "platform"
53-
public static let ITBL_KEY_SDK_VERSION = "SDKVersion"
54-
public static let ITBL_KEY_TOKEN = "token"
55-
public static let ITBL_KEY_TEMPLATE_ID = "templateId"
56-
public static let ITBL_KEY_TOTAL = "total"
57-
public static let ITBL_KEY_UNSUB_CHANNEL = "unsubscribedChannelIds"
58-
public static let ITBL_KEY_UNSUB_MESSAGE = "unsubscribedMessageTypeIds"
59-
public static let ITBL_KEY_USER = "user"
60-
public static let ITBL_KEY_USER_ID = "userId"
61-
public static let ITBL_KEY_ACTION_IDENTIFIER = "actionIdentifier"
62-
public static let ITBL_KEY_USER_TEXT = "userText"
63-
public static let ITBL_KEY_PREFER_USER_ID = "preferUserId"
37+
static let ITBL_KEY_API_KEY = "api_key"
38+
static let ITBL_KEY_APPLICATION_NAME = "applicationName"
39+
static let ITBL_KEY_CAMPAIGN_ID = "campaignId"
40+
static let ITBL_KEY_COUNT = "count"
41+
static let ITBL_KEY_CURRENT_EMAIL = "currentEmail"
42+
static let ITBL_KEY_CURRENT_USER_ID = "currentUserId"
43+
static let ITBL_KEY_DATA_FIELDS = "dataFields"
44+
static let ITBL_KEY_DEVICE = "device"
45+
static let ITBL_KEY_EMAIL = "email"
46+
static let ITBL_KEY_EMAIL_LIST_IDS = "emailListIds"
47+
static let ITBL_KEY_EVENT_NAME = "eventName"
48+
static let ITBL_KEY_ITEMS = "items"
49+
static let ITBL_KEY_MERGE_NESTED = "mergeNestedObjects"
50+
static let ITBL_KEY_MESSAGE_ID = "messageId"
51+
static let ITBL_KEY_NEW_EMAIL = "newEmail"
52+
static let ITBL_KEY_PLATFORM = "platform"
53+
static let ITBL_KEY_SDK_VERSION = "SDKVersion"
54+
static let ITBL_KEY_TOKEN = "token"
55+
static let ITBL_KEY_TEMPLATE_ID = "templateId"
56+
static let ITBL_KEY_TOTAL = "total"
57+
static let ITBL_KEY_UNSUB_CHANNEL = "unsubscribedChannelIds"
58+
static let ITBL_KEY_UNSUB_MESSAGE = "unsubscribedMessageTypeIds"
59+
static let ITBL_KEY_USER = "user"
60+
static let ITBL_KEY_USER_ID = "userId"
61+
static let ITBL_KEY_ACTION_IDENTIFIER = "actionIdentifier"
62+
static let ITBL_KEY_USER_TEXT = "userText"
63+
static let ITBL_KEY_PREFER_USER_ID = "preferUserId"
6464
}
6565

6666
// More Keys
6767
public extension String {
68-
public static let ITBL_KEY_GET = "GET"
69-
public static let ITBL_KEY_POST = "POST"
68+
static let ITBL_KEY_GET = "GET"
69+
static let ITBL_KEY_POST = "POST"
7070

71-
public static let ITBL_KEY_APNS = "APNS"
72-
public static let ITBL_KEY_APNS_SANDBOX = "APNS_SANDBOX"
73-
public static let ITBL_KEY_PAD = "Pad"
74-
public static let ITBL_KEY_PHONE = "Phone"
75-
public static let ITBL_KEY_UNSPECIFIED = "Unspecified"
71+
static let ITBL_KEY_APNS = "APNS"
72+
static let ITBL_KEY_APNS_SANDBOX = "APNS_SANDBOX"
73+
static let ITBL_KEY_PAD = "Pad"
74+
static let ITBL_KEY_PHONE = "Phone"
75+
static let ITBL_KEY_UNSPECIFIED = "Unspecified"
7676
}
7777

7878
// Misc Values
7979
public extension String {
80-
public static let ITBL_VALUE_DEFAULT_PUSH_OPEN_ACTION_ID = "default"
81-
public static let ITBL_PLATFORM_IOS = "iOS"
82-
public static let ITBL_DEEPLINK_IDENTIFIER = "/a/[a-zA-Z0-9]+"
80+
static let ITBL_VALUE_DEFAULT_PUSH_OPEN_ACTION_ID = "default"
81+
static let ITBL_PLATFORM_IOS = "iOS"
82+
static let ITBL_DEEPLINK_IDENTIFIER = "/a/[a-zA-Z0-9]+"
8383
}
8484

8585
// Decvice Dictionary
8686
public extension String {
87-
public static let ITBL_DEVICE_LOCALIZED_MODEL = "localizedModel"
88-
public static let ITBL_DEVICE_ID_VENDOR = "identifierForVendor"
89-
public static let ITBL_DEVICE_MODEL = "model"
90-
public static let ITBL_DEVICE_SYSTEM_NAME = "systemName"
91-
public static let ITBL_DEVICE_SYSTEM_VERSION = "systemVersion"
92-
public static let ITBL_DEVICE_USER_INTERFACE = "userInterfaceIdiom"
87+
static let ITBL_DEVICE_LOCALIZED_MODEL = "localizedModel"
88+
static let ITBL_DEVICE_ID_VENDOR = "identifierForVendor"
89+
static let ITBL_DEVICE_MODEL = "model"
90+
static let ITBL_DEVICE_SYSTEM_NAME = "systemName"
91+
static let ITBL_DEVICE_SYSTEM_VERSION = "systemVersion"
92+
static let ITBL_DEVICE_USER_INTERFACE = "userInterfaceIdiom"
9393

94-
public static let ITBL_DEVICE_DEVICE_ID = "deviceId"
95-
public static let ITBL_DEVICE_APP_PACKAGE_NAME = "appPackageName"
96-
public static let ITBL_DEVICE_APP_VERSION = "appVersion"
97-
public static let ITBL_DEVICE_APP_BUILD = "appBuild"
98-
public static let ITBL_DEVICE_NOTIFICATIONS_ENABLED = "notificationsEnabled"
99-
public static let ITBL_DEVICE_ITERABLE_SDK_VERSION = "iterableSdkVersion"
94+
static let ITBL_DEVICE_DEVICE_ID = "deviceId"
95+
static let ITBL_DEVICE_APP_PACKAGE_NAME = "appPackageName"
96+
static let ITBL_DEVICE_APP_VERSION = "appVersion"
97+
static let ITBL_DEVICE_APP_BUILD = "appBuild"
98+
static let ITBL_DEVICE_NOTIFICATIONS_ENABLED = "notificationsEnabled"
99+
static let ITBL_DEVICE_ITERABLE_SDK_VERSION = "iterableSdkVersion"
100100
}
101101

102102
// Push Payload
103103
public extension AnyHashable {
104-
public static let ITBL_PAYLOAD_METADATA = "itbl"
105-
public static let ITBL_PAYLOAD_MESSAGE_ID = "messageId"
106-
public static let ITBL_PAYLOAD_DEEP_LINK_URL = "url"
107-
public static let ITBL_PAYLOAD_ATTACHMENT_URL = "attachment-url"
108-
public static let ITBL_PAYLOAD_ACTION_BUTTONS = "actionButtons"
109-
public static let ITBL_PAYLOAD_DEFAULT_ACTION = "defaultAction"
104+
static let ITBL_PAYLOAD_METADATA = "itbl"
105+
static let ITBL_PAYLOAD_MESSAGE_ID = "messageId"
106+
static let ITBL_PAYLOAD_DEEP_LINK_URL = "url"
107+
static let ITBL_PAYLOAD_ATTACHMENT_URL = "attachment-url"
108+
static let ITBL_PAYLOAD_ACTION_BUTTONS = "actionButtons"
109+
static let ITBL_PAYLOAD_DEFAULT_ACTION = "defaultAction"
110110
}
111111

112112
// UserDefaults String Consts
113113
public extension String {
114-
public static let ITBL_USER_DEFAULTS_PAYLOAD_KEY = "itbl_payload_key"
115-
public static let ITBL_USER_DEFAULTS_ATTRIBUTION_INFO_KEY = "itbl_attribution_info_key"
116-
public static let ITBL_USER_DEFAULTS_EMAIL_KEY = "itbl_email"
117-
public static let ITBL_USER_DEFAULTS_USERID_KEY = "itbl_userid"
118-
public static let ITBL_USER_DEFAULTS_DDL_CHECKED = "itbl_ddl_checked"
119-
public static let ITBL_USER_DEFAULTS_DEVICE_ID = "itbl_device_id"
120-
public static let ITBL_USER_DEFAULTS_SDK_VERSION = "itbl_sdk_version"
114+
static let ITBL_USER_DEFAULTS_PAYLOAD_KEY = "itbl_payload_key"
115+
static let ITBL_USER_DEFAULTS_ATTRIBUTION_INFO_KEY = "itbl_attribution_info_key"
116+
static let ITBL_USER_DEFAULTS_EMAIL_KEY = "itbl_email"
117+
static let ITBL_USER_DEFAULTS_USERID_KEY = "itbl_userid"
118+
static let ITBL_USER_DEFAULTS_DDL_CHECKED = "itbl_ddl_checked"
119+
static let ITBL_USER_DEFAULTS_DEVICE_ID = "itbl_device_id"
120+
static let ITBL_USER_DEFAULTS_SDK_VERSION = "itbl_sdk_version"
121121
}
122122

123123
// UserDefaults Int Consts
124124
public extension Int {
125-
public static let ITBL_USER_DEFAULTS_PAYLOAD_EXPIRATION_HOURS = 24
126-
public static let ITBL_USER_DEFAULTS_ATTRIBUTION_INFO_EXPIRATION_HOURS = 24
125+
static let ITBL_USER_DEFAULTS_PAYLOAD_EXPIRATION_HOURS = 24
126+
static let ITBL_USER_DEFAULTS_ATTRIBUTION_INFO_EXPIRATION_HOURS = 24
127127
}
128128

129129
//Action Buttons
130130
public extension AnyHashable {
131-
public static let ITBL_BUTTON_IDENTIFIER = "identifier"
132-
public static let ITBL_BUTTON_TYPE = "buttonType"
133-
public static let ITBL_BUTTON_TITLE = "title"
134-
public static let ITBL_BUTTON_OPEN_APP = "openApp"
135-
public static let ITBL_BUTTON_REQUIRES_UNLOCK = "requiresUnlock"
136-
public static let ITBL_BUTTON_INPUT_TITLE = "inputTitle"
137-
public static let ITBL_BUTTON_INPUT_PLACEHOLDER = "inputPlaceholder"
138-
public static let ITBL_BUTTON_ACTION = "action"
131+
static let ITBL_BUTTON_IDENTIFIER = "identifier"
132+
static let ITBL_BUTTON_TYPE = "buttonType"
133+
static let ITBL_BUTTON_TITLE = "title"
134+
static let ITBL_BUTTON_OPEN_APP = "openApp"
135+
static let ITBL_BUTTON_REQUIRES_UNLOCK = "requiresUnlock"
136+
static let ITBL_BUTTON_INPUT_TITLE = "inputTitle"
137+
static let ITBL_BUTTON_INPUT_PLACEHOLDER = "inputPlaceholder"
138+
static let ITBL_BUTTON_ACTION = "action"
139139
}
140140

141141
//In-App Constants
142142
public extension AnyHashable {
143-
public static let ITBL_IN_APP_CLICK_URL = "urlClick"
143+
static let ITBL_IN_APP_CLICKED_URL = "clickedUrl"
144144

145-
public static let ITBL_IN_APP_BUTTON_INDEX = "buttonIndex"
146-
public static let ITBL_IN_APP_MESSAGE = "inAppMessages"
145+
static let ITBL_IN_APP_BUTTON_INDEX = "buttonIndex"
146+
static let ITBL_IN_APP_MESSAGE = "inAppMessages"
147147

148-
public static let ITBL_IN_APP_TRIGGER = "trigger"
149-
public static let ITBL_IN_APP_TRIGGER_TYPE = "type"
150-
public static let ITBL_IN_APP_EXPIRES_AT = "expiresAt"
148+
static let ITBL_IN_APP_TRIGGER = "trigger"
149+
static let ITBL_IN_APP_TRIGGER_TYPE = "type"
150+
static let ITBL_IN_APP_EXPIRES_AT = "expiresAt"
151151

152-
public static let ITBL_IN_APP_CONTENT = "content"
152+
static let ITBL_IN_APP_CONTENT = "content"
153153

154154
//In-App HTML Constants
155-
public static let ITBL_IN_APP_BACKGROUND_ALPHA = "backgroundAlpha"
156-
public static let ITBL_IN_APP_HTML = "html"
157-
public static let ITBL_IN_APP_HREF = "href"
158-
public static let ITBL_IN_APP_DISPLAY_SETTINGS = "inAppDisplaySettings"
159-
public static let ITBL_IN_APP_CUSTOM_PAYLOAD = "customPayload"
160-
public static let ITBL_IN_APP_INAPP_TYPE = "inAppType"
161-
public static let ITBL_IN_APP_CONTENT_TYPE = "contentType"
155+
static let ITBL_IN_APP_BACKGROUND_ALPHA = "backgroundAlpha"
156+
static let ITBL_IN_APP_HTML = "html"
157+
static let ITBL_IN_APP_HREF = "href"
158+
static let ITBL_IN_APP_DISPLAY_SETTINGS = "inAppDisplaySettings"
159+
static let ITBL_IN_APP_CUSTOM_PAYLOAD = "customPayload"
160+
static let ITBL_IN_APP_INAPP_TYPE = "inAppType"
161+
static let ITBL_IN_APP_CONTENT_TYPE = "contentType"
162162
}
163163

swift-sdk/Internal/ClassExtensions.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ public extension Array {
1515
}
1616

1717
public extension Bundle {
18-
public var appPackageName : String? {
18+
var appPackageName : String? {
1919
return bundleIdentifier
2020
}
2121

22-
public var appVersion : String? {
22+
var appVersion : String? {
2323
guard let infoDictionary = self.infoDictionary else {
2424
return nil
2525
}
2626
return infoDictionary["CFBundleShortVersionString"] as? String
2727
}
2828

29-
public var appBuild : String? {
29+
var appBuild : String? {
3030
guard let infoDictionary = self.infoDictionary else {
3131
return nil
3232
}

swift-sdk/Internal/IterableAPIInternal.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ final class IterableAPIInternal : NSObject, PushTrackerProtocol {
449449
func trackInAppClick(_ messageId: String, buttonURL: String) {
450450
var args: [AnyHashable : Any] = [
451451
.ITBL_KEY_MESSAGE_ID: messageId,
452-
.ITBL_IN_APP_CLICK_URL: buttonURL
452+
.ITBL_IN_APP_CLICKED_URL: buttonURL
453453
]
454454
addEmailOrUserId(args: &args)
455455

swift-sdk/Internal/IterableRequestUtil.swift

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,13 @@ struct IterableRequestUtil {
4545
}
4646

4747
if let args = args {
48-
components.queryItems = args.map{ URLQueryItem(name: $0.key, value: encodeURLParam($0.value)) }
48+
components.queryItems = args.map { URLQueryItem(name: $0.key, value: $0.value) }
4949
}
50+
components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
5051

5152
return components
5253
}
5354

54-
static func encodeURLParam(_ paramValue: String?) -> String? {
55-
guard let paramValue = paramValue else {
56-
return nil
57-
}
58-
return paramValue.addingPercentEncoding(withAllowedCharacters: encodedCharacterSet)
59-
}
60-
6155
static func dictToJsonData(_ dict: [AnyHashable : Any]?) -> Data? {
6256
guard let dict = dict else {
6357
return nil
@@ -81,10 +75,4 @@ struct IterableRequestUtil {
8175
result.append(path2)
8276
return result
8377
}
84-
85-
private static let encodedCharacterSet : CharacterSet = {
86-
var characterSet = CharacterSet.urlQueryAllowed
87-
characterSet.remove(charactersIn: "+")
88-
return characterSet
89-
} ()
9078
}

0 commit comments

Comments
 (0)