1

I am creating a custom back-end framework for my app which encapsulates all my database related logic, so, when we are going to use any database operation, we just need to call that framework function.

Now, for database operations I need to #import <sqlite3.h> in the file called Data.swift where my all database functions exist. But this is swift file so how can I import sqlite..?

When I use bridging-header.h, I receive error

error: using bridging headers with framework targets is unsupported

bridging-header.h has #import <sqlite3.h> inside it and set settings Bridging Header variable.

If I add import statement in Umbrella header which gives me the error

include of non-modular header inside framework module

I googled a lot but unable to find proper answer. I also looked upon https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_82 and followed it properly but I think I still miss something.

Please suggest me something.

3
  • Did you find a solution ? I am stuck with the same issue here Commented Sep 10, 2015 at 14:31
  • Check the code I have posted on last comments. I have provided links to download code. Commented Sep 10, 2015 at 18:25
  • or check the answer written by me below which contains code of file I have created to solve the issue. The basic idea is, I have to create SQLWrapper class and #import "SQLWrapper.h" in side umbrella header. Check the code below you will get some solution. Still I am facing some issues, like, when I import framework in example code, it is not working. I think Abdullah is best person to answer my framework related questions. :) Commented Sep 10, 2015 at 18:30

2 Answers 2

1

As far as I know you can not directly import Objective-C files into Swift in a framework. But you could do it in another Objective-C classes in he same framework. So if you write an Objective-C class in your framework you will be able to include the header file directly into your class and use it.

For the same purpose I ended up writing a wrapper class for sqlite.h in Objective-C and then access that in my Swift code. The header for wrapper class you write must be made public and added to your umbrella header.

I wrote something like this:

#ifndef SqlWrapper_h
#define SqlWrapper_h

struct sqlite3;

BOOL OpenDatabaseWithFileName(NSString* databaseFileName, struct sqlite3** database);

BOOL PrepareStatement(struct sqlite3* database,NSString *selectStatement,struct sqlite3_stmt** preparedStatement);

BOOL StepStatement(struct sqlite3_stmt* compiledStatement);

BOOL FinalizeStatement(struct sqlite3_stmt* compiledStatement);

NSInteger NumberOfRowsAffected(struct sqlite3* database);

NSInteger LastInsertedRowID(struct sqlite3* database);

NSInteger GetColumnCount(struct sqlite3_stmt* compiledStatement);

const unsigned char* GetColumnValue(struct sqlite3_stmt* compiledStatement,int index);

NSInteger GetColumnValueInInteger(struct sqlite3_stmt* compiledStatement,int index);

double GetColumnValueInDouble(struct sqlite3_stmt* compiledStatement,int index);

BOOL CloseDatabase(struct sqlite3* database);

#endif

Each of these function wraps sqlite3 methods. These could be called from your Swift classes in the same framework.

Sign up to request clarification or add additional context in comments.

9 Comments

But I want to code my database logic into Swift and want to make these swift functions public. If I create wrapper for this then it might end up all functions implementation in Objective C. Can you throw some more light here?
This is not working for me. It gives me error, like, "error: could not build Objective-C module". And what you are explaining makes me copy all functional prototypes from header files. If I am using 4 to 5 SDKs, then, do I need to copy all header files from these all SDKs? I think this is not a good option.
I do agree with you that this is not a good option, but I am afraid, at the moment this seems to be the only option. I would suggest instead of writing forwarding functions for each individual method write wrapper for functionality.
Please check out my project at github.com/abdullah-chhatra/SqlWrapper This is not finished yet, but I did what you are trying to achieve.
Were you able to find any better way to solve your issue? If yes then please let us know.
|
0

I have taken an objective c file and somewhat customised it.

Sqlite3Wrapper.h

#ifndef SqliteWrap_Sqlite3Wrapper_h
#define SqliteWrap_Sqlite3Wrapper_h

struct sqlite3;
@class SqlStatement;

@interface Sqlite3Wrapper : NSObject

-(id)initWithFileName: (NSString*) databaseFileName;

-(BOOL) openDatabase;

-(BOOL) closeDatabase;

-(BOOL) isOpen;

-(BOOL) executeStatement: (NSString*) sqlStatement;

-(SqlStatement*) prepareStatement: (NSString*) sqlStatement;

@end

#endif

Sqlite3Wrapper.m

#import <Foundation/Foundation.h>
#import <sqlite3.h>

#import "Sqlite3Wrapper.h"
#import "SqlStatement.h"

@implementation Sqlite3Wrapper {
    sqlite3* _database;
    NSString* _databaseFileName;
}

-(id) initWithFileName:(NSString *)databaseFileName {
    self = [super init];
    if (self) {
        _databaseFileName = databaseFileName;
    }
    return self;
}

-(BOOL) openDatabase {
    int returnValue = sqlite3_open([_databaseFileName UTF8String], &_database);
    if (returnValue == SQLITE_OK) {
        return YES;
    }

    return NO;
}

-(BOOL) closeDatabase {
    if (_database) {
        if (sqlite3_close(_database) == SQLITE_OK) {
            _database = nil;
            return YES;
        }
    }
    return NO;
}

-(BOOL) isOpen {
    return (_database != nil);
}

-(BOOL) executeStatement: (NSString *)sqlStatement {
    if (_database == nil) {
        return NO;
    }

    return (sqlite3_exec(_database, [sqlStatement UTF8String], nil, nil, nil) == SQLITE_OK);

//    sqlite3_stmt* statement;
//    if (sqlite3_prepare_v2(_database, [sqlStatement UTF8String], -1, &statement, nil) == SQLITE_OK) {
//        BOOL success = (sqlite3_step(statement) == SQLITE_OK);
//        sqlite3_finalize(statement);
//        return success;
//    }
//    return NO;
}

-(SqlStatement*) prepareStatement:(NSString *)sqlStatement {
    if (_database == nil) {
        return nil;
    }

    sqlite3_stmt* pStmt = nil;
    if (sqlite3_prepare_v2(_database, [sqlStatement UTF8String], -1, &pStmt, nil) == SQLITE_OK) {
        return [[SqlStatement alloc] initWithStatement: pStmt];
    }

    return nil;
}


@end

I am using this wrapper file and created a swift file.

Logger.swift

import Foundation

public class Logger {
    var dbWrapper:Sqlite3Wrapper
    var dataAccess = [String:AnyObject]()

    public init(dbName:String) {
        let docDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
        let dbPath:NSString = docDirPath.stringByAppendingString(dbName)

        let fileManager:NSFileManager = NSFileManager.defaultManager()
        dbWrapper = Sqlite3Wrapper(fileName: dbName)
        if !fileManager.fileExistsAtPath(dbPath) {
            if dbWrapper.openDatabase() == false {
                NSLog("Database not opened")
                let sql:NSString = "CREATE TABLE IF NOT EXISTS CONTACTS (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, ADDRESS TEXT, PHONE TEXT)"
                if dbWrapper.executeStatement(sql) {
                    NSLog("Table Created")
                }
                dbWrapper.closeDatabase()
            } else {
                NSLog("Database opened")
            }
        } else {
            NSLog("Database exists")
        }
    }

    public func open() -> Bool {
        return dbWrapper.openDatabase()
    }

    public func test(dbName:NSString) {
        let docDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
        let dbPath:NSString = docDirPath.stringByAppendingString(dbName)

        let fileManager:NSFileManager = NSFileManager.defaultManager()

        if !fileManager.fileExistsAtPath(dbPath) {
            if Sqlite3Wrapper(fileName: dbPath) == false {
                NSLog("Database not opened")
                let sql:NSString = "CREATE TABLE IF NOT EXISTS CONTACTS (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, ADDRESS TEXT, PHONE TEXT)"
                if dbWrapper.executeStatement(sql) {
                    NSLog("Table Created")
                }
                dbWrapper.closeDatabase()
            } else {
                NSLog("Database opened")
            }
        } else {
            NSLog("Database exists")
        }
    }
}

public func logMessage(message: NSString ) {
    NSLog("Message is printed")
}

public func createDatabase(dbName: NSString ) {
    Data(dbName: dbName)
}

Last 2 functions createDatabase and logMessage are just temporary to test if I can access these functions directly or not.

I have created an "Aggregated Target" as well for universal framework for both device and simulator. But when I use this universal framework, I am unable to import Logger and receives error "No such module 'Logger'". But when I package framework for device and try to add the same framework, I am able to do the import thing.

Finally, the issue is once I import Logger, I am unable to access any function or Data class from the framework. I only can access Sqlite3Wrapper which is written in Objective C. I want to use that Data class or public functions.

Please let me know if you think I have doing something wrong.

6 Comments

@Abdullah can you check this and let me know if there is any solution?
Have you made Logger.swift and file containing Data class as public. Also import the framework i.e. import <your framework name> and not class.
I have made that Logger.swift and both Data and Logger classes are public. And I am importing that framework name only. I have posted my code on dropbox and you can access both from the links. Framework: dropbox.com/s/54ph81azqypd07y/Logger%20To%20Ab.zip?dl=0, Test Project: dropbox.com/s/fxlgf5bfh1bohig/TestProduct.zip?dl=0
The one which is integrated in 'TestProduct' is Aggregated framework which gives error "No such module 'Logger'". The framework I created with target device is placed on dropbox.com/s/z0m0piqiouwhy93/Logger.framework.zip?dl=0, which allows to 'import Logger' but does not allow to access any methods from Logger and Data classes.
Unfortunately I do not have access to mac now a days so I am not able to test this out.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.