Подтвердить что ты не робот

Использование и доступ к существующей базе данных SQLite на iOS

У меня есть полностью заполненная база данных в SQLite, которую я бы хотел использовать в своем новом приложении. Он довольно большой, поэтому я хотел бы, если это возможно, не менять его в другом формате. Как я могу использовать эту базу данных таким образом, чтобы она поставлялась с моим приложением?

EDIT: Если я просто отброшу файл в каталог поддерживаемых файлов, например, как я могу получить к нему доступ? Как это сделать?

4b9b3361

Ответ 1

Взаимодействие базы данных SQLite можно сделать простым и чистым с помощью FBDB Framework. FMDB - это оболочка Objective-C для интерфейса SQLite C.

Ссылка стоит:

Документы FMDB Framework

Пример проекта со раскадровкой

Начальная настройка

Добавьте SQLite DB, как и любой другой файл в комплекте приложений, затем скопируйте каталог базы данных в каталог с помощью следующего кода, затем используйте базу данных из каталога документов

  • Сначала загрузите структуру FMDB
  • Извлеките фреймворк, теперь скопируйте весь файл из папки src/fmdb (а не папки src/sample или src/extra).
  • Выберите ваш проект в левом столбце Xcode.
  • Нажмите главную цель в среднем столбце.
  • Перейдите на вкладку "Построить фазы".
  • Разверните стрелку рядом с "Связывание двоичных файлов с библиотеками".
  • Нажмите кнопку "+".
  • Найдите libsqlite3.0.dylib и дважды щелкните его.

Копирование existing database в app document в didFinishLaunchingWithOptions: и сохранение пути базы данных через приложение.

В AppDelegate добавьте следующий код.

AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

// Application Start
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Function called to create a copy of the database if needed.
    [self createCopyOfDatabaseIfNeeded];

    return YES;
}

#pragma mark - Defined Functions

// Function to Create a writable copy of the bundled default database in the application Documents directory.
- (void)createCopyOfDatabaseIfNeeded {
    // First, test for existence.
    BOOL success;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    // Database filename can have extension db/sqlite.
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *appDBPath = [documentsDirectory stringByAppendingPathComponent:@"database-name.sqlite"];

    success = [fileManager fileExistsAtPath:appDBPath];
    if (success) {
        return;
    }
    // The writable database does not exist, so copy the default to the appropriate location.
    NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"database-name.sqlite"];
    success = [fileManager copyItemAtPath:defaultDBPath toPath:appDBPath error:&error];
    NSAssert(success, @"Failed to create writable database file with message '%@'.", [error localizedDescription]);
}

YourViewController.m

Выберите запрос

#import "FMDatabase.h"

- (void)getAllData {
    // Getting the database path.
    NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docsPath = [paths objectAtIndex:0];
    NSString *dbPath = [docsPath stringByAppendingPathComponent:@"database-name.sqlite"];

    FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
    [database open];
    NSString *sqlSelectQuery = @"SELECT * FROM tablename";

    // Query result 
    FMResultSet *resultsWithNameLocation = [database executeQuery:sqlSelectQuery];
    while([resultsWithNameLocation next]) {
        NSString *strID = [NSString stringWithFormat:@"%d",[resultsWithNameLocation intForColumn:@"ID"]];
        NSString *strName = [NSString stringWithFormat:@"%@",[resultsWithNameLocation stringForColumn:@"Name"]];
        NSString *strLoc = [NSString stringWithFormat:@"%@",[resultsWithNameLocation stringForColumn:@"Location"]];

        // loading your data into the array, dictionaries.
        NSLog(@"ID = %d, Name = %@, Location = %@",strID, strName, strLoc);
    }
    [database close];   
}

Вставить запрос

#import "FMDatabase.h"

- (void)insertData {

    // Getting the database path.
    NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docsPath = [paths objectAtIndex:0];
    NSString *dbPath = [docsPath stringByAppendingPathComponent:@"database-name.sqlite"];

    FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
    [database open];    
    NSString *insertQuery = [NSString stringWithFormat:@"INSERT INTO user VALUES ('%@', %d)", @"Jobin Kurian", 25];
    [database executeUpdate:insertQuery];   
    [database close];
}

Обновить запрос

- (void)updateDate {

    // Getting the database path.
    NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docsPath = [paths objectAtIndex:0];
    NSString *dbPath = [docsPath stringByAppendingPathComponent:@"fmdb-sample.sqlite"];

    FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
    [database open];    
    NSString *insertQuery = [NSString stringWithFormat:@"UPDATE users SET age = '%@' WHERE username = '%@'", @"23", @"colin" ];
    [database executeUpdate:insertQuery];
    [database close];
}

Удалить запрос

#import "FMDatabase.h"

- (void)deleteData {

    // Getting the database path.
    NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docsPath = [paths objectAtIndex:0];
    NSString *dbPath = [docsPath stringByAppendingPathComponent:@"database-name.sqlite"];

    FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
    [database open];
    NSString *deleteQuery = @"DELETE FROM user WHERE age = 25";
    [database executeUpdate:deleteQuery];   
    [database close];
}

Функциональность добавления

Получение числа строк

Обязательно включите файл FMDatabaseAdditions.h, чтобы использовать intForQuery:.

#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"

- (void)gettingRowCount {

    // Getting the database path.
    NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docsPath = [paths objectAtIndex:0];
    NSString *dbPath = [docsPath stringByAppendingPathComponent:@"database-name.sqlite"];

    FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
    [database open];
    NSUInteger count = [database intForQuery:@"SELECT COUNT(field_name) FROM table_name"];
    [database close];
}

Ответ 2

Добавьте Sqlite DB как любой другой файл в ваш пакет приложений

Скопируйте его в каталог документов с помощью кода и используйте его. Целью этого является то, что обновление содержимого в sqlite возможно только в каталоге документов

-(void) checkAndCreateDatabase
{
    // Check if the SQL database has already been saved to the users phone, if not then copy it over
    BOOL success;

    // Create a FileManager object, we will use this to check the status
    // of the database and to copy it over if required
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // Check if the database has already been created in the users filesystem
    success = [fileManager fileExistsAtPath:_databasePath];

    // If the database already exists then return without doing anything
    if(success) return;

    // If not then proceed to copy the database from the application to the users filesystem

    // Get the path to the database in the application package
    NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:_databaseName];

    // Copy the database from the package to the users filesystem
    [fileManager copyItemAtPath:databasePathFromApp toPath:_databasePath error:nil];

}

- (id)init {
    if ((self = [super init]))
    {
        _databaseName = DB_NAME;

        NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDir = [documentPaths objectAtIndex:0];
        _databasePath = [documentsDir stringByAppendingPathComponent:_databaseName];

        if (sqlite3_open([[self dbPath] UTF8String], &_database) != SQLITE_OK)
        {
            [[[UIAlertView alloc]initWithTitle:@"Missing"
                                       message:@"Database file not found"
                                      delegate:nil
                             cancelButtonTitle:@"OK"
                             otherButtonTitles:nil, nil]show];
        }
    }
    return self;
}

Ответ 3

Использование swift, singleton class и FMDB. вы можете использовать код ниже, чтобы достичь его очень легко.

Загрузить пример

import Foundation
class LocalDatabase: NSObject {


//sharedInstance
static let sharedInstance = LocalDatabase()



    func methodToCreateDatabase() -> NSURL? {

    let fileManager = NSFileManager.defaultManager()

    let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)

    if let documentDirectory:NSURL = urls.first { // No use of as? NSURL because let urls returns array of NSURL

        // exclude cloud backup
        do {
            try documentDirectory.setResourceValue(true, forKey: NSURLIsExcludedFromBackupKey)
        } catch _{
            print("Failed to exclude backup")
        }

        // This is where the database should be in the documents directory
        let finalDatabaseURL = documentDirectory.URLByAppendingPathComponent("contact.db")

        if finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) {
            // The file already exists, so just return the URL
            return finalDatabaseURL
        } else {
            // Copy the initial file from the application bundle to the documents directory
            if let bundleURL = NSBundle.mainBundle().URLForResource("contact", withExtension: "db") {

                do {
                    try fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL)
                } catch _ {
                    print("Couldn't copy file to final location!")
                }

            } else {
                print("Couldn't find initial database in the bundle!")
            }
        }
    } else {
        print("Couldn't get documents directory!")
    }

    return nil
}

    func methodToInsertUpdateDeleteData(strQuery : String) -> Bool
    {

       // print("%@",String(methodToCreateDatabase()!.absoluteString))

        let contactDB = FMDatabase(path: String(methodToCreateDatabase()!.absoluteString) )

        if contactDB.open() {

            let insertSQL = strQuery

            let result = contactDB.executeUpdate(insertSQL,
                withArgumentsInArray: nil)

            if !result {
                print("Failed to add contact")
                print("Error: \(contactDB.lastErrorMessage())")
                return false
            } else {
                print("Contact Added")
                return true
            }
        } else {
            print("Error: \(contactDB.lastErrorMessage())")
            return false
        }

    }

    func methodToSelectData(strQuery : String) -> NSMutableArray
    {

        let arryToReturn : NSMutableArray = []

        print("%@",String(methodToCreateDatabase()!.absoluteString))

        let contactDB = FMDatabase(path: String(methodToCreateDatabase()!.absoluteString) )

        if contactDB.open() {
            let querySQL = strQuery

            let results:FMResultSet? = contactDB.executeQuery(querySQL,
                withArgumentsInArray: nil)

            while results?.next() == true
            {
                arryToReturn.addObject(results!.resultDictionary())
            }

            // NSLog("%@", arryToReturn)

            if arryToReturn.count == 0
            {
                print("Record Not Found")

            }
            else
            {
                print("Record Found")

            }


            contactDB.close()
        } else {
            print("Error: \(contactDB.lastErrorMessage())")
        }

        return arryToReturn

    }
}

Иметь счастливое кодирование.

Ответ 4

Скопировать файл .sqlite в каталог...

BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// Database filename can have extension db/sqlite.
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:@"MapView.sqlite"];

success = [fileManager fileExistsAtPath:databasePath];
//    if (success){
//        return;
//    }
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"MapView.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:databasePath error:&error];
if (!success) {
    //NSAssert1(0, @"Failed to create writable database file with message '%@'.", [error localizedDescription]);
}
else
{
    NSLog(@"Database created successfully");
}

Чтобы выбрать данные из базы данных...

const char *dbpath = [databasePath UTF8String];
sqlite3_stmt    *statement;

if (sqlite3_open(dbpath, &mapDB) == SQLITE_OK)
{
    NSString *querySQL = [NSString stringWithFormat: @"SELECT * FROM maplatlong"];

    const char *query_stmt = [querySQL UTF8String];

    if (sqlite3_prepare_v2(mapDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
    {
        while(sqlite3_step(statement) == SQLITE_ROW)
        {
            NSString *cityN = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
            NSString *lat = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
            NSString *longi = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];

            [cityName addObject:cityN];
            [latitude addObject:lat];
            [longitude addObject:longi];

        }
        sqlite3_finalize(statement);

                }
    sqlite3_close(mapDB);

}

Ответ 5

Следующие методы помогут вам справиться с базой данных

Метод для копирования базы данных в каталог документов, если не существует

-(void)copyDatabase
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *insPath = [NSString stringWithFormat:@"Instamontage.sqlite"];
    destPath = [documentsDirectory stringByAppendingPathComponent:insPath];
    NSString *srcPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:insPath];
// NSLog(@"\n src %@ \n dest %@", srcPath, destPath);
   if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
   {
    NSError *error;
    NSLog(@"not exist");
    [[NSFileManager defaultManager] copyItemAtPath:srcPath toPath:destPath error:&error];
   }
   else
   {
      NSLog(@"exist");
   }
}

Способ вставки/удаления/обновления таблицы

-(BOOL)dataManipulation: (NSString *)query
{
    BOOL result=NO;
   if (sqlite3_open([destPath UTF8String], &connectDatabase)==SQLITE_OK)
   {
      sqlite3_stmt *stmt;

      if (sqlite3_prepare_v2(connectDatabase, [query UTF8String], -1, &stmt, NULL)==SQLITE_OK)
    {
        sqlite3_step(stmt);
        result=YES;
    }
    sqlite3_finalize(stmt);
  }

  sqlite3_close(connectDatabase);
  return result;
}

Способ получения строк из таблицы

-(NSMutableArray *)getData: (NSString *)query
{
    NSMutableArray *arrData=[[NSMutableArray alloc]init];
    if (sqlite3_open([destPath UTF8String],&connectDatabase)==SQLITE_OK)
    {
        sqlite3_stmt *stmt;
        const char *query_stmt = [query UTF8String];

        if (sqlite3_prepare_v2(connectDatabase,query_stmt, -1, &stmt, NULL)==SQLITE_OK)
        {
            while (sqlite3_step(stmt)==SQLITE_ROW)
            {
                NSMutableDictionary *dictResult=[[NSMutableDictionary alloc] init];

                for (int i=0;i<sqlite3_column_count(stmt);i++)
                {
                    NSString *str;

                    if (sqlite3_column_text(stmt,i)!=NULL)
                    {
                        str = [NSString stringWithUTF8String:(char *)sqlite3_column_text(stmt,i)];
                    }
                    else
                    {
                        [email protected]"";
                    }
                    [dictResult setValue:str forKey:[NSString stringWithUTF8String:(char *)sqlite3_column_name(stmt,i)]];
                }
                [arrData addObject:dictResult];
            }
            sqlite3_finalize(stmt);
        }
        sqlite3_close(connectDatabase);
    }
    return arrData;
}

Выше методы в swift будут написаны как ниже

Метод для копирования базы данных в каталог документов, если не существует

func copyDatabaseToDocumentDirectory() {
    let directoryList = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    var documentDirectory = directoryList.first
    documentDirectory?.append("/DatabasePract1.sqlite")
    print(documentDirectory!)

    if !FileManager.default.fileExists(atPath: documentDirectory!) {
        let databaseBundlePath = Bundle.main.path(forResource: "DatabasePract1", ofType: "sqlite")
        do {
           try FileManager.default.copyItem(atPath: databaseBundlePath!, toPath: documentDirectory!)
            self.databasePath = documentDirectory
        } catch {
            print("Unable to copy database.")
        }
    } else {
        print("database exist")
        self.databasePath = documentDirectory
    }
}

Способ вставки/удаления/обновления таблицы

func dataManipulation(query: String) -> Bool {
    var database: OpaquePointer?
    var result = false

    if (sqlite3_open(databasePath, &database) == SQLITE_OK) {
        var queryStatement: OpaquePointer?
        if (sqlite3_prepare_v2(database, query, -1, &queryStatement, nil) == SQLITE_OK) {
            sqlite3_step(queryStatement)
            result = true
        } else {
            let errmsg = String(cString: sqlite3_errmsg(database)!)
            print("error preparing insert: \(errmsg)")
        }
        sqlite3_finalize(queryStatement)
    }
    sqlite3_close(database)
    return result
}

Способ получения строк из таблицы

func fetchData(_ query: String) -> [[String:Any]] {
    var database: OpaquePointer?
    var arrData: [[String:Any]] = []

    if (sqlite3_open(databasePath, &database) == SQLITE_OK) {
        var stmt:OpaquePointer?
        if sqlite3_prepare(database, query, -1, &stmt, nil) != SQLITE_OK{
            let errmsg = String(cString: sqlite3_errmsg(database)!)
            print("error preparing insert: \(errmsg)")
            return arrData
        } 

        while(sqlite3_step(stmt) == SQLITE_ROW) {
            var dictData: [String: Any] = [:]
            for i in 0..<sqlite3_column_count(stmt) {
                var strValue = ""
                if (sqlite3_column_text(stmt, i) != nil) {
                    strValue = String(cString: sqlite3_column_text(stmt, i))
                }
                let keyName = String(cString: sqlite3_column_name(stmt, i), encoding: .utf8)
                dictData[keyName!] = strValue
            }
            arrData.append(dictData)
        }

        sqlite3_close(database)
    }
    return arrData
}

Ответ 6

Свифт только. Файлы Objective-C не требуются.

Вот еще одно решение, которое использует SQLite.swift вместо FMDB.

1.: Получить правильный путь к файлу вашей базы данных

Не сохраняйте файл базы данных в папке "Ресурсы", но добавьте его в список "Ресурсы комплекта копирования" в настройке "Фазы сборки" текущей цели. Если ваш файл называется myDb.db вы можете получить правильный путь, например:

let dbUrl = Bundle.main.url(forResource: "myDb", withExtension: "db")!
let dbPath = dbUrl.path

2.: Доступ к вашей базе данных (без копирования)

Теперь можно получить доступ к вашей базе данных без необходимости (вручную?) Копировать ее. Просто используйте библиотеку SQLite.swift:

db = try! Connection(dbPath)

Примечания: Единственное, что я еще не проверил, - это запись в базу данных. По крайней мере, доступ только для чтения работает как шарм, хотя.