Choosing Between Ti.App.Properties and Ti.Database for Local Data in Titanium SDK
Learn when to use Ti.App.Properties versus Ti.Database for local data in Titanium SDK apps, with a comparison table, trade‑offs, and ready‑to‑copy code examples.
03 Sept 2026, 22:24 UTC

Decision point
When building a Titanium SDK app you need to persist data locally. The choice hinges on data volume, query complexity, and whether you need relational features. For tiny, flat settings (a few kilobytes) use Ti.App.Properties. For anything that requires indexing, joins, or larger storage, use Ti.Database (SQLite).
Constraints to consider
- Maximum practical size:
Ti.App.Propertiesis limited by platform preferences stores (≈1 MB on iOS and Android). - Access pattern: point look‑ups vs. range queries or filtering.
- Concurrency: multiple threads accessing the same store.
- Data type needs: native booleans/numbers vs. arbitrary blobs.
Option comparison
| Feature | Ti.App.Properties | Ti.Database (SQLite) |
|---|---|---|
| Data model | Key‑value strings (all values stored as strings) | Typed columns, indexes, support for JOINs |
| Setup | None – property bag is ready at runtime | Open/close connection, create tables if needed |
| Performance (read) | O(1) hash lookup | O(log N) with proper indexes |
| Scalability | Few MB practical limit | Hundreds of MB–GB (limited by device storage) |
| Transactions | None | Full ACID support |
| Cross‑platform consistency | Same API, values persisted via platform defaults | Same SQL; only file‑path handling differs slightly |
Trade‑offs
Ti.App.Properties gives you instant, zero‑boilerplate access and automatic sync with the native preferences store. The downside is that every value is a string, so you must cast numbers/booleans, and you cannot run queries beyond simple key look‑ups. If you store many keys or large blobs, you may hit the platform‑specific size ceiling and see slower start‑up times.
Ti.Database introduces a small amount of boilerplate (opening the DB, creating tables, closing result sets) but rewards you with powerful querying, indexing, and the ability to store binary data (BLOBs). It scales far beyond the preferences limit and handles concurrent access safely when you synchronize connections or use a single database instance per thread.
Concrete implementation (Alloy style)
Storing a user preference with Ti.App.Properties
// app.js or a controller
// Save a boolean flag
Ti.App.Properties.setBool('pushEnabled', true);
// Later retrieve it (default false if not set)
var pushEnabled = Ti.App.Properties.getBool('pushEnabled', false);
if (pushEnabled) {
// enable push notifications
}
Caching API responses with Ti.Database
// Open (or create) the SQLite database
var db = Ti.Database.open('appcache');
// Ensure the table exists
var createSQL = 'CREATE TABLE IF NOT EXISTS feeds (' +
'id INTEGER PRIMARY KEY, ' +
'title TEXT NOT NULL, ' +
'url TEXT UNIQUE, ' +
'fetched INTEGER NOT NULL)'; // timestamp
try {
db.execute(createSQL);
} catch (e) {
Ti.API.error('Table creation failed: ' + e.message);
}
// Insert or replace a feed entry
var insertSQL = 'INSERT OR REPLACE INTO feeds (title, url, fetched) VALUES (?, ?, ?)';
db.execute(insertSQL, 'Example Feed', 'https://example.com/feed', Date.now());
// Retrieve the cached feed
var selectSQL = 'SELECT * FROM feeds WHERE url = ?';
var rows = db.execute(selectSQL, 'https://example.com/feed');
if (rows.isValidRow()) {
var title = rows.fieldByName('title');
var fetched = rows.fieldByName('fetched');
// use title, fetched, etc.
}
rows.close();
// Always close the database when done
db.close();
Validation steps
- Run the app on an iOS simulator and an Android emulator.
- Toggle the
pushEnabledflag via the UI, then close and relaunch the app. - Read the flag with
Ti.App.Properties.getBooland confirm it matches the last set value. - For the database path, insert a test record as shown, restart the app, reopen the database, and query the same record.
- Verify that the returned
fetchedtimestamp equals the value you inserted and that the row is still present.
If any of these checks fail, review the open/close calls and ensure you are not leaking result sets or database connections.
Limitations and practical checks
- Ti.App.Properties: On iOS the underlying
NSUserDefaultshas a ~1 MB limit; Android’sSharedPreferencesis similar. To stay safe, keep total stored data under a few hundred kilobytes. - Ti.Database: Forgetting to close a
Ti.Database.ResultSetor the database itself can lead to "database is locked" errors, especially on Android when accessed from multiple threads. Use a single database instance per thread or synchronize access.
You can quickly check the size of the preferences store by inspecting the platform‑specific defaults file (e.g., Library/Preferences on iOS simulator) or by logging the length of the JSON string you would store if you serialized all properties.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.