You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NativeScript开发类Anki应用:可更新离线存储方案咨询

Hey there! Building an Anki-like app with NativeScript sounds awesome—let's work through your data storage and update challenges together, step by step.

1. Local SQLite Setup for Flashcards

First, you'll want to use the nativescript-sqlite plugin (it's the most straightforward way to work with SQLite in NativeScript). Let's start with a solid table structure that supports your review needs:

You'll need two core tables at minimum:

  • cards: Stores all flashcard content and review metadata
  • app_metadata: Tracks data versions to handle incremental updates

Here's a sample SQL script to create these tables (you can run this on app first launch):

import { openDatabase } from "nativescript-sqlite";

async function initDatabase() {
  const db = await openDatabase("flashcards.db");
  
  // Create cards table
  await db.execSQL(`
    CREATE TABLE IF NOT EXISTS cards (
      card_id TEXT PRIMARY KEY, -- Use a unique, stable ID (like a UUID) for each default card
      front TEXT NOT NULL,
      back TEXT NOT NULL,
      next_review INTEGER NOT NULL, -- Unix timestamp for next scheduled review
      review_count INTEGER DEFAULT 0,
      is_user_modified INTEGER DEFAULT 0 -- 0 = default card, 1 = user edited this card
    )
  `);
  
  // Create metadata table for version tracking
  await db.execSQL(`
    CREATE TABLE IF NOT EXISTS app_metadata (
      key TEXT PRIMARY KEY,
      value TEXT NOT NULL
    )
  `);
  
  // Initialize data version if it doesn't exist
  const versionCheck = await db.get("SELECT value FROM app_metadata WHERE key = 'data_version'");
  if (!versionCheck) {
    await db.execSQL("INSERT INTO app_metadata (key, value) VALUES ('data_version', '0')");
  }
}
2. Handling Review Scheduling

For the spaced repetition logic, you can calculate the next_review timestamp based on how well the user remembers the card. A simple starting point (you can refine this later like Anki's SM-2 algorithm):

  • If user marks "Easy": Set next review to current time + 1 day
  • If user marks "Good": Set next review to current time + 10 minutes
  • If user marks "Hard": Set next review to current time + 5 minutes

When querying cards to review, just filter for next_review <= CURRENT_TIMESTAMP:

async function getDueCards() {
  const db = await openDatabase("flashcards.db");
  const now = Date.now();
  return db.all("SELECT * FROM cards WHERE next_review <= ?", [now]);
}
3. Incremental Card Updates (No Full Sync Needed)

This is the key part—you want to add new cards on app updates without overwriting user data. Here's a rock-solid approach using versioned built-in cards:

How it works:

  1. Bundle versioned card data with your app: Store new cards in JSON files in your app's assets folder (e.g., cards_v1.json, cards_v2.json). Each file should have a version field and an array of cards with unique card_ids.
  2. Track the latest imported version: Use the app_metadata table to store the highest version of cards the user has already imported.
  3. On app launch, check for new versions: Compare the local stored version with the latest version bundled in the app. If there's a newer version, import only those new cards—skip any cards the user has modified.

Sample implementation:

First, create a cards_v2.json in assets/data/:

{
  "version": "2",
  "cards": [
    {
      "card_id": "math_algebra_001",
      "front": "What's the quadratic formula?",
      "back": "x = [-b ± √(b²-4ac)]/(2a)",
      "next_review": 0, // Will be set to current time + default interval on first import
      "review_count": 0
    }
  ]
}

Then, add an update check function that runs on app start:

import { knownFolders } from "@nativescript/core";

async function checkForCardUpdates() {
  const db = await openDatabase("flashcards.db");
  const localVersion = await db.get("SELECT value FROM app_metadata WHERE key = 'data_version'");
  const latestVersion = "2"; // Hardcode this for the current app version, or read from a config file
  
  if (parseInt(localVersion.value) < parseInt(latestVersion)) {
    // Load the new cards from assets
    const cardsFile = knownFolders.currentApp().getFile("data/cards_v2.json");
    const newCardsData = await cardsFile.readText();
    const newCards = JSON.parse(newCardsData).cards;
    
    // Use a transaction to batch insert (faster and atomic)
    await db.execSQL("BEGIN TRANSACTION");
    
    for (const card of newCards) {
      // Check if the card already exists, and if the user hasn't modified it
      const existingCard = await db.get("SELECT is_user_modified FROM cards WHERE card_id = ?", [card.card_id]);
      
      if (!existingCard) {
        // Insert new card, set initial next_review to now (so it shows up immediately)
        await db.execSQL(`
          INSERT INTO cards (card_id, front, back, next_review)
          VALUES (?, ?, ?, ?)
        `, [card.card_id, card.front, card.back, Date.now()]);
      } else if (existingCard.is_user_modified === 0) {
        // Optional: Update default cards if you need to fix errors (skip if you don't want to overwrite)
        await db.execSQL(`
          UPDATE cards SET front = ?, back = ? WHERE card_id = ?
        `, [card.front, card.back, card.card_id]);
      }
    }
    
    await db.execSQL("COMMIT");
    // Update the local version to the latest
    await db.execSQL("UPDATE app_metadata SET value = ? WHERE key = 'data_version'", [latestVersion]);
  }
}

Bonus: Protect User-Created Cards

If you want users to create their own cards, add a card_type field (e.g., default vs user_created) to the cards table. When updating, never touch user_created cards—only handle the default ones.

4. Quick Best Practices
  • Run DB operations off the main thread: Use async/await properly, and consider wrapping heavy operations in a background task to avoid UI freezes.
  • Add backup functionality: Let users export their database to the device's storage (or cloud, if you want) so they don't lose data if they reinstall the app.
  • Test edge cases: What if the user skips multiple app updates? Make sure your version check logic can jump from v1 to v3 without missing v2's cards.

内容的提问来源于stack exchange,提问作者Hoargarth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:18:14