Xcode project built in SwiftUI relating to an eCommerce app showcasing fashion products and enabling users to :
- create an account or login to an existing account
- browse the product catalog
- add/remove items to/from their favorites list
- add/remove items to/from their cart
- proceed with payment
- view orders history
Project using Firebase and Stripe API.
App architecture : Repository Design Pattern
Open the eCommerce.xcodeproj file with Xcode and build the application in the simulator or on a device.
This project uses several libraries provided by Firebase SDK for iOS platforms :
Authentication: see the related section belowHosting: see the related section belowCloud Firestore: products and users data storage through collections and documentsCloud Storage: products images uploadAnalytics: for logging events that provide insight on app usage and user engagementRemote Config: used to change the colors of the app without publishing an app update
This project embeds Stripe iOS SDK and in particular the StripePaymentSheet module which provides a prebuilt payment UI.
The payment method integration uses three Stripe API objects :
PaymentIntent: represents the intent to collect payment from a customer during the Checkout process. Charge attempts and payment state changes are tracked throughout the processCustomer: the Customer object is created during the Sign Up processCustomer Ephemeral Key: grants the SDK temporary access to the Customer
For security reasons, Stripe does not allow the app to create these objects. Instead, this has to be done on a server-side (see related section below).
Note
Stripe provides test cards numbers allowing to test the integration through various scenarios.
For instance, filling out the credit card form using the credit card number 4242 4242 4242 4242 with any expiration in a future date, CVC, and postal code should lead to a payment success.
This project uses Alamofire library to ease HTTP requests between the app and the server.
This project uses Firebase Authentication, specifically the Email and password based authentication sign-in method.
In the app, the AuthenticationManager class is responsible for authentication state and users related operations such as creation, email or password update, and deletion.
To keep things simple, anyone can register by providing an email address and a password.
Note : the email address does not need to be valid in order to create an account or sign in.
This project uses the Firebase method sendEmailVerification(beforeUpdatingEmail:,actionCodeSettings:) which sends a verification link to the user's email address. You will therefore need access to a working email account if you wish to test this feature.
An instance of ActionCodeSettings is provided when sending the verification email. It includes the following parameters :
url: represents the state/Continue URL in the form of a Universal Link. This link will either open the app directly or redirect to it after the action is handled by FirebasesetIOSBundleID: the bundle identifier of the iOS apphandleCodeInApp: specifies whether the link will open directly in the app or through a Firebase-hosted web widget before redirectionlinkDomain: the custom Firebase Hosting domain used to open the link in the app. This replaces Firebase Dynamic Links. If not set, the default hosting domain is used
The link sent by Firebase has the following format :
https://<hosting_domain>/__auth/action?apiKey=&mode=&oobCode=&continueUrl=
The oobCode query parameter must be extracted from the deep link and passed to Firebase's applyActionCode method for the action to take effect — in this case, verifying and updating the user’s email address.
In this project, the action code is handled directly in the app. SwiftUI receives the Universal Link via the onOpenURL(perform:) modifier. The DeepLinkManager EnvironmentObject is responsible for handling the link through its handleFirebaseAuthLink(:) method.
Note
The Universal Link's domain passed to the ActionCodeSettings instance must be whitelisted in the Firebase Console.
You can do this by navigating to Authentication > Settings > Authorized domains.
This project uses the default email templates provided by Firebase.
Firebase Hosting is used in this project to support Universal Links for Firebase Authentication flows such as email verification and email address updates.
Starting August 25, 2025, Firebase Dynamic Links will be deprecated for email link sign-in and out-of-band actions. As a replacement, Firebase Authentication now relies on Firebase Hosting domains to generate secure, app-linkable URLs. This project has been updated accordingly.
Firebase Hosting has been initialized and deployed for this project using the default hosting configuration provided by Firebase.
Upon setup, the following default domains are provisioned automatically and associated with the project :
PROJECT_ID.firebaseapp.comPROJECT_ID.web.app
Note
To enable Universal Links on iOS, both hosting domains have been added to the Associated Domains capability of the app target in Xcode :
applinks:PROJECT_ID.firebaseapp.comapplinks:PROJECT_ID.web.app
To establish the domain-to-app trust required by Apple for Universal Links, an apple-app-site-association file was created and deployed.
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.BUNDLE_ID",
"paths": [
"/__/auth/*"
]
}
]
}
}This file is deployed with :
firebase deploy --only hosting
The Firebase Admin SDK is used in combination with Firebase Functions to instruct Firebase to generate mobile authentication links using the Hosting domain.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.setEmailActionDomain = functions.https.onRequest(async (req, res) => {
const projectConfigManager = admin.auth().projectConfigManager();
const updateRequest = {
mobileLinksConfig: {
domain: 'HOSTING_DOMAIN'
}
};
try {
await projectConfigManager.updateProjectConfig(updateRequest);
res.status(200).send("Hosting Domain successfully updated");
} catch (error) {
console.error("Error :", error);
res.status(500).send("Error : " + error.message);
}
});This function is deployed using :
firebase deploy --only functions
├── firebase.json
├── public
│ ├── index.html
│ ├── 404.html
│ └── .well-known
│ └── apple-app-site-association
└── functions
├── index.js
├── package.json
└── package-lock.json
{
"functions": {
"source": "functions"
},
"hosting": {
"public": "public",
"headers": [
{
"source": "/.well-known/apple-app-site-association",
"headers": [
{
"key": "Content-Type",
"value": "application/json"
}
]
}
],
"appAssociation": "NONE",
"ignore": [
"firebase.json",
"**/.*",
"!**/.well-known/**",
"**/node_modules/**"
]
}
}As mentioned above, Stripe requires a server-side implementation.
The server in this project is hosted on Render and has two endpoints. The source code can be found here.
-
The
/customersendpoint creates the Customer object. -
Additionally, as stated on Stripe setup, the
/checkoutendpoint :- retrieves the Customer
- creates an Ephemeral Key for the Customer
- creates a PaymentIntent, with the amount, currency, and customer
- returns the Payment Intent’s client secret, the Ephemeral Key’s secret, the Customer’s id, and the publishable key to the app.
Note
For performance purposes, Render’s projects sleep after 15 minutes. Therefore, one may experience delay when interacting with the app during the processes involving calls to the server (i.e. Sign Up & Checkout).
Under the path eCommerce/Utilities/ProductData, you can find :
- a folder
/databasecontaining the datasets set up from scratch that have been used in this project - a file
ProductDataManager.swiftdefining the methods that have been used to load the data in Firestore
For app leightweihting purpose, the images uploaded in Firestore have not been embedded in this project. However they can be retrieved within the file ProductDatabase.swift since the urls related to these images have been added to the ProductVariant model.
Note
Security Rules for both Firestore and Storage have been setup in the Firebase console.
