app_spec.json Reference

app_spec.json sits at the root of a client app repository and drives the patch_and_tag_for_xcode_build.py script (peek_field_app/xcode-cloud-template/). Running the script before tagging a build stamps all client-specific values into the Angular project files, the Xcode project, and the server-info defaults asset — so the built App.ipa is pre-configured for that deployment with no manual edits required.

Shape

{
    "appName": "peek",
    "appId": "com.synerty.peek",
    "appIcon": "resources/icon.png",
    "appDotVersion": "0.0.0",
    "teamId": null,
    "appearance": {
        "backgroundColor": "#26abe2",
        "environmentName": null
    },
    "serverConnection": {
        "host": null,
        "useSsl": false,
        "httpPort": 8000,
        "websocketPort": 8000,
        "hasConnected": false
    },
    "oauth2": {
        "appId": null,
        "tenantId": null
    }
}

Field Reference

Field

Description

appId

iOS bundle identifier, e.g. com.synerty.peek or com.synerty.peek.acme

teamId

Apple Developer team ID for code signing

appearance. backgroundColor

Hex colour used as the app’s primary / splash colour

appearance. environmentName

Short label shown on the config screen; must match the git branch name

serverConnection.host

Peek backend hostname (no scheme or port)

serverConnection.useSsl

true for HTTPS / WSS connections

serverConnection. httpPort

HTTP port — 443 with SSL, 8000 for direct

serverConnection. websocketPort

WebSocket port — typically same as httpPort

serverConnection. hasConnected

true skips the Connect screen on first launch

oauth2.appId

Azure Entra Application (client) ID

oauth2.tenantId

Azure Entra Directory (tenant) ID

What the Script Writes

The script writes the following server-info-tuple-defaults.json asset (peek_field_app/src/assets/peek_core_device/):

app_spec.json

server-info-tuple-defaults.json

serverConnection.host

host

serverConnection.useSsl

useSsl

serverConnection.httpPort

httpPort

serverConnection. websocketPort

websocketPort

serverConnection. hasConnected

hasConnected

oauth2.appId

oauth2AppId

oauth2.tenantId

oauth2TenantId

appId (top-level)

oauth2BundleId

It also updates:

  • ios/App/App.xcodeproj/project.pbxprojPRODUCT_BUNDLE_IDENTIFIER and DEVELOPMENT_TEAM

  • src/environments/peek-app-environment.ts — version string

  • app_spec.jsonappDotVersion bumped to the new git tag

Azure Application Proxy — host and oauth2.appId

When the Peek backend is published through Azure Application Proxy, serverConnection.host and oauth2.appId must belong to the same Entra app registration.

host

The Application Proxy external hostname (without scheme), e.g. peekfieldapp-synerty.msappproxy.net. The field app connects to this host for both HTTP and WebSocket traffic.

oauth2.appId

The Application (client) ID of the Entra app registration that owns the Application Proxy endpoint at host. The iOS app uses this ID to drive the MSAL authentication flow against Entra before the proxy grants access.

oauth2BundleId (derived from appId)

The iOS bundle identifier is used by MSAL to construct the redirect URI msauth.<bundleId>://auth. That exact URI must be registered on the Entra app registration under the iOS / macOS platform — see Azure Proxy — App Setup. Because oauth2BundleId is derived from the top-level appId, changing the bundle ID requires updating both the Xcode project and the Entra registration.

The OAuth2 scope the app requests depends on the identifier URI configured on the Entra app registration:

  • https://<external-host> (recommended for Application Proxy) → scope https://<host>/user_impersonation

  • api://<clientId> → scope api://<clientId>/user_impersonation

The https:// format is recommended because it keeps the scope human-readable and ties it directly to the proxy URL.

Example

app_spec.json for the PeekFieldApp Azure proxy deployment:

{
    "appName": "peek",
    "appId": "com.synerty.peek",
    "teamId": "Q55F59LQD3",
    "appearance": {
        "backgroundColor": "#26abe2",
        "environmentName": "PROD"
    },
    "serverConnection": {
        "host": "peekfieldapp-synerty.msappproxy.net",
        "useSsl": true,
        "httpPort": 443,
        "websocketPort": 443,
        "hasConnected": true
    },
    "oauth2": {
        "appId": "908c7b2f-e757-46be-9c6c-03da026a4159",
        "tenantId": "253aed61-d766-447d-8c13-44d7e0daeeb9"
    }
}

Running the script produces the following server-info-tuple-defaults.json:

{
    "host": "peekfieldapp-synerty.msappproxy.net",
    "useSsl": true,
    "httpPort": 443,
    "websocketPort": 443,
    "hasConnected": true,
    "oauth2AppId": "908c7b2f-e757-46be-9c6c-03da026a4159",
    "oauth2TenantId": "253aed61-d766-447d-8c13-44d7e0daeeb9",
    "oauth2BundleId": "com.synerty.peek"
}

On first launch the iOS app reads this asset, skips the Connect screen (hasConnected: true), and immediately starts the MSAL flow against the Entra tenant. The redirect URI msauth.com.synerty.peek://auth must be registered under iOS / macOS on the app registration — see Azure Proxy — App Setup.

Running the Script

From the repo root (the directory that contains app_spec.json and peek_field_app/):

pip install gitpython
python peek_field_app/xcode-cloud-template/patch_and_tag_for_xcode_build.py

The script validates that the active git branch name matches appearance.environmentName, then stamps all project files, commits, and creates a version tag. Xcode Cloud detects the new tag and starts a build automatically.

Troubleshooting

“Safari cannot open the page because the address is invalid” after Microsoft login.

The CFBundleURLSchemes entry in Info.plist does not match the iOS bundle identifier. After a successful Entra login, MSAL constructs the redirect URI as msauth.<bundleId>://auth using the runtime bundle ID. If Info.plist still registers the base bundle ID (e.g. msauth.com.synerty.peek) instead of the client-specific one (e.g. msauth.com.synerty.peek.nzor1pek6), iOS has no handler for the redirect URI and Safari shows this error.

Fix — update the MSAL entry in peek_field_app/ios/App/App/Info.plist to match the bundle ID:

<!-- Before -->
<dict>
    <key>CFBundleURLName</key>
    <string>com.synerty.peek</string>
    <key>CFBundleURLSchemes</key>
    <array>
        <string>msauth.com.synerty.peek</string>
    </array>
</dict>

<!-- After (bundle ID = com.synerty.peek.nzor1pek6) -->
<dict>
    <key>CFBundleURLName</key>
    <string>com.synerty.peek.nzor1pek6</string>
    <key>CFBundleURLSchemes</key>
    <array>
        <string>msauth.com.synerty.peek.nzor1pek6</string>
    </array>
</dict>

patch_and_tag_for_xcode_build.py performs this update automatically using plistlib — it finds the CFBundleURLTypes entry whose scheme starts with msauth. and replaces both the scheme and the CFBundleURLName with msauth.<appId> and <appId> respectively. This only requires the appId field in app_spec.json to be correct.