I want my iOS swift app to preload existing sqlite file in MagicalRecord.
With reference to this, and others,
add sqlite, sqlite-shm, sqlite-wal file to my project, wrote some code in didFinishLaunchingWithOption like this.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// copy initial sqlite file to application directory
let preloadSQLiteURL:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myData", ofType: "sqlite")!)
let preloadShmURL:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myData", ofType: "sqlite-shm")!)
let preloadWalURL:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myData", ofType: "sqlite-wal")!)
let storeSQLiteURL:NSURL = NSPersistentStore.MR_urlForStoreName("myData.sqlite")
let storeShmURL:NSURL = NSPersistentStore.MR_urlForStoreName("myData.sqlite-shm")
let storeWalURL:NSURL = NSPersistentStore.MR_urlForStoreName("myData.sqlite-wal")
if !NSFileManager.defaultManager().copyItemAtURL(preloadSQLiteURL, toURL: storeSQLiteURL, error:&err) {
println("failed to copy sqlite file.")
}
if !NSFileManager.defaultManager().copyItemAtURL(preloadShmURL, toURL: storeShmURL, error:&err) {
println("failed to copy shm file.")
}
if !NSFileManager.defaultManager().copyItemAtURL(preloadWalURL, toURL: storeWalURL, error:&err) {
println("failed to copy wal file.")
}
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed("myData.sqlite")
return true
}
but copyItemAtURL all fails.
value of paths are these.
preloadSQLiteURL = file:///var/mobile/Applications/XXX/myApp.app/myData.sqlite
preloadShmURL = file:///var/mobile/Applications/XXX/myApp.app/myData.sqlite-shm
preloadWalURL = file:///var/mobile/Applications/XXX/myApp.app/myData.sqlite-wal
storeSQLiteURL = file:///var/mobile/Applications/XXX/Documents/myData.sqlite
storeShmURL = file:///var/mobile/Applications/XXX/Documents/myData.sqlite-shm
storeWalURL = file:///var/mobile/Applications/XXX/Documents/myData.sqlite-wal
my environment is Xcode6 beta7, iOS7.1.3.
Does anyone please help me? What should I do?
Any advice appreciated. Thanks in advance.
As a result, self-resolved.
NSError shows "The Operation couldn't be completed. (Cocoa error 516)"
Cocoa error 516 is "NSFileWriteFileExistsError" means "You can’t move a file to a place where a file already exists."
so, I corrected my code in this way.
let fileManager = NSFileManager.defaulManager()
let preloadSQLiteURL:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myData", ofType: "sqlite")!)
let storeSQLiteURL:NSURL = NSPersistentStore.MR_urlForStoreName("myData.sqlite")
if fileManager.fileExistsAtPath(storeSQLiteURL.path!) {
// file already exists, delete it and try again.
fileManager.removeItemAtURL(storeSQLiteURL, error:&err)
}
fileManager.copyItemAtURL(preloadSQLiteURL, toURL:storeSQLiteURL, error:nil)
and then, copyItemAtURL successfully completed and sqlite file takes effect.
While this is a poorly worded question, I'm assuming you want to load a SQLite database file from the app bundle into the user's documents directory so you can start with that data and then update it from there.
You're on the correct path, however, rather than copying all 3 files, when you create the default data store file, set your journal_mode to "DELETE" using the NSSQLitePragmasOption:
NSDictionary *options = #{NSSQLitePragmasOption: #{#"journal_mode": #"DELETE"}}
using this set of options when you add a persistent store to a coordinator when you're creating your default data set will only create a single SQLite file. From there, it'll be easier to copy only a single file over.
I also noticed in your code that you're copying myData.sqlite, but you're trying to load "acData.sqlite". Are you trying to load a file that isn't there?
Related
I am trying load SCNParticleSystem from download bundle which i am not able to load.
Path for the resource.
file:///var/mobile/Containers/Data/Application/A91E9970-CDE1-43D8-B822-4B61EFC6149B/Documents/so/solarsystem.bundle/Contents/Resources/
let objScene = SCNParticleSystem(named: "stars", inDirectory: directory)
This object is nil.
This is a legitimate problem since SceneKit does not provide an out-of-the-box solution for initializing particle systems from files that are outside of the main bundle (the only init method SCNParticleSystem.init(named:inDirectory:) implies that SCNParticleSystem.scnp files are in the main bundle).
Luckily for us .scnp files are just encoded/archived SCNParticleSystem instances that we can easily decode/unarchive using NSKeyedUnarchiver:
extension SCNParticleSystem {
static func make(fromFileAt url: URL) -> SCNParticleSystem? {
guard let data = try? Data(contentsOf: url),
let object = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
let system = object as? SCNParticleSystem else { return nil }
return system
}
}
If you do not need to support iOS 9 and iOS 10 you can use NSKeyedUnarchiver.unarchivedObject(ofClass: SCNParticleSystem.self, from: data) instead of NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(_:) and type casting, which was introduced in iOS 11.0.
Another issue that you're most likely to encounter is missing particle images. That is because by default SceneKit will look for them in the main bundle. As of current versions of iOS (which is iOS 12) and Xcode (Xcode 10) particle images in .scnp files (particleImage property) are String values which are texture filenames in the main bundle (that might change, but probably won't, however there's not much else we could use).
So my suggestion is to take that filename and look for the texture file with the same name in the same directory where the .scnp file is:
extension SCNParticleSystem {
static func make(fromFileAt url: URL) -> SCNParticleSystem? {
guard let data = try? Data(contentsOf: url),
let object = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
let system = object as? SCNParticleSystem else { return nil }
if let particleImageName = system.particleImage as? String {
let particleImageURL = url
.deletingLastPathComponent()
.appendingPathComponent(particleImageName)
if FileManager.default.fileExists(atPath: particleImageURL.path) {
system.particleImage = particleImageURL
}
}
return system
}
}
You can just set the URL of the image file and SceneKit will handle it from there.
As a little side-note, the recommended directory for downloadable content is Application Support directory, not Documents.
Application Support: Use this directory to store all app data files except those associated with the user’s documents. For example, you might use this directory to store app-created data files, configuration files, templates, or other fixed or modifiable resources that are managed by the app. An app might use this directory to store a modifiable copy of resources contained initially in the app’s bundle. A game might use this directory to store new levels purchased by the user and downloaded from a server.
(from File System Basics)
Don't have enough reps to add the comment so adding it as the answer.
The answer by Lësha Turkowski works for sure but was had issues with loading the particle images using only NSURL.
All particles were appearing square which meant,
If the value is nil (the default), SceneKit renders each particle as a
small white square (colorized by the particleColor property).
SCNParticleSystem particleImage
In the documentation it says You may specify an image using an
NSImage (in macOS) or UIImage (in iOS) instance, or an NSString or
NSURL instance containing the path or URL to an image file.
Instead of using the NSURL, ended up using the UIImage and it loaded up fine.
extension SCNParticleSystem {
static func make(fromFileAt url: URL) -> SCNParticleSystem? {
guard let data = try? Data(contentsOf: url),
let object = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
let system = object as? SCNParticleSystem else { return nil }
if let particleImageName = system.particleImage as? String {
let particleImageURL = url
.deletingLastPathComponent()
.appendingPathComponent(particleImageName)
if FileManager.default.fileExists(atPath: particleImageURL.path) {
// load up the NSURL contents in UIImage
let particleUIImage = UIImage(contentsOfFile: particleImageURL.path)
system.particleImage = particleUIImage
}
}
return system
}
}
I found out, that sometimes when dragging a SCNParticleSystem file into your project (probably form a different project) a silent error can happen due to some bugs in Xcode. As a result you can't get a reference to an instance of your SCNParticleSystem.
Solution: Check your BuildSettings in your target. The SCNPaticleSystem AND the associated ImageFile should be listed there and then you should get it right. (see screenShot below)
I am trying to store 30k users details in to core data. To achieve this I search and came up with a solution to have all the data in a file in CSV format.
I am able to download and read CSV file to using following code:
func readCsvFile () {
if let path = Bundle.main.path(forResource: "users", ofType: "csv") {
if FileManager.default.fileExists(atPath: path){
if let fileHandler = FileHandle(forReadingAtPath: path){
if let data = fileHandler.readDataToEndOfFile() as? Data{
if let dataStr = String(data: data, encoding: .utf8){
print(dataStr)
}
}
}
}
}
}
but now the problem is that when i read entire data from file it causes memory issue. I need to read some portion and process core data storing. Again get back and continue data read from where I left and go on.
My file has data as following:
Firstname,Lastname,Email,Phone,Title
Tanja,van Vlissingen-ten Brink,1,,Vulr(st)er
Berdien,Huismn Noornnen,2,,Filanager
Ailma,Ankit,3,,Vulr(st)er
Rzita,Salmani Samani,4,,Vulr(st)er
DeEora,Levaart,5,,Eerste Vulr(st)er
Kirsten,Veroor,6,,Vulr(st)er
Tristan,Haenbol,7,,Vulr(st)er
Manon,Bland,8,,Aankomend Vulrer
Naomi,Ruman,9,,Aankomend Vulrer
So I found that using NSFileHandler I can move or seek my file cursor to specific location in file, but it needs offset:
fileHandler = FileHandle(forReadingAtPath: path)
fileHandler.seek(toFileOffset: 10)
but I don't know how can I identify that I have read some specific number of lines, and now get back to next set of lines.
Also NSStream is the way to read File but I haven't explored it.
This question already has answers here:
Save An Image To Application Documents Folder From UIView On IOS
(6 answers)
Closed 5 years ago.
I want to in my app, be able to allow users to save large images(jpg) as well as data for each image(txt) and load the images/data. I'm having trouble figuring out where to save these images and text files. Userdefault wouldnt work because of the size of the image files and I don't want to save in the documents directory because then the user can access and potentially corrupt the data.
Where is a good place to save large data files for my app so I can load them later?
You can save and retrieve your files in application directory folder. Also you can use iCloud to save your files.
Use below code if you want to save and retrieve files from Directory folder .
Xcode 8.3.2 Swift 3.x. Using NSKeyedArchiver and NSKeyedUnarchiver
Reading file from documents
let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
let jsonFilePath = documentsDirectoryPath.appendingPathComponent("Filename.json")
let fileManager = FileManager.default
var isDirectory: ObjCBool = false
if fileManager.fileExists(atPath: (jsonFilePath?.absoluteString)!, isDirectory: &isDirectory) {
let finalDataDict = NSKeyedUnarchiver.unarchiveObject(withFile: (jsonFilePath?.absoluteString)!) as! [String: Any]
}
else{
print("File does not exists")
}
Write file to documents
NSKeyedArchiver.archiveRootObject(finalDataDict, toFile:(jsonFilePath?.absoluteString)!)
If for some reason you don't want to use the documents directory, or you want those data in a separate folder, or you don't want to permanently save them, you can also create your own temporary directory
saving data as file in a new directory (swift 3):
func saveToMyDirectory(data: Data, filename: String) {
var tempDirectoryURL = NSURL.fileURL(withPath: NSTemporaryDirectory(), isDirectory: true)
tempDirectoryURL = tempDirectoryURL.appendingPathComponent(filename)
do {
try data?.write(to: tempDirectoryURL)
} catch {}
}
Alternative approach: use Apple's UIDocumentInteractionController or UIActivityViewController and let the user choose how to save his/her documents:
https://developer.apple.com/documentation/uikit/uidocumentinteractioncontroller
https://developer.apple.com/documentation/uikit/uiactivityviewcontroller
I'm storing files in iCloud as a means of backup. Uploading and downloading files seems to work fine, but if I delete the app on a device and re-install the app on that same device, I no longer see the files that were uploaded to iCloud (even from that device just before deleting the app). The ubiquityIdentityToken is the same from all installs. I'm not trying to sync among devices, just store and retrieve. I can see the files in \settings\icloud\manage storage\ but not by running this code:
func createListOfSQLBaseFilesIniCloudDocumentsDirectory() -> [String]{
let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
var iCloudBackupSQLFiles = [String]()
do{
let directoryContents = try FileManager.default.contentsOfDirectory(at: iCloudDocumentsURL!, includingPropertiesForKeys: nil, options: [])
for myFile in directoryContents {
if myFile.pathExtension == "sqlite" {
let fileWithExtension = myFile.lastPathComponent
//backupSQLFiles is an array of full file names - including the extension
iCloudBackupSQLFiles.append(fileWithExtension)
}//if myFile
}//for in
} catch let error as NSError {
print("Unresolved error \(error), \(error.userInfo)")
}//do catch
return iCloudBackupSQLFiles
}//createListOfSQLBaseFilesIniCloudDocumentsDirectory
Any guidance would be appreciated. Swift 3, iOS 10, Xcode 8
Hard to believe no one else has had this issue. Again, this is for simple file storage and retrieval, not syncing dynamically among devices.
The gist of the issue is that iCloud does not automatically sync cloud files down to a new device. Your code must do that. So if you remove an app from a device (but not from iCloud) and reinstall that same app, the app will not see prior iCloud files. You can add new ones and see them with the code above, but you are really just seeing the local ubiquitous container copy. To see previous items you need to perform a metadataQuery on iCloud, parse the filenames of the files of interest from the metadataQuery results and then run
startDownloadingUbiquitousItem(at:) on each file. For example, make an array of files in iCloud from the metadataQuery results and put this do-catch in a for-in loop.
let fileManager = FileManager.default
let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents", isDirectory: true)
let iCloudDocumentToCheckURL = iCloudDocumentsURL?.appendingPathComponent(whateverFileName, isDirectory: false)
do {
try fileManager.startDownloadingUbiquitousItem(at: iCloudDocumentToCheckURL!)
print("tested file: \(whateverFileName)")
} catch let error as NSError {
print("Unresolved error \(error), \(error.userInfo)")
}//do catch
In AppDelegate.swift, on first launch, the intent is to place some sample docs in the local Documents folder, or in the iCloud Documents folder if iCloud is enabled.
var templates = NSBundle.mainBundle().pathsForResourcesOfType(AppDelegate.myExtension, inDirectory: "Templates")
dispatch_async(appDelegateQueue) {
self.ubiquityURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)
if self.ubiquityURL != nil && templates.count != 0 {
// Move sample documents from Templates to iCloud directory on initial launch
for template in templates {
let tempurl = NSURL(fileURLWithPath: template)
let title = tempurl.URLByDeletingPathExtension?.lastPathComponent
let ubiquitousDestinationURL = self.ubiquityURL?.URLByAppendingPathComponent(title!).URLByAppendingPathExtension(AppDelegate.myExtension)
// let exists = NSFileManager().isUbiquitousItemAtURL(ubiquitousDestinationURL!)
do {
try NSFileManager.defaultManager().setUbiquitous(true, itemAtURL: tempurl, destinationURL: ubiquitousDestinationURL!)
}
catch let error as NSError {
print("Failed to move file \(title!) to iCloud: \(error)")
}
}
}
return
}
Before running this, I delete the app from the device and make sure no doc of that name is in iCloud. On first launch, without iCloud, the sample docs copy properly into the local Documents folder. With iCloud, this code runs, and the setUbiquitous call results in an error that says the file already exists. The commented call to isUbiquitousItemAtURL also returns true.
What might be making these calls register that a file exists that I'm pretty sure doesn't? Thank you!
The file already exists, so just replace it
The primary solution...in all the trial and error, I'd forgotten to put "Documents" back in the url. Should be:
let ubiquitousDestinationURL = self.ubiquityURL?.URLByAppendingPathComponent("Documents").URLByAppendingPathComponent(title!).URLByAppendingPathExtension(AppDelegate.myExtension)
Without that, wrote the file to the wrong directory, and so I couldn't see it by normal means.