Mobile Development 11 min read

Native iOS Apps in PHP: TypePHP Compiles PHP to arm64 with UIKit Bridge

This article demonstrates how TypePHP enables native iOS app development entirely in PHP by compiling PHP to arm64 machine code and using a thin Objective-C++ bridge to UIKit, covering architecture, code structure, compilation, signing, deployment, and customization with concrete examples and commands.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Native iOS Apps in PHP: TypePHP Compiles PHP to arm64 with UIKit Bridge

Division of Labor: PHP Logic, Objective-C++ Thin Bridge

TypePHP development follows normal PHP conventions with one convention: declare a native function in the bridge file for each required iOS capability. The example app uses only six native functions: ui_app_run() – enter iOS application lifecycle ui_create_window() – create window, set logical canvas size ui_add_label() – add text label, return control ID ui_add_button() – add button, return control ID ui_set_control_text() – modify control text ui_show_window() – show window

The bridge is written in Objective-C++ ( .mm), belongs to the app, and is minimal. All UI layout, button actions, and state management remain in PHP. After compilation, the app contains no PHP source code—only machine instructions.

Code Structure

The official example resides in examples/apple-native/:

examples/apple-native/
├── php-src/
│   ├── application.php      # UI and interaction logic (PHP)
│   └── ios-main.php         # iOS entry point (PHP)
├── ios-src/
│   ├── uikit_bridge.stub.php # Native function declarations for PHP
│   └── uikit_bridge.mm       # UIKit implementation (Objective-C++)
├── ios.yml                  # Compilation configuration
└── package-ios-app.sh       # Packaging and signing script

iOS Entry Point ( ios-main.php )

<?php

/** iOS launch completion, called by bridge */
function typephp_application_did_launch(): void
{
    HelloApplication::build('UIKit');
    ui_show_window();
}

/** Each control click triggers this callback with control ID */
function typephp_application_control_activated(int $controlId): void
{
    HelloApplication::handleEvent($controlId);
}

function main(): void
{
    ui_app_run('TypePHP iOS Hello');
}

Layout in PHP ( buildPhoneLayout() )

private static function buildPhoneLayout(): void
{
    ui_create_window('TypePHP Native Hello', 390, 844);
    self::$titleLabel      = ui_add_label('', 24, 672, 342, 76, 30, true);
    self::$descriptionLabel = ui_add_label('', 30, 544, 330, 104, 16, false);
    self::$statusLabel     = ui_add_label('', 30, 454, 330, 58, 18, true);
    self::$countButton     = ui_add_button('', 30, 354, 330, 58, 1);
    self::$resetButton     = ui_add_button('', 30, 280, 158, 52, 2);
    self::$languageButton  = ui_add_button('', 202, 280, 158, 52, 3);
}

Click Handling in PHP

public static function handleEvent(int $controlId): void
{
    if ($controlId === self::$languageButton) {
        self::$isChinese = !self::$isChinese;
        self::renderText();
        return;
    }

    if ($controlId === self::$resetButton) {
        self::$clickCount = 0;
        self::renderStatus();
        return;
    }

    if ($controlId === self::$countButton) {
        self::$clickCount++;
        self::renderStatus();
    }
}
ui_add_label()

and ui_add_button() return an auto-incrementing control ID. The bridge calls typephp_application_control_activated() with that ID on click, so event handling in PHP is a plain method call.

Compilation

Run from the TypePHP repository root using ios.yml as configuration:

export PHPX_HOME=/path/to/phpx
php bin/tpc.php examples/apple-native/ios.yml --no-progress

The output is examples/apple-native/typephp_ios_hello, verified as a Mach-O 64-bit arm64 executable targeting iOS 15.0:

$ file typephp_ios_hello
typephp_ios_hello: Mach-O 64-bit executable arm64

$ otool -l typephp_ios_hello | grep -A3 LC_BUILD_VERSION
      cmd LC_BUILD_VERSION
  platform 2          # iOS
      minos 15.0

Key ios.yml settings for iOS:

target-platform: arm64-apple-ios15.0  # cross-compile target
cpp-compiler: xcrun --sdk iphoneos clang++  # Xcode iPhoneOS toolchain
cxx-flags:
  - -fobjc-arc          # enable ARC for bridge
  - -miphoneos-version-min=15.0
ld-flags:
  - -framework UIKit
  - -framework Foundation
  - -framework CoreGraphics

Packaging and Signing

iOS requires code signing. Prepare a provisioning profile ( .mobileprovision) and developer certificate, then set environment variables:

export TYPEPHP_IOS_PROVISIONING_PROFILE=/path/to/profile.mobileprovision
export TYPEPHP_IOS_CODE_SIGN_IDENTITY='Apple Development: Your Name (TEAMID)'
# Override Bundle ID if profile differs from example
export TYPEPHP_IOS_BUNDLE_IDENTIFIER='your.provisioned.bundle.identifier'

sh examples/apple-native/package-ios-app.sh

The script assembles the .app bundle, copies icons, signs, and verifies, outputting:

Created and signed .../dist/TypePHP iOS Hello.app

Install and Run on iPhone

List devices to obtain <device-id>: xcrun devicectl list devices Install the app:

xcrun devicectl device install app \
    --device <device-id> \
    'examples/apple-native/dist/TypePHP iOS Hello.app'

Launch on device (or tap the icon):

xcrun devicectl device process launch --device <device-id> swoole.typephp

After first install, trust the developer certificate in Settings → General → VPN & Device Management.

Customizing Your Own UI

Coordinates use a logical canvas with origin at top-left; example uses 390 x 844 (iPhone portrait). The bridge converts to UIKit coordinates and scales automatically for screen size and notch. ui_add_label($text, $x, $y, $w, $h, $fontSize, $bold) – text control. ui_add_button($title, $x, $y, $w, $h, $style) – button; style: 1 (solid blue), 2 (light blue), 3 (gray). ui_set_control_text($id, $text) – update text for live status.

Button clicks unified via typephp_application_control_activated($id) callback; differentiate by ID.

Example: add a "Greet" button:

// Layout
self::$helloButton = ui_add_button('打个招呼', 30, 200, 330, 52, 2);

// Event
if ($controlId === self::$helloButton) {
    ui_set_control_text(self::$statusLabel, '你好,TypePHP!');
}

For capabilities beyond UIKit (location, camera, network), add a php_ -prefixed function in uikit_bridge.mm and declare it in .stub.php; PHP can then call it directly.

Important Notes

Real device only. Current iOS target provides only iphoneos arm64; no simulator target, signing mandatory.

Bridge is hand-written, not auto-generated. Unlike Android/JNI reflection bridges, each app maintains its own .mm file—fully controllable and small, but requires writing a function for each system capability used.

Provisioning profiles expire. Personal developer profiles typically last 7 days; re-issue via Xcode when expired.

Build environment: macOS + full Xcode + matching fully static libphp.a / libphpx.a (located at PHPX_HOME/ios/iphoneos-arm64).

The same application.php, with a different entry and bridge, can compile to a macOS AppKit app; Android also has an arm64 native solution. One PHP codebase targeting native UI on desktop and mobile —a new direction for TypePHP beyond the server.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

iOSPHPObjective-C++arm64Native DevelopmentUIKitCross-compilationTypePHP
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.