One codebase, 288 restaurants
Each restaurant gets its own App Store app with its branding, menu, rewards and checkout. Adding a client means editing configuration, not writing a new app.



git logA new branded search page
7a08eecPages no longer jump or reset as they open
941d394New theme for the rewards page
3fe3641Rewards activity now shows inline, no separate page
2977a6fbranch: mainFrom the restaurant app's repositoryaverage App Store rating, across 12,868 ratings on 229 of the apps.
orders placed through the apps in the last 30 days.
commits to the app repository, the most on the team.
Four pieces of the same system. Open any of them for the screens, the numbers and how it was built.
Each restaurant gets its own App Store app with its branding, menu, rewards and checkout. Adding a client means editing configuration, not writing a new app.
There was no Fastlane or CI before this. I moved every merchant onto one branch and one target, then automated signing, builds and uploads.

An analytics server for the iOS and Android apps, with orders, revenue, funnel and checkout health for all 287 active apps.

The full payment path, from the Apple Pay sheet to the order landing on the restaurant's Clover terminal. Firebase App Check keeps the API for genuine app clients only.
Five real problems from the restaurant app, straight from my commits. The code below is the actual fix, trimmed to fit.
Some merchants' apps crashed with an NSRangeException when switching tabs.
The tab index came from one filtered menu list, while the real view controllers were built from another that silently dropped items. When the two drifted, the index pointed past the end.
Record the exact items that built the tabs, index against that same list, and bounds-check as a backstop.
// Index against the list that actually built viewControllers, // not a freshly recomputed one that can drift. let bottomItems = currentTabItems guard let tabs = self.viewControllers, tabs.indices.contains(index) else { self.updateViewController() return }
Every time a guest tapped the referral card, a whole sign-in flow stayed in memory for good.
The AuthCoordinator stored closures that captured itself strongly, and nothing else owned it. That's a self-retain cycle ARC can never break.
The parent now owns the child through childCoordinators, and every stored closure captures weakly.
childCoordinators.append(coordinator) coordinator.didFinishAuth_ = { [weak self] user in guard let self else { return } self.tabBar.update(user) } coordinator.didCancelAuth = { [weak coordinator] in coordinator?.navigationController.popToRootViewController(animated: true) }
The same pattern turned up in other coordinators, and I fixed those in the same pass.
Opening checkout felt heavy on older phones.
The time-slot builder created a new DateFormatter for every generated slot, about 1,000 of the most expensive objects in Foundation per checkout open.
A small thread-safe cache keyed by format, time zone and locale, used by every formatter on that path.
enum CachedDateFormatter { private static let lock = NSLock() private static var cache: [String: DateFormatter] = [:] static func formatter(_ format: String, timeZone: TimeZone? = nil) -> DateFormatter { let key = "\(format)|\(timeZone?.identifier ?? "")" lock.lock(); defer { lock.unlock() } if let existing = cache[key] { return existing } let f = DateFormatter(); f.dateFormat = format if let timeZone { f.timeZone = timeZone } cache[key] = f return f } }
The same performance pass also removed main-thread CLLocationManager calls flagged by Xcode's Thread Performance Checker, bounded the image cache, and paused timers in the background.
The Facebook SDK had ad tracking forced on at every launch, with no App Tracking Transparency prompt. That's an App Review risk under guideline 5.1.2.
Tracking was hard-enabled, and sign-up events forwarded raw name, email and phone number.
Ask for consent only when a merchant actually uses Facebook, and only once the app is active. Facebook joins the analytics fan-out only if the user agrees, and email and phone are hashed.
private func requestFacebookTrackingAuthorizationIfNeeded() { guard !hasRequestedTrackingAuthorization else { return } hasRequestedTrackingAuthorization = true let facebookCouldTrack = AnalyticsConfig.facebookEnabled && !MerchantManager.shared.config.facebookAppId.isEmpty guard facebookCouldTrack else { return } requestTrackingAuthorization { authorized in Settings.isAdvertiserIDCollectionEnabled = authorized if authorized { AnalyticsTracker.shared.enableFacebookTracking() } } }
It builds on my rewrite of analytics into one opt-in fan-out tracker: Firebase, Facebook and our own server are separate backends, each enabled per merchant.
The unit-test target existed but was empty, and store-hours bugs kept coming back (a store "closed" while open, a sort that ordered 10 miles before 9).
Open or closed logic depended on the current time and time zone, and nothing pinned it down.
Made the calculator take now as a parameter, then covered badge states, midnight closing, closure overrides, numeric distance sorting and filters.
func testMidnightEndTreatedAsEndOfDay() { // 18:00 -> 00:00 means "open until midnight", not "closed at 00:00". XCTAssertEqual(badge(hours("wednesday", 1800, 0), at: wednesday(hour: 20))?.state, .open) } func testClosingSoonWithinLastHour() { let result = badge(hours("wednesday", 900, 2100), at: wednesday(hour: 20, minute: 30)) XCTAssertEqual(result?.state, .closingSoon) }
Real App Store reviews of apps I build and ship.
“Very easy to use, very easy to understand, and double checks with you to verify that you are ordering for the right place, time, and order!”

“This place is legit! The app is dangerously convenient.”

“Super easy to use and convenient and the rewards are a good incentive!”

Turns a long video into YouTube Shorts on its own. Whisper transcribes it, Llama 3.3 70B picks the moments worth clipping, ffmpeg cuts vertical clips with burned-in captions, and the YouTube Data API uploads them on a daily queue.
Morocco's Registre Social Unifié score decides what social support a household gets, and it comes from a long official form. I led the iOS app that turns it into short steps with the scoring logic behind them, in light and dark.
A native iOS chatbot that handles conversations through the Google Gemini API.
Scans event tickets, with secure login and a custom API for authentication and ticket validation.
Profiles, posts and followers for a restaurant social app. MySQL model, auth and endpoints shaped for what the screens render.
A configurable slider plugin used across several client sites, so editors change slides without filing a ticket.
A Laravel API with a React front end, built on my own in a one-month internship. It's the project that got me everything after it.
Khalid Alaoui has spent three years at Zaytech turning one Swift codebase into hundreds of branded ordering apps. He owns the iOS side from build to App Store, and he keeps what is already in the store working.
When shipping updates to hundreds of apps stopped scaling, he rebuilt how the platform is configured and wrote the Fastlane and GitHub Actions pipeline that ships it. Then he built the analytics that count every order.
He is now looking for a full-time iOS role, remote or with relocation. Sources describe him as "the reason it works", although that source is him.
| Substance | Found in | Finding |
|---|---|---|
| Swift, UIKit | The restaurant apps | Primary tool |
| SwiftUI | Newer app screens | In production |
| Fastlane, GitHub Actions | Release pipeline | Built it |
| Firebase | App Check, Crashlytics | In production |
| Apple Pay, Clover POS | Checkout | Built it |
| Node.js, SQLite | Analytics platform | Built it |
| Laravel, MySQL | The Foodies API | Comfortable |
| React, Python | Side projects | Comfortable |
PERSONAL. Loves the small details; they are the interesting part. Would rather skip the headache than win an argument. Skates, and lets the mood pick the music.
EARLY CAREER. Sold his book collection, ran a clothes resale with a partner, moved furniture for the neighbours and assisted a topographer. Then found Swift.
WANTED. A second monitor for the simulator. Non-negotiable.
LANGUAGES. Arabic native, English fluent, French intermediate, German losing.
NOTICE. The whiteboard behind his desk says discipline creates freedom. It is not ironic.

I'm looking for a full-time iOS role. If you have an app that needs building, or one that needs rescuing, write to me. I answer.