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:
- Initialization: Set up Nativeblocks in your app (for example, in your
Appstruct, or via UIKit/AppDelegate). - Frame Registration: The system loads the list of screen frames from the backend or your configuration.
- 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
STRINGorJSONvariable type - Int, Float, Double: Map to
NUMBERvariable type - Bool: Maps to
BOOLEANvariable 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()
)