Website Links

Monday, 7 December 2015

iOS - Using a preloaded database

Hi all, this tutorial will be on using a preloaded database in Swift. There are several reasons you may want to do this, one being that you may have data that won't change that you want to be available to the user. You could load this up at runtime, but that could be slow if you have a lot of records to import. This method ensures that the application runs quickly, upon being opened for the first time... And let's be honest, the first impression counts!

Creating the database

  1. Get the data you want into some format you can parse, e.g. XML, JSON or CSV.
  2. Create your project, ensure that you select to use Core Data.
  3. Copy your data file into your project
  4. Setup your core data schema. I will assume core data familiarity in this article so if you are unfamiliar you should read this article on core data.
  5. Write code to parse the file mentioned in step 1, and then save the data. My parser was as follows, and was located in my AppDelegate:
    
      func loadData () {
        // path to csv file you are loading data for
        let path = NSBundle.mainBundle()
          .URLForResource("wordlist", withExtension: "csv")
            
        do {
          let content = try String(
            contentsOfURL: path!, 
            encoding: NSUTF8StringEncoding)
          let lines = content
            .componentsSeparatedByCharactersInSet(
            NSCharacterSet.newlineCharacterSet())
            as [String]
            
          for line in lines {
            var values = line
              .componentsSeparatedByString(",")
            let mode = NSEntityDescription
              .insertNewObjectForEntityForName("Mode", 
              inManagedObjectContext: managedObjectContext)
            mode.setValue(values[0], forKey: "name")
                
            let word = NSEntityDescription
              .insertNewObjectForEntityForName("Word",
              inManagedObjectContext: managedObjectContext)
            word.setValue(values[1], forKey: "word")
          }
            
          do {
            try managedObjectContext.save()
          } catch {
                 
          }
        } catch {
                
        }
      }
    
    
  6. Run your application and ensure that you have the following print statement and the call to your parsing method somewhere in the code that will be executed, e.g.:
    
      func application(application: UIApplication, 
        didFinishLaunchingWithOptions launchOptions: 
        [NSObject: AnyObject]?) -> Bool
      {
        // where to get the preloaded database from
        print(applicationDocumentsDirectory.path)
        loadData()
            
        return true
      }
    
    
  7. Copy the files into your project from the directory output by the console, ensuring "Copy items if needed" is checked.
  8. Make the necessary code changes (below) to reference your preloaded database. I have highlighted the additional code in yellow.

Code Changes to Access Your Preloaded Database


  lazy var persistentStoreCoordinator: 
    NSPersistentStoreCoordinator = {
      let coordinator = NSPersistentStoreCoordinator(
        managedObjectModel: self.managedObjectModel)
      let url = self.applicationDocumentsDirectory
        .URLByAppendingPathComponent("SpellingBee.sqlite")
    
          
      if !NSFileManager.defaultManager()
        .fileExistsAtPath(url.path!) {
        
        let sourceSqliteURLs = [
          NSBundle.mainBundle().URLForResource("SpellingBee", 
            withExtension: "sqlite")!,
          NSBundle.mainBundle().URLForResource("SpellingBee", 
            withExtension: "sqlite-wal")!, 
          NSBundle.mainBundle().URLForResource("SpellingBee", 
            withExtension: "sqlite-shm")!
        ]
            
        let destSqliteURLs = [
          self.applicationDocumentsDirectory
            .URLByAppendingPathComponent("SpellingBee.sqlite"),
          self.applicationDocumentsDirectory
            .URLByAppendingPathComponent("SpellingBee.sqlite-wal"),
          self.applicationDocumentsDirectory
            .URLByAppendingPathComponent("SpellingBee.sqlite-shm")
        ]
            
        var error:NSError? = nil
        for var index = 0; index < sourceSqliteURLs.count; index++ {
          do {
            try NSFileManager.defaultManager()
              .copyItemAtURL(sourceSqliteURLs[index], 
              toURL: destSqliteURLs[index])
          } catch {
                  
          }
        }
      }
        
      var failureReason = "..."
      do {
        try coordinator.addPersistentStoreWithType(
          NSSQLiteStoreType, configuration: nil, 
          URL: url, options: nil)
      } catch {
        ...
      }
    }()

Basically, this new code copies the database from your project files to the directory where the databases are stored. If the copy is not performed, a new database will be created if no databases exist in the directory. Please note, if you are getting nil when unwrapping the value returned by "NSBundle.mainBundle().URLForResource("SpellingBee", withExtension: "sqlite")". Then you may need to go to your build phases and ensure all 3 sqlite files are added to the "Copy Bundle Resources" section. Good luck and happy programming!

No comments:

Post a Comment