BEST MOVIES UG · iOS API
API DOCUMENTATION

iOS API Documentation

Integration reference for the BEST MOVIES UG iOS application. Endpoints, authentication, request examples and implementation notes.

Production base URL
https://v2.bestmoviesug.com

Content: /api/collections/{collection}/records  ·  Accounts: /ios/account

Before you begin

The routes below reflect the current server configuration. Complete authenticated device tests before release. Apple purchase processing and some response fields still need integration testing.

API / INTRODUCTION

Integration overview

The iOS application uses Firebase Authentication to obtain an ID token, then sends that token to the application server. The server verifies identity and provides approved catalogue data and user-owned account operations.

HTTPS
Transport

Use secure HTTPS for every production request.

GET
Catalogue access

Approved collections support authenticated GET and HEAD for iOS.

UID
Account ownership

The server resolves account ownership from the verified Firebase UID.

ComponentContract
Base URLhttps://v2.bestmoviesug.com
Content API/api/collections/{collection}/records
Account API/ios/account
AuthenticationAuthorization: Bearer <FIREBASE_ID_TOKEN>
Content permissionsAuthenticated GET/HEAD on allowlisted collections
Account identityServer-derived; never trust client-supplied owner IDs
Do not ship server credentials.

Never embed gateway secrets, privileged service credentials, or Android package/signature compatibility headers in the iOS binary.

API / STATUS

Endpoint status

AreaStatusEvidence / limitation
Account routesDeployedMissing-token registration and profile returned HTTP 401.
Public gatewayOnlineProduction root returned HTTP 200 after restart.
CatalogueConfigurediOS Firebase-authenticated GET/HEAD allowlist reviewed.
VJs & genresConfiguredCollections allowlisted; Android filtering contract reviewed. Anonymous tests returned 403.
Account uniquenessProtectedDuplicate Firebase UID groups cleaned; unique index created; database check OK.
Real-user testsPendingRegistration, profile, content and account operations need authenticated end-to-end testing.
iOS purchasesPendingStoreKit purchase, restore and server-side transaction verification not yet implemented or confirmed.
IDENTITY

Firebase authentication

  1. Configure the iOS application with the correct Firebase project and approved sign-in providers.
  2. Sign in through the Firebase iOS SDK.
  3. Obtain the current Firebase ID token from the signed-in user.
  4. Attach it as a bearer token to content and account requests.
  5. Refresh through the SDK when necessary; do not permanently cache an ID token.
HTTP · Authenticated request
GET /api/collections/vjs/records?perPage=100 HTTP/1.1
Host: v2.bestmoviesug.com
Authorization: Bearer <FIREBASE_ID_TOKEN>

A Firebase user may exist before an application profile has been created. Call the registration endpoint as part of onboarding, then retrieve the profile.

04 / User accounts

Registration & profile

MethodPathPurpose
POST/ios/account/registerCreate free profile or return an existing profile; existing subscription state is preserved.
GET/ios/account/profileRead safe fields for the authenticated user.
PATCH/ios/account/profileUpdate name and/or profileImageUrl only.

Register or resolve existing profile

HTTP · Registration
POST /ios/account/register HTTP/1.1
Host: v2.bestmoviesug.com
Authorization: Bearer <FIREBASE_ID_TOKEN>
Content-Type: application/json

{"name":"Jane"}

Registration uses the verified Firebase UID to find an existing profile. A new profile receives non-privileged defaults. Expected statuses: 200 for an existing profile, 201 for a newly created profile, 400 for invalid input, and 401 for missing or invalid authentication.

Update permitted profile fields

HTTP · Profile update
PATCH /ios/account/profile HTTP/1.1
Host: v2.bestmoviesug.com
Authorization: Bearer <FIREBASE_ID_TOKEN>
Content-Type: application/json

{
  "name": "Jane K.",
  "profileImageUrl": "https://example.com/avatar.jpg"
}
Server-owned fields

Do not send subscription activation, expiry, role, device limits, download counts, payment flags or other privileged fields in registration or profile updates.

CATALOGUE

Content catalogue

Authenticated iOS clients may retrieve records from the following allowlisted collections. List requests use the same paginated record format; individual record details use the record ID.

CollectionUse
mediaMovies and series, detail pages, searches and filters
bannersHome banners and carousels
vjsVJ category names
genresGenre category names
short_videosShort-form video content
radioRadio stations
radio_categoriesRadio categories
radio_citiesRadio cities
live_tvLive television
subscription_plansPlan catalogue for display
HTTP · List and detail patterns
GET /api/collections/media/records?page=1&perPage=30
GET /api/collections/media/records/<MEDIA_RECORD_ID>
GET /api/collections/banners/records?page=1&perPage=30

Pagination contract

JSON · Illustrative response shape
{
  "page": 1,
  "perPage": 30,
  "totalPages": 4,
  "totalItems": 102,
  "items": [ /* collection records */ ]
}

Begin at page 1 and append items while page < totalPages. Guard against overlapping page requests, provide retry and empty states, and validate each collection's actual JSON fields before finalizing app models.

DISCOVERY

VJs & genres

The supplied Android CategoriesActivity has two tabs. It loads up to 100 VJs or genres, extracts the name field, sorts names alphabetically and displays a two-column category selector. Selecting an item opens a three-column media grid with 30 records per page and infinite scrolling.

FeatureMethodEndpoint / query
VJ listGET/api/collections/vjs/records?perPage=100
Genre listGET/api/collections/genres/records?perPage=100
VJ-filtered mediaGET/api/collections/media/records with filter=vjs ~ 'VJ JUNIOR'
Genre-filtered mediaGET/api/collections/media/records with filter=genres ~ 'Action'
HTTP · Category selection
GET /api/collections/media/records?page=1&perPage=30&filter=vjs%20~%20%27VJ%20JUNIOR%27
Authorization: Bearer <FIREBASE_ID_TOKEN>

# Conceptual filter values:
vjs ~ 'VJ JUNIOR'
genres ~ 'Action'
  1. Fetch and display the VJ or Genre tab list, sorted by name.
  2. When a category is selected, use its name as the filter value, matching Android behavior.
  3. Build the filter as a query parameter with URLComponents and URLQueryItem.
  4. Escape embedded quotes according to the backend filter syntax; URL encoding alone does not escape filter-language syntax.
  5. Load 30 records per page; stop when the last page is reached.
  6. Support back navigation, loading, no-results and retry states.
Category count

Android currently requests perPage=100 for VJs and genres. If a category collection exceeds 100 records, implement pagination for the category list too.

USER DATA

My List · Favorites

MethodPathBehavior
GET/ios/account/my-listList the signed-in user's favorites.
POST/ios/account/my-listAdd a media record to favorites.
DELETE/ios/account/my-list/{id}Remove an owned favorite by favorite record ID.
HTTP · Add to My List
POST /ios/account/my-list HTTP/1.1
Authorization: Bearer <FIREBASE_ID_TOKEN>
Content-Type: application/json

{"media":"MEDIA_RECORD_ID"}

The server derives the owner from the authenticated user. Do not include a user field. For deletion, use the favorites collection record ID, not the media record ID. Optimistic UI changes should roll back on failure.

USER DATA

Watch history & progress

MethodPathBehavior
GET/ios/account/watch-historyList history belonging to the current user.
POST/ios/account/watch-historyCreate a record with media and playback progress.
PATCH/ios/account/watch-history/{id}Update progress and optional artwork.
DELETE/ios/account/watch-history/{id}Remove an owned history record.
HTTP · Save progress
POST /ios/account/watch-history HTTP/1.1
Authorization: Bearer <FIREBASE_ID_TOKEN>
Content-Type: application/json

{
  "media": "MEDIA_RECORD_ID",
  "progress": {
    "seasonKey": "1",
    "episodeIndex": 0,
    "timestamp": 120
  },
  "posterUrl": "https://example.com/poster.jpg"
}

The progress object must contain seasonKey (string), episodeIndex (number) and timestamp (seconds). For movies, agree on a consistent season/episode convention and verify it against the live API. Save periodically and when playback pauses or closes.

Ownership restriction

PATCH permits progress, posterUrl and backdropUrl. It must not reassign a history entry to another user or media record.

MEDIA

Playback & entitlement gating

Catalogue responses may contain streaming URLs, but the gateway checks subscription entitlement and can remove playback links for inactive, expired or incomplete subscription states. HTTP 200 does not guarantee that a playable URL is present.

  1. Retrieve the selected media record using authenticated catalogue access.
  2. Inspect the returned data for an allowed, usable playback URL.
  3. If access is unavailable, display the appropriate locked or subscription state.
  4. Use AVPlayer for compatible HLS or progressive streams.
  5. Test redirects, MIME types, URL expiry, network interruptions and HTTP range support.
Playback rights

Do not assume offline downloads are permitted. Implement downloads only after confirming content rights, entitlement rules and approved server behavior. The Android category screen can use an ad-access gate before series details; this does not automatically carry over to iOS.

BILLING

Subscription plans & iOS purchases

HTTP · Plan catalogue
GET /api/collections/subscription_plans/records
Authorization: Bearer <FIREBASE_ID_TOKEN>

The plan collection is allowlisted for authenticated iOS catalogue requests. It provides plan information for display; it does not mean that an iOS purchase or subscription activation flow is ready.

Required iOS purchase work

  1. Configure eligible in-app subscription products in App Store Connect.
  2. Implement StoreKit purchase and restore experiences according to applicable App Store rules.
  3. Verify transactions on the server before granting or extending streaming entitlement.
  4. Handle renewals, cancellations, billing issues, expiry and entitlement restoration.
  5. Test sandbox purchases and cross-device entitlement behavior.
Not implemented or verified

This guide does not specify a verified iOS purchase, restore, transaction-verification or App Store notification endpoint. Do not reuse Android mobile-money or card checkout inside the iOS app without reviewing the applicable Apple rules.

COLLECTIONS

Radio, live TV, banners & shorts

ScreenCollection endpoint
Radio stations/api/collections/radio/records
Radio categories/api/collections/radio_categories/records
Radio cities/api/collections/radio_cities/records
Live TV/api/collections/live_tv/records
Home banners/api/collections/banners/records
Short videos/api/collections/short_videos/records

Use the same authenticated GET and pagination conventions. Validate production JSON field names, artwork URLs and stream formats before building final data models.

Radio

Test background audio, audio interruptions, Bluetooth, lock-screen controls and reconnection when needed.

Live TV

Confirm HLS compatibility, stream stability and entitlement handling.

Banners & shorts

Use appropriate image/video caching, lifecycle handling and memory management.

CODE EXAMPLES

Swift · Firebase + URLSession

This example demonstrates bearer-token acquisition, URL query construction and JSON requests. It is an integration reference, not a complete production networking layer. Define Codable response models only after checking actual API responses.

Swift · API client
import Foundation
import FirebaseAuth

enum APIError: Error {
    case notSignedIn
    case invalidURL
    case httpStatus(Int, Data)
}

struct BMUAPIClient {
    private let baseURL = URL(string: "https://v2.bestmoviesug.com")!

    func request(
        _ path: String,
        method: String = "GET",
        query: [URLQueryItem] = [],
        jsonBody: [String: Any]? = nil
    ) async throws -> Data {
        guard let user = Auth.auth().currentUser else {
            throw APIError.notSignedIn
        }

        let token = try await user.getIDToken()
        let cleanPath = path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
        var parts = URLComponents(
            url: baseURL.appendingPathComponent(cleanPath),
            resolvingAgainstBaseURL: false
        )!
        if !query.isEmpty { parts.queryItems = query }
        guard let url = parts.url else { throw APIError.invalidURL }

        var request = URLRequest(url: url)
        request.httpMethod = method
        request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")

        if let body = jsonBody {
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
            request.httpBody = try JSONSerialization.data(withJSONObject: body)
        }

        let (data, response) = try await URLSession.shared.data(for: request)
        guard let http = response as? HTTPURLResponse else {
            throw URLError(.badServerResponse)
        }
        guard (200...299).contains(http.statusCode) else {
            throw APIError.httpStatus(http.statusCode, data)
        }
        return data
    }
}

Fetch VJs, genres and filtered media

Swift · Category requests
let api = BMUAPIClient()

let vjData = try await api.request(
    "api/collections/vjs/records",
    query: [URLQueryItem(name: "perPage", value: "100")]
)

let genreData = try await api.request(
    "api/collections/genres/records",
    query: [URLQueryItem(name: "perPage", value: "100")]
)

let mediaData = try await api.request(
    "api/collections/media/records",
    query: [
        URLQueryItem(name: "page", value: "1"),
        URLQueryItem(name: "perPage", value: "30"),
        URLQueryItem(name: "filter", value: "vjs ~ 'VJ JUNIOR'")
    ]
)

// Decode vjData, genreData and mediaData into verified models.

Register, save favorite and update profile

Swift · Account requests
let registration = try await api.request(
    "ios/account/register",
    method: "POST",
    jsonBody: ["name": "Jane"]
)

let favorite = try await api.request(
    "ios/account/my-list",
    method: "POST",
    jsonBody: ["media": "MEDIA_RECORD_ID"]
)

let updatedProfile = try await api.request(
    "ios/account/profile",
    method: "PATCH",
    jsonBody: ["name": "Jane K."]
)
Production hardening

Handle token expiry, cancellation, timeouts, decoding failures, network reachability and structured server errors. Do not log bearer tokens, credentials or private account records.

REFERENCE

HTTP errors & security rules

HTTP statusMeaning / action
200 / 201Success. Registration returns 200 for existing profile, 201 for new profile.
400Invalid input or filter; validate submitted fields.
401Missing/invalid account authentication; refresh token or sign in again.
403Content access denied, missing valid token or route not allowlisted.
404Record not found or inaccessible.
405Method blocked on the iOS content API.
429 / 5xxUse safe retry/backoff; avoid automatically repeating non-idempotent mutations.
  • Use Firebase bearer tokens for both catalogue and account requests.
  • Do not send gateway secrets, privileged credentials or Android impersonation headers.
  • Account ownership is server-derived, never supplied by the client.
  • Do not unlock playback from a cached local plan alone.
  • Use authenticated GET/HEAD only on approved catalogue collections.
  • Do not expose user identifiers, tokens or private account data in logs.
TESTING

Release checklist

Tick these off as the implementation is verified on real devices. The checkmarks are stored only in this browser.

NOTES

Implementation notes

Confirmed live

Public root HTTP 200; unauthenticated registration and profile HTTP 401; anonymous VJ, genre and filtered-media HTTP 403; production code allowlists authenticated iOS GET/HEAD for these content collections. Account routes are deployed and database duplicate UID groups were resolved with an integrity check.

Still to validate

Real Firebase-authenticated iOS catalogue/account calls, actual media and radio/live TV response fields, streaming behavior on device, and the complete Apple purchase/entitlement lifecycle.

Keep the existing Android and website behavior unchanged. Reuse the secure application gateway and account routes, and add server functionality only when testing demonstrates a concrete requirement.