Is there a way one can check the connection status of a SQLite DB in IOS. I do not want to keep opening the db connection again and again. As a work around, I have put the SQLite DB object into a static variable and check if the object is NIL, else use the object as is.
Is there a simpler and cleaner way to do this
A lot of SQLite Wrappers for iOS provide this functionality. Here is a library I wrote for handling SQLite connections which you can use to check the status of a connection: https://github.com/ziminji/objective-c-sql-query-builder
First of all, I would recommend using FMDB instead of SQLite directly.
To answer your question: don't bother. I have many apps, with many users and I have never seen the database "connection" fail. It just doesn't fail, it's not a network connection, just an open file.
Try www.github.com/pmurphyjam/DBExample It's an Xcode project that uses SQLite. It abstracts the entire SQL layer for you so you can concentrate on just writing SQL queries. It does large transactions also. The SQL syntax is exactly like FMDB, and it also uses Dictionaries for complex queries. Here's an example:
For selects NSMutableArray = GetRecordsForQuery:#"select firstName, lastName from Company where lastName = ? ",#"Smith",nil];
OR For inserts, deletes or updates
BOOL = ExecuteStatement:#"insert into Company (firstName, lastName) values(?,?)",#"John",#"Smith", nil];
There is an example project for using SQLite here you can refer to: https://github.com/AaronBratcher/ABSQLite
It has classes for accessing SQLite in a more traditional database way.
Related
I have iOS app that takes data from the server as json and then serializes them into objects of different types. Types can be complicated, can contain subtypes, can inherit, so there is no any limitations. Another thing that makes everything even more complicated is some of types are stored as AnyObject? and only in run time they are being serialized into real types accordingly to the specific rules. Something like that:
class A {
var typeName: String?
var b: AnyObject?
}
Then when it's serialized it can be done something like that:
if let someClass = NSClassFromString(typeName) as? SomeGenericType.Type{
b = someClass.init()
}
Also querying should be done on all the data. Currently I'm trying to store all of them locally, then load into memory and query there from the code. I'm using User defaults, but they have some limitations, also I needed to provide custom coding to make it work, and each time when I add a new field it turned out that I missed something in coding and nothing works. So it's pain.
Ideally I would just do some magic command and all the objects are sent to local storage no matter how complicated they are. The same to extract them from this storage. Also, user change data so I can't just store primary Json. And I don't want to covert objects back to Jason as for it's pain too.
Any suggestions?
If you want to use sqlite then You can store whole object in one row! I means you can create table with 2 columns one is id and second is your dataobject(it's data type should be blob). Then convert your whole object into data. Then store in sqlite table and retrieve it as data then convert it to object when want to use. By this way your object will remains in same format as you asked
Firebase while meant for online synching and storage can also cache everything locally in case you are offline and perform query's against the local cache. It uses JSON.
CouchDB also has a mobile version for iOS.
Both of those are over kill if your dataset is small; you can just store it as a text file and read the JSON back in. See performance characteristics here. The graph is for a 7MB file so if you are significantly less than that your load time may be minimal.
NSKeyedArchiver.archivedData(withRootObject:) is great for storing custom objects as Data objects. The only thing you need to do to be able to use this is to make your custom objects conform to NSCoding. A great example can be found here:
Save custom objects into NSUserDefaults
Once you have the Data version of the object, it can easily be stored in UserDefaults, as a property in CoreData, or even in the app's keychain entries. Depending on your use case, sensitivity of data, and how much data you intend to store, you might want to use any number of storage methods. NSKeyedArchiver.archivedData(withRootObject:) allows you to pretty much use any of them.
I have an app with a simple table stored in a common sqlite database. The app has a mainview and several other views say, view1, view2, ....,viewN. From a mainview, the user go to view1 by this code section:
screen.modalTransitionStyle=UIModalTransitionStyleCoverVertical;
[self presentModalViewController:screen animated:YES];
in view1, the user will access the database, doing something, then update the database, quite view1 back to the mainview:
[self dismissModalViewControllerAnimated:YES];
The user will do the same thing for the other views, i.e., accessing the database, doing something, updating the database, then back to the mainview.
My question is how should I organize the database in my case, using a singleton to create a common object to open the database at the mainview, then all views will access the database, updating it or each view will open the database, accessing it, then update individually or is there any other efficient way. Thank you
As you describe the structure of your app being one threaded only - using a singleton is perfectly OK. You need to open the DB only once when the app starts and make sure to close it when the app ends or even when the app goes to background (of course then you need to open the DB when return from background too)
By the way, I also tried to open and close the DB for each view - which also works fine. In this approach I also sometimes use a "dirty" flag, that is set to indicate that the DB needs updateing before closing - but that turned out to make no difference in performance.
Instead of using a singleton you may also use a class variable or declare it within your app delegate, which is often done for the cotext of core data ( where the "context" in core data is similar to the DB in your case)
What is important in whatever approach you use is that your DB will be in a consistent state all the time since your app can get "interrupted" by phone call for example.
By the way, I tend to use core data more often on iPhone if circumstances allow, since core data takes care of many of the DB issues - only saving at consistent states needs to be done explicitly. But it really depends if your data is more a big DB or just "some" persitent objects/attributes.
I suggest avoiding c level code for calling a SQLite DB - try a simple wrapper over SQLite - see https://github.com/JohnGoodstadt/EasySQLite
This uses a singleton when you 'connect' to it:
self.db = [DBController sharedDatabaseController:#"MyDB.sqlite"];
Any file can then access the shared singleton - e.g.
int personCount = [_db ExecuteScalar:#"SELECT count(*) FROM person" asInt:YES];
DataTable* table = [_db ExecuteQuery:#"SELECT firstname,lastname FROM person"];
for (NSArray* row in table.rows)
{
NSString* firstname = row[0];
NSString* firstname = row[1];
...
}
I am using Mygeneration tools to create the abstract classes responsible for dealing with database to perform CRUD operation as well as some other dooDad operations. Problem is I cant retrieve the auto number field (it is also Primary Key) of table using the code
Employees newObj = new Employees();
newObj.ConnectionString = connectionString;
newObj.AddNew();
// Your Properties will be different here
newObj.FirstName = "Joe";
newObj.LastName = "Plank Plank";
newObj.Save();
int staffid=newObj.StaffID;
The same thing is working fine in MS SQL server or other databases. Looks like auto number is not generated instantly which can be accessed once I added the entry. But, later, when I am checking the database, I found that auto number is generated there. Not sure, why this is happening. Anybody having expertise with dooDads, please help with info.
Edited:
The main problem is I cant access the autonumber field instantly after I create the fresh row entry. Looks like MS Access autonumber takes some time to show up and even in VS, you can see this phenomenon. How to fix this problem?
I have built many applications using Doodads , using MS Access , you have only to make the filed as autonumber .. and generate the stored procedures and other classes.
i.e your code should work ..
also I made modification to Dodads to return list of Objects
How to get list of objects from BusinessEntity using myGeneration?
I'm working on an application that will need to pull from a list of data depending on where the user is located in the US. In a sense, I will have a database full of information based on their location, and a condition statement will determine while value from the list to use.
Example of data:
Tennessee:
Data2 = 25;
Data3 = 58;
...
Texas:
Data2 = 849;
Data3 = 9292;
...
So on...
My question is, what is the best practice to use when developing iOS apps and you have a lot of data? Should you just put all the related data in a file, and import that file when you need to like normal, or is there another method you should use? I know they state you should follow the MVC practice, and I think in this case my data would be considered the Model, but just want to double check if that applies here.
You have some options here:
SQLite database
Core Data (its not a relational database model like sqlite)
write to plain text file. (using NSFileManager )
NSKeyedArchiever
If you want to frequently keep appending data to a single file, I would recommend using sqlite fast and robust.
This is the way I am thinking of using DB4o. When I need to query, I would open the file, read and close:
using (IObjectContainer db = Db4oFactory.OpenFile(Db4oFactory.NewConfiguration(), YapFileName))
{
try
{
List<Pilot> pilots = db.Query<Pilot>().ToList<Pilot>();
}
finally
{
try { db.Close(); }
catch (Exception) { };
}
}
At some later time, when I need to insert, then
using (IObjectContainer db = Db4oFactory.OpenFile(Db4oFactory.NewConfiguration(), YapFileName))
{
try
{
Pilot pilot1 = new Pilot("Michael Schumacher", 100);
db.Store(pilot1);
}
finally
{
try { db.Close(); }
catch (Exception) { };
}
}
In this way, I thought I will keep the file more tidy by only having it open when needed, and have it closed most of the time. But I keep getting InvalidCastException
Unable to cast object of type 'Db4objects.Db4o.Reflect.Generic.GenericObject' to type 'Pilot'
What's the correct way to use DB4o?
No, it's not a good idea to work this way. db4o ObjectContainers are intended to be kept open all the time your application runs. A couple of reasons:
db4o maintains a reference system to identify persistent objects, so it can do updates when you call #store() on an object that is already stored (instead of storing new objects) . This reference system is closed when you close the ObjectContainer, so updates won't work.
Class Metadata would have to be read from the database file every time you reopen it. db4o would also have to analyze the structure of all persistent classes again, when they are used. While both operations are quite fast, you probably don't want this overhead every time you store a single object.
db4o has very efficient caches for class and field indexes and for the database file itself. If you close and reopen the file, you take no advantage of them.
The way you have set up your code there could be failures when you work with multiple threads. What if two threads would want to open the database file at exactly the same time? db4o database files can be opened only once. It is possible to run multiple transactions and multiple threads against the same open instance and you can also use Client/Server mode if you need multiple transactions.
Later on you may like to try Transparent Activation and Transparent Persistence. Transparent Activation lazily loads object members when they are first accessed. Transparent Persistence automatically stores all objects that were modified in a transaction. For Transparent Activation (TA) and Transparent Persistence (TP) to work you certainly have to keep the ObjectContainer open.
You don't need to worry about constantly having an open database file. One of the key targets of db4o is embedded use in (mobile) devices. That's why we have written db4o in such a way that you can turn your machine off at any time without risking database corruption, even if the file is still open.
Possible reasons why you are getting a GenericObject back instead of a Pilot object:
This can happen when the assembly name of the assembly that contains the Pilot object has changed between two runs, either because you let VisualStudio autogenerate the name or because you changed it by hand.
Maybe "db4o" is part of your assembly name? One of the recent builds was too agressive at filtering out internal classes. This has been fixed quite some time ago. You may like to download and try the latest release, "development" or "production" should both be fine.
In a presentation I once did I have once seen really weird symptoms when db4o ObjectContainers were opened in a "using" block. You probably want to work without that anyway and keep the db4o ObjectContainer open all the time.
It is ok to reopen the database multiple times. The problem would be performance and loosing the "identity". Also you can't keep a reference to a result of a query and try to iterate it after closing the db (based on you code, looks like you want to do that).
GenericObjects are instantiated when the class cannot be found.
Can you provide a full, minimalist, sample that fails for you?
Also, which db4o version are you using?
Best