expo-dev-launcher 1.2.1 → 1.3.0

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.
Files changed (28) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/android/build.gradle +1 -1
  3. package/android/src/debug/assets/expo_dev_launcher_android.bundle +22 -21
  4. package/android/src/main/AndroidManifest.xml +0 -1
  5. package/android/src/main/java/expo/modules/devlauncher/helpers/DevLauncherReactUtils.kt +3 -1
  6. package/android/src/main/java/expo/modules/devlauncher/launcher/errors/DevLauncherUncaughtExceptionHandler.kt +6 -9
  7. package/android/src/main/java/expo/modules/devlauncher/logs/DevLauncherRemoteLog.kt +13 -37
  8. package/android/src/main/java/expo/modules/devlauncher/logs/DevLauncherRemoteLogManager.kt +27 -31
  9. package/android/src/react-native-64/expo/modules/devlauncher/rncompatibility/DevLauncherReactNativeHostHandler.kt +5 -0
  10. package/android/src/react-native-65/expo/modules/devlauncher/rncompatibility/DevLauncherReactNativeHostHandler.kt +5 -0
  11. package/android/src/react-native-66/expo/modules/devlauncher/rncompatibility/DevLauncherReactNativeHostHandler.kt +5 -0
  12. package/android/src/react-native-67/expo/modules/devlauncher/rncompatibility/DevLauncherReactNativeHostHandler.kt +5 -0
  13. package/android/src/react-native-69/expo/modules/devlauncher/rncompatibility/DevLauncherReactNativeHostHandler.kt +5 -0
  14. package/bundle/components/AppHeader.tsx +7 -1
  15. package/bundle/components/ScreenContainer.tsx +8 -0
  16. package/bundle/native-modules/DevLauncherAuth.ts +21 -10
  17. package/bundle/screens/BranchesScreen.tsx +18 -15
  18. package/bundle/screens/ExtensionsScreen.tsx +37 -38
  19. package/bundle/screens/ExtensionsStack.tsx +1 -0
  20. package/bundle/screens/HomeScreen.tsx +9 -2
  21. package/bundle/screens/SettingsScreen.tsx +113 -110
  22. package/bundle/screens/UpdatesScreen.tsx +44 -39
  23. package/ios/EXDevLauncherController.m +3 -0
  24. package/ios/EXDevLauncherURLHelper.swift +30 -19
  25. package/ios/Errors/EXDevLauncherUncaughtExceptionHandler.swift +14 -10
  26. package/ios/Logs/EXDevLauncherRemoteLogsManager.swift +39 -34
  27. package/ios/main.jsbundle +22 -21
  28. package/package.json +4 -4
@@ -16,6 +16,7 @@ import * as React from 'react';
16
16
  import { ScrollView, Switch } from 'react-native';
17
17
  import { useQueryClient } from 'react-query';
18
18
  import { SafeAreaTop } from '../components/SafeAreaTop';
19
+ import { ScreenContainer } from '../components/ScreenContainer';
19
20
 
20
21
  import { Toasts } from '../components/Toasts';
21
22
  import { copyToClipboardAsync, updatesConfig } from '../native-modules/DevLauncherInternal';
@@ -83,135 +84,137 @@ export function SettingsScreen() {
83
84
 
84
85
  return (
85
86
  <ScrollView testID="DevLauncherSettingsScreen" showsVerticalScrollIndicator={false}>
86
- <SafeAreaTop />
87
- <Spacer.Vertical size="medium" />
88
-
89
- <View px="medium">
90
- <Heading size="large">Settings</Heading>
91
- </View>
92
-
93
- <View py="large" px="medium">
94
- <View bg="default" rounded="large">
95
- <Row px="medium" py="small" align="center">
96
- <ShowMenuIcon />
97
- <Spacer.Horizontal size="small" />
98
- <Text size="large">Show menu at launch</Text>
99
- <Spacer.Horizontal />
100
- <Switch
101
- accessibilityRole="switch"
102
- accessibilityLabel="Toggle showing menu at launch"
103
- value={showsAtLaunch}
104
- onValueChange={() => setShowsAtLaunch(!showsAtLaunch)}
105
- />
106
- </Row>
107
- </View>
108
-
109
- <Spacer.Vertical size="large" />
87
+ <ScreenContainer>
88
+ <SafeAreaTop />
89
+ <Spacer.Vertical size="medium" />
110
90
 
111
- <View padding="medium">
112
- <Heading color="secondary">Menu gestures</Heading>
91
+ <View px="medium">
92
+ <Heading size="large">Settings</Heading>
113
93
  </View>
114
94
 
115
- <View>
116
- <Button.ScaleOnPressContainer
117
- bg="default"
118
- roundedTop="large"
119
- roundedBottom="none"
120
- onPress={() => setMotionGestureEnabled(!motionGestureEnabled)}
121
- accessibilityState={{ checked: motionGestureEnabled }}>
122
- <Row px="medium" py="small" align="center" bg="default">
123
- <ShakeDeviceIcon />
124
- <Spacer.Horizontal size="small" />
125
- <Text size="large" color="default">
126
- Shake device
127
- </Text>
128
- <Spacer.Horizontal />
129
- {motionGestureEnabled && <CheckIcon />}
130
- </Row>
131
- </Button.ScaleOnPressContainer>
132
-
133
- <Divider />
134
-
135
- <Button.ScaleOnPressContainer
136
- bg="default"
137
- roundedBottom="large"
138
- roundedTop="none"
139
- onPress={() => setTouchGestureEnabled(!touchGestureEnabled)}
140
- accessibilityState={{ checked: touchGestureEnabled }}>
141
- <Row px="medium" py="small" bg="default">
142
- <ThreeFingerPressIcon />
95
+ <View py="large" px="medium">
96
+ <View bg="default" rounded="large">
97
+ <Row px="medium" py="small" align="center">
98
+ <ShowMenuIcon />
143
99
  <Spacer.Horizontal size="small" />
144
- <Text size="large" color="default">
145
- Three-finger long-press
146
- </Text>
100
+ <Text size="large">Show menu at launch</Text>
147
101
  <Spacer.Horizontal />
148
- {touchGestureEnabled && <CheckIcon />}
102
+ <Switch
103
+ accessibilityRole="switch"
104
+ accessibilityLabel="Toggle showing menu at launch"
105
+ value={showsAtLaunch}
106
+ onValueChange={() => setShowsAtLaunch(!showsAtLaunch)}
107
+ />
149
108
  </Row>
150
- </Button.ScaleOnPressContainer>
151
- </View>
152
-
153
- <View padding="small">
154
- <Text color="secondary" size="small" leading="large">
155
- Selected gestures will toggle the developer menu while inside a preview. The menu allows
156
- you to reload or return to home and exposes developer tools.
157
- </Text>
158
- </View>
109
+ </View>
159
110
 
160
- <Spacer.Vertical size="medium" />
111
+ <Spacer.Vertical size="large" />
161
112
 
162
- <View rounded="large" overflow="hidden">
163
- <Row px="medium" py="small" align="center" bg="default">
164
- <Text>Version</Text>
165
- <Spacer.Horizontal />
166
- <Text>{buildInfo?.appVersion}</Text>
167
- </Row>
113
+ <View padding="medium">
114
+ <Heading color="secondary">Menu gestures</Heading>
115
+ </View>
168
116
 
169
- {Boolean(buildInfo.runtimeVersion) && (
170
- <>
171
- <Divider />
117
+ <View>
118
+ <Button.ScaleOnPressContainer
119
+ bg="default"
120
+ roundedTop="large"
121
+ roundedBottom="none"
122
+ onPress={() => setMotionGestureEnabled(!motionGestureEnabled)}
123
+ accessibilityState={{ checked: motionGestureEnabled }}>
172
124
  <Row px="medium" py="small" align="center" bg="default">
173
- <Text>Runtime Version</Text>
125
+ <ShakeDeviceIcon />
126
+ <Spacer.Horizontal size="small" />
127
+ <Text size="large" color="default">
128
+ Shake device
129
+ </Text>
174
130
  <Spacer.Horizontal />
175
- <Text>{buildInfo.runtimeVersion}</Text>
131
+ {motionGestureEnabled && <CheckIcon />}
176
132
  </Row>
177
- </>
178
- )}
179
-
180
- {Boolean(buildInfo.sdkVersion) && !buildInfo.runtimeVersion && (
181
- <>
182
- <Divider />
183
- <Row px="medium" py="small" align="center" bg="default">
184
- <Text>SDK Version</Text>
133
+ </Button.ScaleOnPressContainer>
134
+
135
+ <Divider />
136
+
137
+ <Button.ScaleOnPressContainer
138
+ bg="default"
139
+ roundedBottom="large"
140
+ roundedTop="none"
141
+ onPress={() => setTouchGestureEnabled(!touchGestureEnabled)}
142
+ accessibilityState={{ checked: touchGestureEnabled }}>
143
+ <Row px="medium" py="small" bg="default">
144
+ <ThreeFingerPressIcon />
145
+ <Spacer.Horizontal size="small" />
146
+ <Text size="large" color="default">
147
+ Three-finger long-press
148
+ </Text>
185
149
  <Spacer.Horizontal />
186
- <Text>{buildInfo.sdkVersion}</Text>
150
+ {touchGestureEnabled && <CheckIcon />}
187
151
  </Row>
188
- </>
189
- )}
152
+ </Button.ScaleOnPressContainer>
153
+ </View>
154
+
155
+ <View padding="small">
156
+ <Text color="secondary" size="small" leading="large">
157
+ Selected gestures will toggle the developer menu while inside a preview. The menu
158
+ allows you to reload or return to home and exposes developer tools.
159
+ </Text>
160
+ </View>
190
161
 
191
- <Divider />
162
+ <Spacer.Vertical size="medium" />
192
163
 
193
- <Button.ScaleOnPressContainer
194
- onPress={onCopyPress}
195
- disabled={hasCopiedContent}
196
- bg="default"
197
- roundedTop="none"
198
- roundedBottom="large">
164
+ <View rounded="large" overflow="hidden">
199
165
  <Row px="medium" py="small" align="center" bg="default">
200
- <Text color="link" size="medium">
201
- {hasCopiedContent ? 'Copied to clipboard!' : 'Tap to Copy All'}
202
- </Text>
166
+ <Text>Version</Text>
167
+ <Spacer.Horizontal />
168
+ <Text>{buildInfo?.appVersion}</Text>
203
169
  </Row>
204
- </Button.ScaleOnPressContainer>
205
- {userData?.isExpoAdmin && (
206
- <>
207
- <Spacer.Vertical size="medium" />
208
- <DebugSettings />
209
- <Spacer.Vertical size="medium" />
210
- <UpdatesDebugSettings />
211
- </>
212
- )}
170
+
171
+ {Boolean(buildInfo.runtimeVersion) && (
172
+ <>
173
+ <Divider />
174
+ <Row px="medium" py="small" align="center" bg="default">
175
+ <Text>Runtime Version</Text>
176
+ <Spacer.Horizontal />
177
+ <Text>{buildInfo.runtimeVersion}</Text>
178
+ </Row>
179
+ </>
180
+ )}
181
+
182
+ {Boolean(buildInfo.sdkVersion) && !buildInfo.runtimeVersion && (
183
+ <>
184
+ <Divider />
185
+ <Row px="medium" py="small" align="center" bg="default">
186
+ <Text>SDK Version</Text>
187
+ <Spacer.Horizontal />
188
+ <Text>{buildInfo.sdkVersion}</Text>
189
+ </Row>
190
+ </>
191
+ )}
192
+
193
+ <Divider />
194
+
195
+ <Button.ScaleOnPressContainer
196
+ onPress={onCopyPress}
197
+ disabled={hasCopiedContent}
198
+ bg="default"
199
+ roundedTop="none"
200
+ roundedBottom="large">
201
+ <Row px="medium" py="small" align="center" bg="default">
202
+ <Text color="link" size="medium">
203
+ {hasCopiedContent ? 'Copied to clipboard!' : 'Tap to Copy All'}
204
+ </Text>
205
+ </Row>
206
+ </Button.ScaleOnPressContainer>
207
+ {userData?.isExpoAdmin && (
208
+ <>
209
+ <Spacer.Vertical size="medium" />
210
+ <DebugSettings />
211
+ <Spacer.Vertical size="medium" />
212
+ <UpdatesDebugSettings />
213
+ </>
214
+ )}
215
+ </View>
213
216
  </View>
214
- </View>
217
+ </ScreenContainer>
215
218
  </ScrollView>
216
219
  );
217
220
  }
@@ -19,6 +19,7 @@ import { ScrollView } from 'react-native-gesture-handler';
19
19
  import { BasicButton } from '../components/BasicButton';
20
20
  import { EASUpdateRow } from '../components/EASUpdatesRows';
21
21
  import { FlatList } from '../components/FlatList';
22
+ import { ScreenContainer } from '../components/ScreenContainer';
22
23
  import { useOnUpdatePress } from '../hooks/useOnUpdatePress';
23
24
  import { useUpdatesConfig } from '../providers/UpdatesConfigProvider';
24
25
  import { useChannelsForApp } from '../queries/useChannelsForApp';
@@ -89,29 +90,31 @@ export function UpdatesScreen({ route }: UpdatesScreenProps) {
89
90
 
90
91
  function EmptyList() {
91
92
  return (
92
- <View mt="large" mx="medium" bg="default" rounded="large" padding="medium">
93
- <View>
94
- <Heading>There are no updates available for this branch.</Heading>
95
- <Spacer.Vertical size="small" />
96
- <Text color="secondary" size="small">
97
- Updates allow you to deliver code directly to your users.
98
- </Text>
93
+ <ScreenContainer>
94
+ <View mt="large" mx="medium" bg="default" rounded="large" padding="medium">
95
+ <View>
96
+ <Heading>There are no updates available for this branch.</Heading>
97
+ <Spacer.Vertical size="small" />
98
+ <Text color="secondary" size="small">
99
+ Updates allow you to deliver code directly to your users.
100
+ </Text>
99
101
 
100
- <View py="medium" align="centered">
101
- <Button.ScaleOnPressContainer
102
- bg="tertiary"
103
- onPress={() => {
104
- Linking.openURL(`https://docs.expo.dev/eas-update/how-eas-update-works/`);
105
- }}>
106
- <View px="2.5" py="2">
107
- <Button.Text weight="medium" color="tertiary">
108
- Publish an update
109
- </Button.Text>
110
- </View>
111
- </Button.ScaleOnPressContainer>
102
+ <View py="medium" align="centered">
103
+ <Button.ScaleOnPressContainer
104
+ bg="tertiary"
105
+ onPress={() => {
106
+ Linking.openURL(`https://docs.expo.dev/eas-update/how-eas-update-works/`);
107
+ }}>
108
+ <View px="2.5" py="2">
109
+ <Button.Text weight="medium" color="tertiary">
110
+ Publish an update
111
+ </Button.Text>
112
+ </View>
113
+ </Button.ScaleOnPressContainer>
114
+ </View>
112
115
  </View>
113
116
  </View>
114
- </View>
117
+ </ScreenContainer>
115
118
  );
116
119
  }
117
120
 
@@ -137,25 +140,27 @@ export function UpdatesScreen({ route }: UpdatesScreenProps) {
137
140
  }
138
141
 
139
142
  return (
140
- <View flex="1">
141
- <BranchDetailsHeader
142
- branchName={branchName}
143
- updates={updates}
144
- onOpenPress={() => onUpdatePress(updates[0])}
145
- />
146
- <FlatList
147
- isLoading={isLoading}
148
- isRefreshing={isRefreshing}
149
- onRefresh={() => refetch()}
150
- ListHeaderComponent={Header}
151
- data={updates}
152
- extraData={{ length: updates.length, loadingUpdateId }}
153
- renderItem={renderUpdate}
154
- keyExtractor={(item) => item.id}
155
- ListFooterComponent={Footer}
156
- ListEmptyComponent={EmptyList}
157
- />
158
- </View>
143
+ <ScreenContainer>
144
+ <View flex="1">
145
+ <BranchDetailsHeader
146
+ branchName={branchName}
147
+ updates={updates}
148
+ onOpenPress={() => onUpdatePress(updates[0])}
149
+ />
150
+ <FlatList
151
+ isLoading={isLoading}
152
+ isRefreshing={isRefreshing}
153
+ onRefresh={() => refetch()}
154
+ ListHeaderComponent={Header}
155
+ data={updates}
156
+ extraData={{ length: updates.length, loadingUpdateId }}
157
+ renderItem={renderUpdate}
158
+ keyExtractor={(item) => item.id}
159
+ ListFooterComponent={Footer}
160
+ ListEmptyComponent={EmptyList}
161
+ />
162
+ </View>
163
+ </ScreenContainer>
159
164
  );
160
165
  }
161
166
 
@@ -404,6 +404,9 @@
404
404
  projectUrl = expoUrl;
405
405
  }
406
406
 
407
+ // Disable onboarding popup if "&disableOnboarding=1" is a param
408
+ [EXDevLauncherURLHelper disableOnboardingPopupIfNeeded:expoUrl];
409
+
407
410
  NSString *installationID = [_installationIDHelper getOrCreateInstallationID];
408
411
 
409
412
  NSDictionary *updatesConfiguration = [EXDevLauncherUpdatesHelper createUpdatesConfigurationWithURL:expoUrl
@@ -1,21 +1,22 @@
1
- // Copyright 2015-present 650 Industries. All rights reserved.
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
2
 
3
3
  import Foundation
4
+ import EXDevMenu
4
5
 
5
6
  @objc
6
7
  public class EXDevLauncherUrl: NSObject {
7
8
  @objc
8
9
  public var url: URL
9
-
10
+
10
11
  @objc
11
12
  public var queryParams: [String: String]
12
-
13
+
13
14
  @objc
14
15
  public init(_ url: URL) {
15
16
  self.queryParams = EXDevLauncherURLHelper.getQueryParamsForUrl(url)
16
17
  self.url = url
17
-
18
- if (EXDevLauncherURLHelper.isDevLauncherURL(url)) {
18
+
19
+ if EXDevLauncherURLHelper.isDevLauncherURL(url) {
19
20
  if let urlParam = self.queryParams["url"] {
20
21
  if let urlFromParam = URL.init(string: urlParam) {
21
22
  self.url = EXDevLauncherURLHelper.replaceEXPScheme(urlFromParam, to: "http")
@@ -24,7 +25,7 @@ public class EXDevLauncherUrl: NSObject {
24
25
  } else {
25
26
  self.url = EXDevLauncherURLHelper.replaceEXPScheme(self.url, to: "http")
26
27
  }
27
-
28
+
28
29
  super.init()
29
30
  }
30
31
  }
@@ -35,41 +36,51 @@ public class EXDevLauncherURLHelper: NSObject {
35
36
  public static func isDevLauncherURL(_ url: URL?) -> Bool {
36
37
  return url?.host == "expo-development-client"
37
38
  }
38
-
39
+
39
40
  @objc
40
41
  public static func hasUrlQueryParam(_ url: URL) -> Bool {
41
42
  var hasUrlQueryParam = false
42
-
43
+
43
44
  let components = URLComponents.init(url: url, resolvingAgainstBaseURL: false)
44
-
45
- for queryItem in components?.queryItems ?? [] {
46
- if queryItem.name == "url" && queryItem.value != nil {
47
- hasUrlQueryParam = true
48
- break
49
- }
45
+
46
+ if ((components?.queryItems?.contains(where: {
47
+ $0.name == "url" && $0.value != nil
48
+ })) ?? false) {
49
+ hasUrlQueryParam = true
50
50
  }
51
-
51
+
52
52
  return hasUrlQueryParam
53
53
  }
54
54
 
55
+ @objc
56
+ public static func disableOnboardingPopupIfNeeded(_ url: URL) {
57
+ let components = URLComponents.init(url: url, resolvingAgainstBaseURL: false)
58
+
59
+ if ((components?.queryItems?.contains(where: {
60
+ $0.name == "disableOnboarding" && ($0.value ?? "") == "1"
61
+ })) ?? false) {
62
+ DevMenuPreferences.isOnboardingFinished = true
63
+ }
64
+ }
65
+
55
66
  @objc
56
67
  public static func replaceEXPScheme(_ url: URL, to scheme: String) -> URL {
57
68
  var components = URLComponents.init(url: url, resolvingAgainstBaseURL: false)!
58
- if (components.scheme == "exp") {
69
+ if components.scheme == "exp" {
59
70
  components.scheme = scheme
60
71
  }
61
72
  return components.url!
62
73
  }
63
-
74
+
64
75
  @objc
65
76
  public static func getQueryParamsForUrl(_ url: URL) -> [String: String] {
66
77
  let components = URLComponents.init(url: url, resolvingAgainstBaseURL: false)
67
78
  var dict: [String: String] = [:]
68
-
79
+
69
80
  for parameter in components?.queryItems ?? [] {
70
81
  dict[parameter.name] = parameter.value?.removingPercentEncoding ?? ""
71
82
  }
72
-
83
+
73
84
  return dict
74
85
  }
75
86
  }
@@ -40,7 +40,7 @@ public class EXDevLauncherUncaughtExceptionHandler: NSObject {
40
40
  static func tryToSendExceptionToBundler(_ exception: NSException) {
41
41
  let controller = EXDevLauncherController.sharedInstance()
42
42
  if (controller.isAppRunning()) {
43
- guard let url = getLogsUrl(controller) else {
43
+ guard let url = getWebSocketUrl(controller) else {
44
44
  return
45
45
  }
46
46
 
@@ -50,18 +50,22 @@ public class EXDevLauncherUncaughtExceptionHandler: NSObject {
50
50
  logsManager.sendSync()
51
51
  }
52
52
  }
53
-
54
- static func getLogsUrl(_ controller: EXDevLauncherController) -> URL? {
55
- let logsUrlFromManifest = controller.appManifest()?.logUrl()
56
- if (logsUrlFromManifest != nil) {
57
- return URL.init(string: logsUrlFromManifest!)
58
- }
59
-
53
+
54
+ static func getWebSocketUrl(_ controller: EXDevLauncherController) -> URL? {
55
+ // URL structure replicates
56
+ // https://github.com/facebook/react-native/blob/0.69-stable/Libraries/Utilities/HMRClient.js#L164
57
+ // but URLSessionWebSocketTask will crash if the scheme is not `ws` or `wss`
60
58
  guard let appUrl = controller.appBridge?.bundleURL else {
61
59
  return nil
62
60
  }
63
-
64
- return URL.init(string: "logs", relativeTo: appUrl)
61
+ guard let socketUrl = URL.init(string: "hot", relativeTo: appUrl) else {
62
+ return nil
63
+ }
64
+ guard var components = URLComponents(url: socketUrl, resolvingAgainstBaseURL: true) else {
65
+ return nil
66
+ }
67
+ components.scheme = "ws"
68
+ return components.url
65
69
  }
66
70
 
67
71
  static func tryToSaveException(_ exception: NSException) {
@@ -1,52 +1,57 @@
1
1
  import Foundation
2
2
 
3
3
  class EXDevLauncherRemoteLogsManager {
4
- private var batch: [[String: Any]] = []
4
+ private var batch: [String] = []
5
5
  private let url: URL
6
-
6
+
7
7
  init(withUrl url: URL) {
8
8
  self.url = url
9
9
  }
10
-
10
+
11
11
  func deferError(exception: NSException) {
12
- batch.append([
13
- "level": "error",
14
- "body": [
15
- "message": exception.description,
16
- "stack": exception.callStackSymbols.joined(separator: "\n")
17
- ],
18
- "includesStack": true
19
- ])
12
+ batch.append("\(exception.name.rawValue): \(exception.reason ?? exception.description)")
13
+ batch.append(" \(exception.callStackSymbols.joined(separator: "\n "))")
20
14
  }
21
-
15
+
22
16
  func deferError(message: String) {
23
- batch.append([
24
- "level": "error",
25
- "body": message,
26
- "includesStack": false
27
- ])
17
+ batch.append(message)
28
18
  }
29
-
19
+
30
20
  func sendSync() {
31
- guard let data = try? JSONSerialization.data(withJSONObject: batch, options: []) else {
21
+ // message format comes from
22
+ // https://github.com/facebook/react-native/blob/0.69-stable/Libraries/Utilities/HMRClient.js#L119-L134
23
+ let messageJson = [
24
+ "type": "log",
25
+ "level": "error",
26
+ "mode": "BRIDGE",
27
+ // `data` is an array whose members are simply concatenated with a space before printing to
28
+ // the console, so we join messages with a newline and send an array consisting of just a
29
+ // single item.
30
+ "data": [batch.joined(separator: "\n")]
31
+ ] as [String: Any]
32
+ guard let data = try? JSONSerialization.data(withJSONObject: messageJson, options: []) else {
32
33
  batch.removeAll()
33
34
  return
34
35
  }
35
-
36
+
36
37
  batch.removeAll()
37
-
38
- var request = URLRequest.init(url: self.url)
39
- request.httpMethod = "POST"
40
- request.httpBody = data
41
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
42
- request.setValue(UIDevice.current.identifierForVendor?.uuidString ?? UIDevice.current.name , forHTTPHeaderField: "Device-Id")
43
- request.setValue(UIDevice.current.name, forHTTPHeaderField: "Device-Name")
44
-
45
- let group = DispatchGroup()
46
- group.enter()
47
- URLSession.shared.dataTask(with: request) { data, response, error in
48
- group.leave()
49
- }.resume()
50
- group.wait(timeout: DispatchTime.now() + .seconds(2))
38
+
39
+ if #available(iOS 13.0, *) {
40
+ let group = DispatchGroup()
41
+ group.enter()
42
+
43
+ let task = URLSession.shared.webSocketTask(with: self.url)
44
+ task.resume()
45
+
46
+ guard let dataString = String(data: data, encoding: .utf8) else {
47
+ group.leave()
48
+ return
49
+ }
50
+ let message = URLSessionWebSocketTask.Message.string(dataString)
51
+ task.send(message) { _ in
52
+ group.leave()
53
+ }
54
+ _ = group.wait(timeout: DispatchTime.now() + .seconds(2))
55
+ }
51
56
  }
52
57
  }