Skip to content

Commit 8c61152

Browse files
committed
Improve configuration + add tests
1 parent c167134 commit 8c61152

14 files changed

Lines changed: 687 additions & 170 deletions

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import PassKit
3434
class AcceleratedCheckoutConfiguration {
3535
static let shared = AcceleratedCheckoutConfiguration()
3636
var configuration: ShopifyAcceleratedCheckouts.Configuration?
37-
var wallets: [String] = ["shopPay", "applePay"]
37+
var wallets: [Wallet] = [Wallet.shopPay, Wallet.applePay]
3838

3939
private init() {
4040
setupApplePay()
@@ -238,9 +238,8 @@ class RCTAcceleratedCheckoutButtonsView: UIView {
238238
return
239239
}
240240

241-
// Use wallets from props, or fall back to shared configuration, or default to both
242-
let walletsToUse = wallets ?? AcceleratedCheckoutConfiguration.shared.wallets
243-
let shopifyWallets = convertToShopifyWallets(walletsToUse)
241+
// Use wallets from props, or fallback to default
242+
let shopifyWallets = wallets.map(convertToShopifyWallets) ?? AcceleratedCheckoutConfiguration.shared.wallets
244243

245244
// Create Apple Pay configuration
246245
let applePayConfig = ShopifyAcceleratedCheckouts.ApplePayConfiguration(
@@ -321,17 +320,15 @@ class RCTAcceleratedCheckoutButtonsView: UIView {
321320
}
322321

323322
private func handleRenderStateChange(_ state: RenderState) {
324-
onRenderStateChange?(["state": ShopifyEventSerialization.serialize(renderState: state)])
323+
onRenderStateChange?(ShopifyEventSerialization.serialize(renderState: state))
325324
}
326325

327326
private func handleWebPixelEvent(_ event: PixelEvent) {
328327
onWebPixelEvent?(ShopifyEventSerialization.serialize(pixelEvent: event))
329328
}
330329

331330
private func handleClickLink(_ url: URL) {
332-
onClickLink?([
333-
"url": url.absoluteString
334-
])
331+
onClickLink?(ShopifyEventSerialization.serialize(clickEvent: url))
335332
}
336333

337334
override func layoutSubviews() {

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ class ShopifyEventSerialization {
9595
}
9696
}
9797

98+
static func serialize(clickEvent url: URL) -> [String: URL] {
99+
return ["url": url]
100+
}
101+
98102
/**
99103
* Converts a CheckoutError to a React Native compatible dictionary.
100104
* Handles all specific error types with proper type information.
@@ -157,16 +161,16 @@ class ShopifyEventSerialization {
157161
/**
158162
* Converts a RenderState enum to a string for React Native.
159163
*/
160-
static func serialize(renderState state: RenderState) -> String {
164+
static func serialize(renderState state: RenderState) -> [String: String] {
161165
switch state {
162166
case .loading:
163-
return "loading"
167+
return ["state": "loading"]
164168
case .rendered:
165-
return "rendered"
169+
return ["state": "rendered"]
166170
case .error:
167-
return "error"
171+
return ["state": "error"]
168172
@unknown default:
169-
return "unknown"
173+
return ["state": "unknown"]
170174
}
171175
}
172176
}

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

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,17 @@ @interface RCT_EXTERN_MODULE (RCTShopifyCheckoutSheetKit, NSObject)
4747
resolve reject : (RCTPromiseRejectBlock)reject)
4848

4949
/// Configure AcceleratedCheckouts
50-
RCT_EXTERN_METHOD(configureAcceleratedCheckouts : (NSString *)
51-
storefrontDomain storefrontAccessToken : (NSString *)
52-
storefrontAccessToken customerEmail : (NSString *)
53-
customerEmail customerPhoneNumber : (NSString *)
54-
customerPhoneNumber);
50+
RCT_EXTERN_METHOD(
51+
configureAcceleratedCheckouts : (NSString *)
52+
storefrontDomain storefrontAccessToken : (NSString *)
53+
storefrontAccessToken customerEmail : (NSString *)
54+
customerEmail customerPhoneNumber : (NSString *)
55+
customerPhoneNumber resolve : (RCTPromiseResolveBlock)
56+
resolve reject : (RCTPromiseRejectBlock)reject);
5557

5658
/// Check if accelerated checkout is available
57-
RCT_EXTERN_METHOD(
58-
isAcceleratedCheckoutAvailable : (NSString *)cartId variantId : (NSString *)
59-
variantId quantity : (nonnull NSNumber *)
60-
quantity resolve : (RCTPromiseResolveBlock)
61-
resolve reject : (RCTPromiseRejectBlock)reject);
59+
RCT_EXTERN_METHOD(isAcceleratedCheckoutAvailable : (RCTPromiseResolveBlock)
60+
resolve reject : (RCTPromiseRejectBlock)reject);
6261

6362
@end
6463

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

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -205,61 +205,48 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
205205
resolve(config)
206206
}
207207

208-
@objc func configureAcceleratedCheckouts(
209-
_ storefrontDomain: String,
210-
storefrontAccessToken: String,
211-
customerEmail: String?,
212-
customerPhoneNumber: String?,
213-
) {
214-
if #available(iOS 17.0, *) {
215-
let customer = ShopifyAcceleratedCheckouts.Customer(
216-
email: customerEmail,
217-
phoneNumber: customerPhoneNumber
218-
)
219-
220-
acceleratedCheckoutsConfiguration = ShopifyAcceleratedCheckouts.Configuration(
221-
storefrontDomain: storefrontDomain,
222-
storefrontAccessToken: storefrontAccessToken,
223-
customer: customer
224-
)
225-
226-
// Update the shared configuration for the UI components
227-
AcceleratedCheckoutConfiguration.shared.configuration = acceleratedCheckoutsConfiguration as? ShopifyAcceleratedCheckouts.Configuration
228-
229-
// Notify all button views to update with the new configuration
230-
NotificationCenter.default.post(name: Notification.Name("AcceleratedCheckoutConfigurationUpdated"), object: nil)
231-
}
232-
}
233-
234-
@objc func isAcceleratedCheckoutAvailable(
235-
_ cartId: String?,
236-
variantId: String?,
237-
quantity: NSNumber,
238-
resolve: @escaping RCTPromiseResolveBlock,
239-
reject: @escaping RCTPromiseRejectBlock
240-
) {
241-
guard #available(iOS 17.0, *) else {
242-
reject("UNAVAILABLE", "AcceleratedCheckouts requires iOS 17.0+", nil)
243-
return
244-
}
245-
246-
guard let config = acceleratedCheckoutsConfiguration as? ShopifyAcceleratedCheckouts.Configuration else {
247-
reject("CONFIG_ERROR", "AcceleratedCheckouts configuration not set", nil)
248-
return
249-
}
250-
251-
Task {
252-
do {
253-
// For now, assume accelerated checkouts are available if configuration is set
254-
// Future enhancement: implement actual availability checking based on shop settings
255-
let available = true
208+
@objc func configureAcceleratedCheckouts(
209+
_ storefrontDomain: String,
210+
storefrontAccessToken: String,
211+
customerEmail: String?,
212+
customerPhoneNumber: String?,
213+
resolve: @escaping RCTPromiseResolveBlock,
214+
reject: @escaping RCTPromiseRejectBlock
215+
) {
216+
if #available(iOS 17.0, *) {
217+
let customer = ShopifyAcceleratedCheckouts.Customer(
218+
email: customerEmail,
219+
phoneNumber: customerPhoneNumber
220+
)
221+
222+
acceleratedCheckoutsConfiguration = ShopifyAcceleratedCheckouts.Configuration(
223+
storefrontDomain: storefrontDomain,
224+
storefrontAccessToken: storefrontAccessToken,
225+
customer: customer
226+
)
227+
228+
AcceleratedCheckoutConfiguration.shared.configuration = acceleratedCheckoutsConfiguration as? ShopifyAcceleratedCheckouts.Configuration
229+
230+
NotificationCenter.default.post(name: Notification.Name("AcceleratedCheckoutConfigurationUpdated"), object: nil)
231+
232+
resolve(true)
233+
} else {
234+
resolve(false)
235+
}
236+
}
256237

257-
resolve(available)
258-
} catch {
259-
reject("AVAILABILITY_ERROR", error.localizedDescription, error)
260-
}
261-
}
262-
}
238+
@objc func isAcceleratedCheckoutAvailable(
239+
_ resolve: @escaping RCTPromiseResolveBlock,
240+
reject: @escaping RCTPromiseRejectBlock
241+
) {
242+
guard #available(iOS 17.0, *) else {
243+
resolve(false)
244+
return
245+
}
246+
247+
let isConfigured = (acceleratedCheckoutsConfiguration as? ShopifyAcceleratedCheckouts.Configuration) != nil
248+
resolve(isConfigured)
249+
}
263250

264251
// MARK: - Private
265252
}

modules/@shopify/checkout-sheet-kit/src/context.tsx

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2222
*/
2323

24-
import React, {useCallback, useMemo, useRef} from 'react';
24+
import React, {useCallback, useMemo, useRef, useEffect, useState} from 'react';
2525
import type {PropsWithChildren} from 'react';
2626
import {type EmitterSubscription} from 'react-native';
2727
import {ShopifyCheckoutSheet} from './index';
@@ -31,12 +31,12 @@ import type {
3131
RemoveEventListeners,
3232
CheckoutEvent,
3333
Configuration,
34-
AcceleratedCheckoutConfiguration,
3534
} from './index.d';
3635

3736
type Maybe<T> = T | undefined;
3837

3938
interface Context {
39+
acceleratedCheckoutsAvailable: boolean;
4040
addEventListener: AddEventListener;
4141
getConfig: () => Promise<Configuration | undefined>;
4242
setConfig: (config: Configuration) => void;
@@ -46,19 +46,12 @@ interface Context {
4646
dismiss: () => void;
4747
invalidate: () => void;
4848
version: Maybe<string>;
49-
configureAcceleratedCheckouts: (
50-
config: AcceleratedCheckoutConfiguration,
51-
) => void;
52-
isAcceleratedCheckoutAvailable: (options: {
53-
cartId?: string;
54-
variantId?: string;
55-
quantity?: number;
56-
}) => Promise<boolean>;
5749
}
5850

5951
const noop = () => undefined;
6052

6153
const ShopifyCheckoutSheetContext = React.createContext<Context>({
54+
acceleratedCheckoutsAvailable: false,
6255
addEventListener: noop,
6356
removeEventListeners: noop,
6457
setConfig: noop,
@@ -68,8 +61,6 @@ const ShopifyCheckoutSheetContext = React.createContext<Context>({
6861
invalidate: noop,
6962
dismiss: noop,
7063
version: undefined,
71-
configureAcceleratedCheckouts: noop,
72-
isAcceleratedCheckoutAvailable: async () => false,
7364
});
7465

7566
interface Props {
@@ -82,12 +73,32 @@ export function ShopifyCheckoutSheetProvider({
8273
configuration,
8374
children,
8475
}: PropsWithChildren<Props>) {
76+
const [acceleratedCheckoutsAvailable, setAcceleratedCheckoutsAvailable] =
77+
useState(false);
8578
const instance = useRef<ShopifyCheckoutSheet | null>(null);
8679

8780
if (!instance.current) {
8881
instance.current = new ShopifyCheckoutSheet(configuration, features);
8982
}
9083

84+
useEffect(() => {
85+
async function configureAcceleratedCheckouts() {
86+
if (!instance.current || !configuration) {
87+
return;
88+
}
89+
90+
if (configuration.acceleratedCheckouts) {
91+
setAcceleratedCheckoutsAvailable(
92+
await instance.current?.configureAcceleratedCheckouts(
93+
configuration.acceleratedCheckouts,
94+
),
95+
);
96+
}
97+
}
98+
99+
configureAcceleratedCheckouts();
100+
}, [configuration]);
101+
91102
const addEventListener: AddEventListener = useCallback(
92103
(eventName, callback): EmitterSubscription | undefined => {
93104
return instance.current?.addEventListener(eventName, callback);
@@ -127,29 +138,9 @@ export function ShopifyCheckoutSheetProvider({
127138
return instance.current?.getConfig();
128139
}, []);
129140

130-
const configureAcceleratedCheckouts = useCallback(
131-
(config: AcceleratedCheckoutConfiguration) => {
132-
instance.current?.configureAcceleratedCheckouts(config);
133-
},
134-
[],
135-
);
136-
137-
const isAcceleratedCheckoutAvailable = useCallback(
138-
async (options: {
139-
cartId?: string;
140-
variantId?: string;
141-
quantity?: number;
142-
}) => {
143-
return (
144-
(await instance.current?.isAcceleratedCheckoutAvailable(options)) ??
145-
false
146-
);
147-
},
148-
[],
149-
);
150-
151141
const context = useMemo((): Context => {
152142
return {
143+
acceleratedCheckoutsAvailable,
153144
addEventListener,
154145
dismiss,
155146
setConfig,
@@ -159,10 +150,9 @@ export function ShopifyCheckoutSheetProvider({
159150
invalidate,
160151
removeEventListeners,
161152
version: instance.current?.version,
162-
configureAcceleratedCheckouts,
163-
isAcceleratedCheckoutAvailable,
164153
};
165154
}, [
155+
acceleratedCheckoutsAvailable,
166156
addEventListener,
167157
dismiss,
168158
removeEventListeners,
@@ -171,8 +161,6 @@ export function ShopifyCheckoutSheetProvider({
171161
preload,
172162
present,
173163
invalidate,
174-
configureAcceleratedCheckouts,
175-
isAcceleratedCheckoutAvailable,
176164
]);
177165

178166
return (

0 commit comments

Comments
 (0)