3

I currently have a Swift iOS app on Apple's App Store. I have many users and I would like to make a new version and help current users migrate to the new version. FYI: the new version is an Ionic app.

Data-wise, my app is using Core Data without any iCloud or sync support. It contains JSON data and also multiple images. So I'd need to bundle the current data and find a way of bringing it to the new ionic app version.

Basically my question is: Is there a way of writing in the app's documents directory and let the new version grab that file to import its data? Is there a way of letting both apps transmit data other than AirDrop or Custom URLs?

I don't want to upload the data remotely, I'd like to do this all locally on the device and also seamlessly so the user don't have to manually do anything.

Suggestions are welcome, thanks!

Tommy B.
  • 3,591
  • 14
  • 61
  • 105
  • I assume you are trying to deliver a hybrid App as a new version of an existing native App to replace the native one when users upgrade it? – Todanley Jun 21 '18 at 04:26
  • @Todanley exactly – Tommy B. Jun 21 '18 at 05:03
  • No experience on Ionic but trying to offer some ideas. The update will replace heaps of existing Native UI with a WebView running JS code. I guess the core data model will not be removed during the process. Is it possible to just use it without any migration or something. – Todanley Jun 21 '18 at 05:19
  • 1
    I’m not familiar with Ionic, but could you set up an app group identifier, so that multiple apps can access a shared container of data? This would require an update to the existing app to copy data to shared container before users used the new version and they would have to install new one while old one installed; so not very clean but might be an option? – Chris Jun 23 '18 at 17:43
  • 1
    have you looked at using an app groups? It allows you to have some shared dictionaries and also running some extension processes. You would have to update the old app to utilize it but it would all be on the same device without having to go to some external service. – Justin Miller Jun 29 '18 at 13:39
  • @Chris your answer (app groups) is what I'm looking for, more than the existing answers. If you post it as an answer I'll award you the bounty. Thanks! – Tommy B. Jun 30 '18 at 00:58
  • @JustinMiller same for you, first come first serve! thanks! – Tommy B. Jun 30 '18 at 00:58
  • @TomShreds Thanks. I’ll be at my computer in a couple of hours and will post an answer then. – Chris Jun 30 '18 at 08:44
  • @TomShreds I have submitted an answer. – Chris Jun 30 '18 at 11:09

3 Answers3

2

I would suggest using App Groups to get a shared container. I’m not familiar with Ionic, but this is quite straightforward in native Swift. It allows multiple apps or extensions to access a shared container of data, like the image below:

enter image description here

(Image from https://agostini.tech/2017/08/13/sharing-data-between-applications-and-extensions-using-app-groups/ )


This would require an update to the existing app to copy data to the shared container and then users would have to install the new app while the old one was still installed, because the shared container will be deleted when there are no installed apps using it.

It can be set up like this:

1: Enable App Groups in your project's Capabilities tab (for both apps).

2: Add a new app group and name it something like "group.appDomain.appName" or similar.

3: Now that the App Group is set up, it’s shared container can be used in several ways (User Defaults, NSCoding or Core Data).


For shared User Defaults:

let defaults = UserDefaults.init(suiteName: "group.appDomain.appName")
defaults.set("Example", forKey: "exampleKey")   
defaults.synchronize()

More info from Apple here.


For NSCoding:

let sharedContainerDirectory: URL = FileManager().containerURL(forSecurityApplicationGroupIdentifier: "group.appDomain.appName")!

let sharedArchiveURL: URL = sharedContainerDirectory.appendingPathComponent("whateverYouNeed")

NSKeyedArchiver.archiveRootObject(yourObject, toFile: sharedArchiveURL.path)

For Core Data:

You can set up the container as below. I have taken this code from this answer as I have not actually tried this with Core Data myself.

You use containerURL(forSecurityApplicationGroupIdentifier: "group.appDomain.appName")! to make this work in shared container.

lazy var persistentContainer: NSPersistentContainer = {
    /*
    The persistent container for the application.
    This implementation creates and returns a container, having loaded the store for the application to it.
    This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
     */
    let container = NSPersistentContainer(name: "xx")

    let appName: String = "xx"
    var persistentStoreDescriptions: NSPersistentStoreDescription

    let storeUrl =  FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.appDomain.appName")!.appendingPathComponent("xx.sqlite")

    let description = NSPersistentStoreDescription()
    description.shouldInferMappingModelAutomatically = true
    description.shouldMigrateStoreAutomatically = true
    description.url = storeUrl

    container.persistentStoreDescriptions = [NSPersistentStoreDescription(url:  FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.xxx.xx.container")!.appendingPathComponent("xx.sqlite"))]

    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

This answer provides a way to migrate the persistent store.

As I mentioned, I am not familiar with Ionic, so I’m not sure how working in that context might change this technique.

I hope this is helpful.

Chris
  • 4,009
  • 3
  • 21
  • 52
1

I would have just left a comment but I am unable to do so.

I was able to see that after loading a native iOS application then a Ionic project with the same bundle structure that the data within Library/Application Support/DisplayName.sqlite was still there and the data within the database still intact. (Note: this is deploying using Xcode not through the App Store)

You can see this using Xcode -> Window -> Devices and Simulators -> Devices tab -> Click on your app -> settings cog -> Download container -> after saving Show package contents

I was unable to use the Ionic SQLite native plugin to open the database for some reason. That is as far as I could get. I think it might have something to do with the space of the Application Support folder.

1

You can do the transition without using AirDrop or Custom Url. The idea is based on how ionic works. Much of the ionic functionality depends upon the plugins developed by community like working with hardware features.

Dealing with device specific features are done in native codes then JS wrapper classes are created for making a bridge between your code and native code.

I would suggest you to write native code which will access the data and files from CoreData and then use the cordova plugin tech to setup communication between the native code and the ionic code. here is a good post on Creating Custom Plugin for ionic and a sample github project

Sahil Manchanda
  • 9,812
  • 4
  • 39
  • 89