Skip to content

Commit e085858

Browse files
committed
Use serializations + checkout event handlers in sample app
1 parent 696ccdc commit e085858

4 files changed

Lines changed: 10 additions & 159 deletions

File tree

modules/@shopify/checkout-sheet-kit/ios/ShopifyCheckoutSheetKit.swift

Lines changed: 3 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
6262

6363
func checkoutDidComplete(event: CheckoutCompletedEvent) {
6464
if hasListeners {
65-
self.sendEvent(withName: "completed", body: encodeToJSON(from: event))
65+
self.sendEvent(withName: "completed", body: ShopifyEventSerialization.serialize(checkoutCompletedEvent: event))
6666
}
6767
}
6868

@@ -73,66 +73,12 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
7373
func checkoutDidFail(error: ShopifyCheckoutSheetKit.CheckoutError) {
7474
guard hasListeners else { return }
7575

76-
if case .checkoutExpired(let message, let code, let recoverable) = error {
77-
self.sendEvent(withName: "error", body: [
78-
"__typename": "CheckoutExpiredError",
79-
"message": message,
80-
"code": code.rawValue,
81-
"recoverable": recoverable
82-
])
83-
} else if case .checkoutUnavailable(let message, let code, let recoverable) = error {
84-
switch code {
85-
case .clientError(let clientErrorCode):
86-
self.sendEvent(withName: "error", body: [
87-
"__typename": "CheckoutClientError",
88-
"message": message,
89-
"code": clientErrorCode.rawValue,
90-
"recoverable": recoverable
91-
])
92-
case .httpError(let statusCode):
93-
self.sendEvent(withName: "error", body: [
94-
"__typename": "CheckoutHTTPError",
95-
"message": message,
96-
"code": "http_error",
97-
"statusCode": statusCode,
98-
"recoverable": recoverable
99-
])
100-
}
101-
} else if case .configurationError(let message, let code, let recoverable) = error {
102-
self.sendEvent(withName: "error", body: [
103-
"__typename": "ConfigurationError",
104-
"message": message,
105-
"code": code.rawValue,
106-
"recoverable": recoverable
107-
])
108-
} else if case .sdkError(let underlying, let recoverable) = error {
109-
var errorMessage = "\(underlying.localizedDescription)"
110-
self.sendEvent(withName: "error", body: [
111-
"__typename": "InternalError",
112-
"code": "unknown",
113-
"message": errorMessage,
114-
"recoverable": recoverable
115-
])
116-
} else {
117-
self.sendEvent(withName: "error", body: [
118-
"__typename": "UnknownError",
119-
"code": "unknown",
120-
"message": error.localizedDescription,
121-
"recoverable": error.isRecoverable
122-
])
123-
}
76+
self.sendEvent(withName: "error", body: ShopifyEventSerialization.serialize(checkoutError: error))
12477
}
12578

12679
func checkoutDidEmitWebPixelEvent(event: ShopifyCheckoutSheetKit.PixelEvent) {
12780
if hasListeners {
128-
var genericEvent: [String: Any]
129-
switch event {
130-
case .standardEvent(let standardEvent):
131-
genericEvent = mapToGenericEvent(standardEvent: standardEvent)
132-
case .customEvent(let customEvent):
133-
genericEvent = mapToGenericEvent(customEvent: customEvent)
134-
}
135-
self.sendEvent(withName: "pixel", body: genericEvent)
81+
self.sendEvent(withName: "pixel", body: ShopifyEventSerialization.serialize(pixelEvent: event))
13682
}
13783
}
13884

@@ -257,87 +203,4 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
257203
}
258204

259205
// MARK: - Private
260-
261-
private func stringToJSON(from value: String?) -> [String: Any]? {
262-
guard let data = value?.data(using: .utf8, allowLossyConversion: false) else { return [:] }
263-
do {
264-
return try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]
265-
} catch {
266-
print("Failed to convert string to JSON: \(error)", value)
267-
return [:]
268-
}
269-
}
270-
271-
private func encodeToJSON(from value: Codable) -> [String: Any] {
272-
let encoder = JSONEncoder()
273-
274-
do {
275-
let jsonData = try encoder.encode(value)
276-
if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
277-
return jsonObject
278-
}
279-
} catch {
280-
print("Error encoding to JSON object: \(error)")
281-
}
282-
return [:]
283-
}
284-
285-
private func mapToGenericEvent(standardEvent: StandardEvent) -> [String: Any] {
286-
let encoded = encodeToJSON(from: standardEvent)
287-
return [
288-
"context": encoded["context"],
289-
"data": encoded["data"],
290-
"id": encoded["id"],
291-
"name": encoded["name"],
292-
"timestamp": encoded["timestamp"],
293-
"type": "STANDARD"
294-
] as [String: Any]
295-
}
296-
297-
private func mapToGenericEvent(customEvent: CustomEvent) -> [String: Any] {
298-
do {
299-
return try decodeAndMap(event: customEvent)
300-
} catch {
301-
print("[debug] Failed to map custom event: \(error)")
302-
}
303-
304-
return [:]
305-
}
306-
307-
private func decodeAndMap(event: CustomEvent, decoder: JSONDecoder = JSONDecoder()) throws -> [String: Any] {
308-
return [
309-
"context": encodeToJSON(from: event.context),
310-
"customData": stringToJSON(from: event.customData),
311-
"id": event.id,
312-
"name": event.name,
313-
"timestamp": event.timestamp,
314-
"type": "CUSTOM"
315-
] as [String: Any]
316-
}
317-
}
318-
319-
extension UIColor {
320-
convenience init(hex: String) {
321-
let hexString: String = hex.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
322-
let start = hexString.index(hexString.startIndex, offsetBy: hexString.hasPrefix("#") ? 1 : 0)
323-
let hexColor = String(hexString[start...])
324-
325-
let scanner = Scanner(string: hexColor)
326-
var hexNumber: UInt64 = 0
327-
328-
if scanner.scanHexInt64(&hexNumber) {
329-
let red = (hexNumber & 0xff0000) >> 16
330-
let green = (hexNumber & 0x00ff00) >> 8
331-
let blue = hexNumber & 0x0000ff
332-
333-
self.init(
334-
red: CGFloat(red) / 0xff,
335-
green: CGFloat(green) / 0xff,
336-
blue: CGFloat(blue) / 0xff,
337-
alpha: 1
338-
)
339-
} else {
340-
self.init(red: 0, green: 0, blue: 0, alpha: 1)
341-
}
342-
}
343206
}

modules/@shopify/checkout-sheet-kit/tests/linking.test.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,6 @@ jest.mock('react-native', () => ({
1111
Platform: {
1212
OS: 'ios',
1313
},
14-
requireNativeComponent: jest.fn().mockImplementation(() => {
15-
const mockComponent = (props: any) => {
16-
// Use React.createElement with plain object instead
17-
const mockReact = jest.requireActual('react');
18-
return mockReact.createElement('View', props);
19-
};
20-
return mockComponent;
21-
}),
2214
}));
2315

2416
describe('Native Module Linking', () => {

sample/src/App.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import CartScreen from './screens/CartScreen';
5757
import ProductDetailsScreen from './screens/ProductDetailsScreen';
5858
import type {ProductVariant, ShopifyProduct} from '../@types';
5959
import ErrorBoundary from './ErrorBoundary';
60+
import {useShopifyEventHandlers} from './hooks/useCheckoutEventHandlers';
6061

6162
const colorScheme = ColorScheme.web;
6263

@@ -163,28 +164,28 @@ class StorefrontURL {
163164

164165
function AppWithContext({children}: PropsWithChildren) {
165166
const shopify = useShopifyCheckoutSheet();
167+
const eventHandlers = useShopifyEventHandlers('App');
166168

167169
useEffect(() => {
168170
const close = shopify.addEventListener('close', () => {
169-
console.log('[CheckoutClose]');
171+
eventHandlers.onCancel?.();
170172
});
171173

172174
const pixel = shopify.addEventListener('pixel', (event: PixelEvent) => {
173-
console.log('[CheckoutPixelEvent]', event.name, event);
175+
eventHandlers.onWebPixelEvent?.(event);
174176
});
175177

176178
const completed = shopify.addEventListener(
177179
'completed',
178180
(event: CheckoutCompletedEvent) => {
179-
console.log('[CheckoutCompletedEvent]', event.orderDetails.id);
180-
console.log('[CheckoutCompletedEvent]', event);
181+
eventHandlers.onComplete?.(event);
181182
},
182183
);
183184

184185
const error = shopify.addEventListener(
185186
'error',
186187
(error: CheckoutException) => {
187-
console.log(error.constructor.name, error);
188+
eventHandlers.onFail?.(error);
188189
},
189190
);
190191

@@ -194,7 +195,7 @@ function AppWithContext({children}: PropsWithChildren) {
194195
close?.remove();
195196
error?.remove();
196197
};
197-
}, [shopify]);
198+
}, [shopify, eventHandlers]);
198199

199200
return (
200201
<ConfigProvider>

sample/src/hooks/useCheckoutEventHandlers.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type {
77
CheckoutCompletedEvent,
88
CheckoutException,
99
PixelEvent,
10-
RenderState,
1110
} from '@shopify/checkout-sheet-kit';
1211
import {Linking} from 'react-native';
1312

@@ -16,7 +15,6 @@ interface EventHandlers {
1615
onFail?: (error: CheckoutException) => void;
1716
onComplete?: (event: CheckoutCompletedEvent) => void;
1817
onCancel?: () => void;
19-
onRenderStateChange?: (state: RenderState) => void;
2018
onShouldRecoverFromError?: (error: {message: string}) => boolean;
2119
onWebPixelEvent?: (event: PixelEvent) => void;
2220
onClickLink?: (url: string) => void;
@@ -41,9 +39,6 @@ export function useShopifyEventHandlers(name?: string): EventHandlers {
4139
onCancel: () => {
4240
log('onCancel');
4341
},
44-
onRenderStateChange: state => {
45-
log('onRenderStateChange', state);
46-
},
4742
onWebPixelEvent: event => {
4843
log('onWebPixelEvent', event.name);
4944
},

0 commit comments

Comments
 (0)