⌘K

Core introduction

Nativeblocks is a tool for iOS that helps you build your app’s screens from simple JSON files that come from your server. You don’t need to update the app for every small change in the UI — you control things from your backend instead.


What can Nativeblocks do?

  • Update UI from the backend: Change what your app looks like by sending new JSON. No need to re-release your app.
  • Choose how you load screens: You can use a cloud setup (everything from your server), or a community setup (you give it a set of frame URLs).
  • Add actions and events: Your screens can have buttons, input, and other interactions driven by your backend.
  • Works for small and big apps: Nativeblocks can be used for simple or very complex UI, and it helps keep your codebase tidy.

How does it work?

Here’s the usual flow:

  1. Initialization: Set up Nativeblocks in your app (for example, in your App struct, or via UIKit/AppDelegate).
  2. Frame Registration: The system loads the list of screen frames from the backend or your configuration.
  3. Rendering: Your blocks, screens, and actions show up according to your JSON config.

Getting Started

Before you begin, make sure you have:

  • Swift 5.0 or later
  • iOS 15.0 or higher
  • SwiftUI for UI development

Add Nativeblocks to Your Project

Add this to your Package.swift or Xcode project:

.package(url: "https://github.com/nativeblocks/nativeblocks-ios-sdk", from: "1.7.1")

Set Up Nativeblocks

You need to initialize Nativeblocks before you use it, usually at the start of your app.

Cloud Edition

If you want Nativeblocks to fetch everything from your Nativeblocks cloud:

  • name (optional): Instance name for multi-instance support. Defaults to "default". Must contain only A–Z, a–z, 0–9, _ or -
  • API Url: Get it from your Nativeblocks studio after you create a new project.
  • API Key: Get it from your Nativeblocks studio after you create a new project.
  • DevelopmentMode: Set this to true if you want to see changes right away during development. Turn it off for production.
NativeblocksManager.initialize(
    edition: .cloud(
        endpoint: NATIVEBLOCKS_API_URL,
        apiKey: NATIVEBLOCKS_API_KEY,
        developmentMode: true
    )
)

Community Edition

If you want to load frames from your server:

NativeblocksManager.initialize(
    edition: .community(frameData: [
        "/login": "https://api.example.com/login.json",
        "/profile": "https://api.example.com/profile.json"
    ])
)

Multi-Instance Support

You can initialize multiple Nativeblocks instances with different names:

// Initialize first instance
let mainManager = NativeblocksManager.initialize(
    name: "main",
    edition: .cloud(
        endpoint: NATIVEBLOCKS_API_URL,
        apiKey: NATIVEBLOCKS_API_KEY,
        developmentMode: true
    )
)

// Initialize second instance
let secondaryManager = NativeblocksManager.initialize(
    name: "secondary",
    edition: .community(frameData: [...])
)

// Get specific instance
let mainManager = NativeblocksManager.getInstance(name: "main")
let secondaryManager = NativeblocksManager.getInstance(name: "secondary")

// Check if instance is initialized
if NativeblocksManager.isInitialized(name: "main") {
    // Use the instance
}

Don't forget to clean up when your app terminates:

class AppDelegate: NSObject, UIApplicationDelegate {
    func applicationWillTerminate(_ application: UIApplication) {
        NativeblocksManager.getInstance().destroy()
    }
}

Wandkit Setup

Configure Wandkit(s) to extend Nativeblocks functionality:

NativeblocksManager.getInstance().wandkit(
    CustomWandkit1(),
    CustomWandkit2()
)

Show a Nativeblocks Frame

Once initialized, you can show your first Nativeblocks screen. For SwiftUI:

struct ContentView: View {
    var body: some View {
        NativeblocksFrame(
            route: "/",
            routeArguments: [:],
            loading: {
                AnyView(NativeblocksLoading())
            },
            error: { message in
                AnyView(NativeblocksError(message: message))
            }
        )
    }
}

The core SDK itself does not have any integrations. All integrations need to be registered in the Nativeblocks integration marketplace and provided to the SDK.


Registering Custom Integrations

Nativeblocks lets you define and register your own custom pieces, like blocks, actions, and loggers:

Custom Block

Register your own block type:

NativeblocksManager.getInstance().provideBlock(
    blockKeyType: "INTEGRATION_UNIQUE_KEY_TYPE",
    block: { props in CustomBlockInstance(props: props) }
)

Fallback Block

Register a fallback block to handle missing or unrecognized block types:

NativeblocksManager.getInstance().provideFallbackBlock { keyType, key in
    // Handle missing block - show placeholder or error UI
    AnyView(Text("Block not found: \(keyType) (\(key))"))
}

Custom Action

To add your own action logic:

NativeblocksManager.getInstance().provideAction(
    actionKeyType: "INTEGRATION_UNIQUE_KEY_TYPE",
    action: CustomActionInstance()
)

Fallback Action

Register a fallback action to handle missing or unrecognized action types:

NativeblocksManager.getInstance().provideFallbackAction { keyType, name in
    // Handle missing action - log or show error
    print("Action not found: \(keyType) (\(name))")
}

Custom Logger

If you want to handle logging in your own way:

NativeblocksManager.getInstance().provideEventLogger(
    loggerType: "INTEGRATION_UNIQUE_KEY_TYPE",
    logger: CustomLoggerInstance()
)

Change Language

To set the language used in your screens:

NativeblocksManager.getInstance().setLocalization(
    languageCode: "EN"
)

Set global parameters for A/B testing

To set global parameters used to load a frame based on some conditions:

NativeblocksManager.getInstance().setGlobalParameters([
    "language": "EN",
    "country": "UAE",
    "currency": "AED",
    "appVersionCode": "45"
])

Experiment & Feature Flags

Get experiment or feature flag values with optional cache control:

// Use default cache (24 hours = 86400 seconds)
let showNewUI = await NativeblocksManager.getInstance().getExperiment(
    key: "show_new_ui",
    defaultValue: false
)

// No cache - always fetch fresh
let criticalFlag = await NativeblocksManager.getInstance().getExperiment(
    key: "payment_enabled",
    defaultValue: false,
    cacheTTL: 0
)

// Custom cache duration (1 hour = 3600 seconds)
let theme = await NativeblocksManager.getInstance().getExperiment(
    key: "theme_color",
    defaultValue: "#FF0000",
    cacheTTL: 3600
)

The getExperiment method supports:

  • String: Maps to STRING or JSON variable type
  • Int, Float, Double: Map to NUMBER variable type
  • Bool: Maps to BOOLEAN variable type

Scaffold API

Retrieve the scaffold model containing frame definitions:

let (scaffold, errorMessage) = await NativeblocksManager.getInstance().getScaffold()
if let scaffold = scaffold {
    // Access scaffold model with frames list
}

Frame Cache Management

Manage frame data synchronization and caching:

// Sync a specific frame from the server
await NativeblocksManager.getInstance().syncFrame(route: "/home")

// Clear a specific frame from cache
await NativeblocksManager.getInstance().clearFrame(route: "/home")

// Clear all frames from cache
await NativeblocksManager.getInstance().clearAllFrames()

Custom Type Converter

If your JSON contains a special type (for example, a size or color), you can register a converter:

NativeblocksManager.getInstance().provideTypeConverter(
    Color.self,
    converter: ColorNativeType()
)