Prevent Realm from writing JSON twice - ios

I'm new to Realm and iOS. I'm working on an app(written in Swift) that has a feature for the user to search for golf courses. I have a JSON file with roughly 18K courses in it. So I wanted to use Realm so I can quickly search through these courses in my app without it slowing down the user experience. I was able to get my JSON file written to the Realm Browser and can retrieve and search through the items, which has made it a LOT faster.
The problem I'm is I have the code in my App Delegate because I wanted to send the JSON items to my Realm Browser upon the app's launch. But if the app is started again then it writes the JSON file again, which creates duplicate golf courses in my Realm Browser.
Any suggestions on how I should do this so I can write the JSON file to the browser without getting duplicates each time the app is launched?
Thanks!
My code for writing my JSON file to my Realm Browser:
let dataManager = DataManager.getGolfCoursesFromFileWithSuccess { (data) -> Void in
let json = JSON(data: data)
if let courseArray = json.array {
for course in courseArray {
let golfCourseName: String? = course["biz_name"].string
let city: String? = course["e_city"].string
let state: String? = course["e_state"].string
if golfCourseName != nil {
let course = Course()
course.name = golfCourseName!
course.city = city!
course.state = state!
let realm = try! Realm()
try! realm.write {
realm.add(course)
}
}
}
}
}

Just check to see if the data is stored already.
I.e.
if try! Realm().objects(GolfCourse).count == 0 {
// your loading code here.
}

I figured it out. Because this was a data set that I wanted the user to have when they initially start using the app I learned how to bundle a Realm file with this data and put the file directly in my Xcode project. I then configured this file using the method:
"NSBundle.mainBundle().pathForResource("MyBundledData", ofType:"realm")"

Related

How to create a pre bundled realm file and upload data to it?

I am new to Realm and I want to ship a pre bundled Realm file my app, however the realm documentation is unclear to me on how to actually create a .realm file and also upload the desired pre bundled data to it. I have not been able to find any solution via SO, Google, or Youtube. Step-by-step instructions would be very helpful.
We're still looking at ways to officially make generating pre-populated Realms more easy. It's definitely a desired feature for the Realm Browser, but due to the way that Realm files require a migration when changing the schema, it's somewhat tricky to incorporate into a UI. It's definitely something we want to improve down the line.
At the moment, the Browser has a converter in it that can perform minimal data import like CSV files.
The Realm documentation is saying that the easiest way for you to produce a pre-populated Realm specific for your apps needs is to build a separate macOS app whose sole role is to generate the Realm file, pre-populate the data and then expose the resulting Realm file so you can copy it to your app.
The operation to do this is just like any normal Realm interaction (try! Realm() is what causes the file to actually be initially created), except you manually interact with the Realm file on disk upon completion.
I'm working on an app for an iOS conference, and the schedule data for the event is going to be stored in a Realm that is pre-bundled with the app when it ships.
Since I thought creating an extra macOS app would be overkill for an iOS app, I instead used iOS unit tests that would generate the pre-bundled Realm from scratch every time it was executed.
class Tests: XCTestCase {
func testGenerateNewDefaultRealm() {
let sources = [conferences, sponsors, conferenceDays] as [Any]
XCTAssert(generateDefaultRealm(named: "MyConferenceRealm.realm", sources: sources))
}
}
extension Tests {
public func generateDefaultRealm(named name: String, sources: [Any]) -> Bool {
// Create and configure the Realm file we'll be writing to
let realm = generateRealm(named: name)
// Open a Realm write transaction
realm.beginWrite()
// Loop through each source and add it to Realm
for source in sources {
if let objectArray = source as? [Object] {
for object in objectArray {
realm.add(object)
}
}
else if let objectDictionary = source as? [String : Object] {
for (_, object) in objectDictionary {
realm.add(object)
}
}
}
// Commit the write transaction
do {
try realm.commitWrite()
}
catch let error {
print(error.localizedDescription)
return false
}
// Print the file location of the generated Realm
print("=====================================================================")
print(" ")
print("Successfully generated at")
print(realm.configuration.fileURL!.path)
print(" ")
print("=====================================================================")
return true
}
public func generateRealm(named name: String) -> Realm {
let exportPath = NSTemporaryDirectory()
let realmPath = exportPath.appending(name)
// Delete previous Realm file
if FileManager.default.fileExists(atPath: realmPath) {
try! FileManager.default.removeItem(atPath: realmPath)
}
// Create new Realm file at path
let objectTypes: [Object.Type] = [Conference.self, ConferenceDay.self, SessionBlock.self, Event.self, Presentation.self,
Session.self, Location.self, Speaker.self, Sponsor.self, Venue.self]
let configuration = Realm.Configuration(fileURL: URL(string: realmPath), objectTypes: objectTypes)
let realm = try! Realm(configuration: configuration)
return realm
}
}
Running this unit test will generate a new Realm file in the NSTemporaryDirectory() directory of the Mac, and then feed in a set of Array and Dictionary objects of un-persisted Realm Object instances that are created each time the test is run. The location of the final Realm is then printed in the console so I can grab it and move it to the app bundle.

Swift 3 Core Data fetch issue

The function below is used to get data for tableview
func fetchProfiles() -> Array<User> {
var users: Array<User> = []
let fetchRequest: NSFetchRequest<Profile> = Profile.fetchRequest()
let fetchedData = try! context.fetch(fetchRequest)
if (!fetchedData.isEmpty) {
print(fetchedData)
for i in 0...fetchedData.count {
var user: User = User()
user.userName = fetchedData[i].profileName
user.userSurname = fetchedData[i].profileSurname
user.userPhoto = fetchedData[i].profilePhoto
users.append(user)
}
return users
}
else {
return users
}
}
"User" is a simple struct. "Profile" is an entity in Core Data. I create an array of structs to use them for cells in table. Code has no errors(for xCode). When there is no fetched data, it skips appending array, but when there is some info, app crashes with error:
fatal error: NSArray element failed to match the Swift Array Element
type
For unknown reason, there were problems with auto generated headers of Core Data entities. I just deleted all files from folder:
/Users/user/Library/Developer/Xcode/DerivedData/MyApp/Build/Intermediates/MyApp.build/Debug-iphonesimulator/MyApp.build/DerivedSources/CoreDataGenerated
then cleaned my project with
command+shift+k
checked my core data model for some possible issues and rebuilt the whole project.
Magic.

How to create a pre-prepared database and then load that data in my ios swift app?

I have a lot of data text in a couple of text files. How can I efficiently create a (Realm?) database with the data from those text files so I can add the database to my Xcode project and load the data into my app?
I have seen many tutorials on how to create Realm database with user entered data and then load it, but none with pre-made databases. I don't know if Realm is the right program to do this, but I looked really well.
I have downloaded the Realm Browser, but I could only view databases and couldn't find out how to create them easily.
EDIT:
I managed to create a database in Realm and put it in my xcode folder.
Then I try to load it like this, but let peoples doesn't contain the file's data, what am I missing:
let path = NSBundle.mainBundle().pathForResource("data", ofType: "realm")
var config = Realm.Configuration(fileURL: NSURL(fileURLWithPath: path!))
config.readOnly = true
let realm = try! Realm(configuration: config)
let peoples = realm.objects(Data)
Data is class which defines the schema:
class Data : Object {
dynamic var name = ""
dynamic var country = ""
dynamic var discription = ""
dynamic var image = ""
dynamic var cartoon = ""
dynamic var startYear = 0
dynamic var endYear = 0
}
An image of the realm file I'm trying to load:
Thanks for help!
Create a Sample Model:
final class ContentModel: Object {
dynamic var title = ""
dynamic var content = ""
dynamic var listName = ""
dynamic var id = 0
override static func primaryKey() -> String? {
return "id"
}
}
Create realm database:
let model = ContentModel()
model.id = 1
model.listName = "List Item 1"
model.title = "Title of content 1"
model.content = "Sample Text"
// Get the default Realm
let realm = try! Realm()
// Persist your data easily
try! realm.write {
realm.add(model)
}
Use this line to print the path to database:
print(Realm.Configuration.defaultConfiguration.fileURL!)
Now follow these steps (explained by realm)
Drag the new compacted copy of your Realm file to your final app’s Xcode Project Navigator.
Go to your app target’s build phases tab in Xcode and add the Realm file to the “Copy Bundle Resources” build phase.
At this point, your bundled Realm file will be accessible to your app. You can find its path by using NSBundle.main.pathForResource(_:ofType:).
Here's the code to get your bundled resource:
open class func getBundledRealm() -> Realm {
let config = Realm.Configuration(
// Get the URL to the bundled file
fileURL: Bundle.main.url(forResource: "default", withExtension: "realm"),
// Open the file in read-only mode as application bundles are not writeable
readOnly: true)
// Open the Realm with the configuration
let realm = try! Realm(configuration: config)
return realm
}
To Test your database:
let realm = RealmUtils.getBundledRealm()
// Read some data from the bundled Realm
let results = realm.objects(ContentModel.self)
for item in results {
print("Id: \(item.id)")
}
Realm Browser (as the name said) is just a browser.
for create pre-prepared database you should write some codes,
create empty database and insert data to it, then save it to Documents folder of simulator , comment insert code and copy generetated database from Documents folder, and add it as resource to your xCode project.
if your database static and you don't want to change anything on it simply just load your database with resource bundle path:
[[NSBundle mainBundle] pathForResource:#"nameOfFile" ofType:#"realm"];
but if you want to change some data you must copy it again to writable folder like Documents (for first time only)
I think what you are looking for is something like
https://github.com/Ahmed-Ali/JSONExport or JSONExportV in the Mac App Store
or
http://realmgenerator.eu/
If you already have your JSON, then these will generate the Realm Models that you can drop into your project. JSONExport supports way more languages and seems to work better for me with Swift. Just make sure you set the language to "Swift - Realm".

How can I take out a value as a string from RealmSwift?

I am developing an iOS app with RealmSwift by referring here. https://realm.io/docs/swift/latest/#in-memory-realms
And, what I don't understand is, how can I indicate the location(record and column) of the data in the realm file.
I've saved a realm file that named "DicData.realm" on the main folder where the same location as ViewController.swift is saved.
The data of DicData.realm is something like this:
1,face,423
2,rain,435
3,airplane,555
If I run the code below, it only printed like this: "results: Results ( )". It seems the filter method is just neglected. When I want to take out the word "airplane" and store in a variable as a string, how should I modify my code?
override func didMoveToView(view: SKView) {
func test()->Int {
let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "DicData"))
let results = realm.objects(DBData).filter("id == 3")
print("results: \(results)")
}
class DBData: Object {
dynamic var id = 0
dynamic var name = ""
dynamic var code = ""
}
You're referring here to the chapter of in-memory Realms and have setup your configuration to use those. An in-memory Realm is not a file. It lives exclusively in memory and is not actually written to disk.
If you've a prepared file, you want to bundle with your app, you need to make sure, that it is part of the Copy-Files Build Phase of the corresponding target of your Xcode project.
You can then copy the file from the app bundle initially via NSFileManager to a path, where the copy can be modified.
let defaultURL = Realm.Configuration.defaultConfiguration.fileURL!
let bundleURL = NSBundle.mainBundle().URLForResource("DicData", withExtension: "realm")!
do {
try NSFileManager.defaultManager().copyItemAtURL(bundleURL, toURL: defaultURL)
} catch {}
}

How do I store private app data on Google Drive with my iOS app?

I have an iOS app that has a local database. I'd like to back that up for users who choose to sign in with Google. The web (https://developers.google.com/drive/web/appdata) and android (https://developers.google.com/drive/android/appfolder) have guides on how to do this, but I can't find a similar one for iOS. Does it exist?
If you already have code to upload a file to the user's Drive account, it is very easy to switch to uploading into the private app folder instead. When making the Files.insert call, the file will be added to all of the folders listed in the parents[] array. (If this array is empty, by default the file is added to the root folder.) To upload the file into the private app data folder, simply set the parents[] array to appfolder. You have to do this at the same time as uploading the file, because once it has been uploaded the file can't be moved between the user's drive and your app's private data folder.
(Note: you may need to use the regular REST API to do this, because Google's Drive API for iOS docs do not show any methods for actually uploading a new file to Drive.)
Check this how this is working for me in swift 4.2 and above:
let googleDrive: GTLRDrive_File = GTLRDrive_File()
googleDrive.name = "name.json"
googleDrive.parents = ["appDataFolder"]
let uploadParam: GTLRUploadParameters = GTLRUploadParameters(data: data, mimeType: "application/json")
uploadParameters.shouldUploadWithSingleRequest = true;
let queryDrive: GTLRDriveQuery_FilesCreate = GTLRDriveQuery_FilesCreate.query(withObject: metadata, uploadParameters: uploadParam)
queryDrive.fields = "id"
self.service.executeQuery(queryDrive) { (result, response, error) in
if let file = response as? GTLRDrive_File {
if (error == nil) {
print(file.identifier)
/// your code here
} else {
// handle error part
}
}
else {
//handle exception part
}
}
"data" the json data you get this like below
let param = [["key": "value"], ["key": "value"], ["key": "value"]]
let data = try JSONSerialization.data(withJSONObject: param, options: .prettyPrinted)

Resources