Swift how to choose local database for offline dictionary? - ios

I want to make an offline translator (dictionary) with swift 4.
I need advice on where to store words with translation.
The words with the translation are now in the dsl format and I search for this particular file, but on average it takes 2-3 seconds to search.
Is it possible to speed up the search? If i put words with translation in core data or realm database, its help or not ?

Realm Swift enables you to efficiently write your app’s model layer in a safe, persisted and fast way. Here’s what it looks like:
// Define your models like regular Swift classes
class Dog: Object {
#objc dynamic var name = ""
#objc dynamic var age = 0
}
class Person: Object {
#objc dynamic var name = ""
#objc dynamic var picture: Data? = nil // optionals supported
let dogs = List<Dog>()
}
// Use them like regular Swift objects
let myDog = Dog()
myDog.name = "Rex"
myDog.age = 1
print("name of dog: \(myDog.name)")
// Get the default Realm
let realm = try! Realm()
// Query Realm for all dogs less than 2 years old
let puppies = realm.objects(Dog.self).filter("age < 2")
puppies.count // => 0 because no dogs have been added to the Realm yet
// Persist your data easily
try! realm.write {
realm.add(myDog)
}
// Queries are updated in realtime
puppies.count // => 1
// Query and update from any thread
DispatchQueue(label: "background").async {
autoreleasepool {
let realm = try! Realm()
let theDog = realm.objects(Dog.self).filter("age == 1").first
try! realm.write {
theDog!.age = 3
}
}
}
For a dictionary app. I'd definitely go with Realm. it uses NSFileProtection and encryption so people won't be able to steal your translation data.
Setup RealmSwift for your projects

Related

How to sort cells in collectionView from newest to oldest. Realm, Swift

These are my realm classes and functions:
class RealmItems: Object {
#objc dynamic var header = String()
#objc dynamic var specification = String()
#objc dynamic var day = Int()
#objc dynamic var month = Int()
#objc dynamic var deadline = String()
#objc dynamic var status = String()
}
class RealmModel {
static let shared = RealmModel()
private let realm = try! Realm()
func addTask(headerTask: String, specificationTask: String, dayTask: Int, monthTask: Int, deadlineTask: String, statusTask: String) {
let new = RealmItems()
new.header = headerTask
new.specification = specificationTask
new.day = dayTask
new.month = monthTask
new.deadline = deadlineTask
new.status = statusTask
try! realm.write {
realm.add(new)
}
}
func deleteTask(name: RealmItems) {
try! realm.write {
realm.delete(name)
}
}
func getTasks() -> [RealmItems] {
var arrayTasks: [RealmItems] = []
for task in realm.objects(RealmItems.self) {
arrayTasks.append(task)
}
return arrayTasks.sorted{$0.day > $1.day}
}
}
function getTasks() doesn't work the way i want it works. Now collection shows oldest cells higher than newest - that's wrong. I want to newest were higher than oldest
A few things to be aware of.
First, read Realm Objects Are Lazily Loaded e.g. don't cast them to an array or sort them using Swift functions. Well, you can but it can lead to other issues so best to avoid that unless there's a specific use case that requires it.
Secondly, Realm Results do not have a guaranteed order unless you specify that order. On the other hand, a List object maintains it's order.
Ordering a Results collection is simple, and actually keeps the objects in the results in order as object properties change, for example
let orderedResults = realm.objects(RealmItems.self).sorted(byKeyPath: "day", ascending: false)
Will load all of the RealmItems objects sorted by day, descending (e.g. 20th will be at the top and the 1st will be at the bottom)and the results will maintain their order. e.g. if a day changes, it will auto sort within the list. Pretty magical, huh?

Is there any pagination like code for saving JSON data to Realmdatabase

I have a button and below it is the table view. Table view cell has some random data.On button click I am calling the the api(function name is : api.urlRequest(userID: 80, businessUnitID: 2) ) .I have an API that has 35,0000 entries. What I want is to save that data in Realm database. The problem is that, when I am calling the save function, my UI freezes. I am appending the JSON data to Model and then saving it to database. I can get the start index and end index of the the JSON data.
What I tried was to call the API on background thread and when saving function is called, I am calling it on main thread. But this didn't worked.
class ViewController: UIViewController,getAdhocJSONDelegate{
let realm = try! Realm()
#IBOutlet weak var tableViewRef: UITableView!
var array = [NSDictionary]()
var adhocData : [AdhocModel] = []//for appending the JSON data to the model
var adhocDB : Results<AdhocDB>?// for accessing the database
let api = AdhocAPIParamteres()
var adhocJSONDatafromAPI : NSDictionary!
override func viewDidLoad() {
super.viewDidLoad()
adhocDB = realm.objects(AdhocDB.self)
}
#IBAction func buttonPressed(_ sender: Any) {
print("BUtton Tapped")
api.urlRequest(userID: 80, businessUnitID: 2)
api.delegate = self
}
func appTutorialData(json: NSDictionary) {
adhocJSONDatafromAPI = json
let apiData = adhocJSONDatafromAPI.value(forKey: "data") as! [NSDictionary]
print("Start Index of the data : ",apiData.startIndex)
print("End Index of the data : ",apiData.endIndex)
apiData.forEach { (abc) in
let model = AdhocModel()
model.site_id = abc.value(forKey: "site_id") as! Int
model.atm_id = abc.value(forKey: "atm_id") as! String
model.site_address = abc.value(forKey: "site_address") as! String
adhocData.append(model)
print("data appended")
DispatchQueue.main.async {
self.saveToDb(data:model)
}
}
func saveToDb(data: AdhocModel) {
let adhoc = AdhocDB()
try! realm.write {
adhoc.SiteId = data.site_id
adhoc.AtmId = data.atm_id
adhoc.SiteAdress = data.site_address
realm.add(adhoc)
}
}
}
I want to save data in such a way that my UI doesn't freeze.
There are a few issues with the code and writing data to Realm on a background thread is covered in the documentation so I won't address that. Following that design pattern will correct the UI lockup.
This is another issue
func saveToDb(data: AdhocModel) {
**let adhoc = AdhocDB()**
You want to write your populated model to realm, but AdhocDB is a Results object, not a Realm model object. Additionally the realm object created in appTutorialData which is model, is passed to saveToDb, then another object is created and then populated with data from the first object. There's no reason to do that (in this code)
Assuming AdHocModel is a Realm object, this is much cleaner
func appTutorialData(json: NSDictionary) {
adhocJSONDatafromAPI = json
let apiData = adhocJSONDatafromAPI.value(forKey: "data") as! [NSDictionary]
print("Start Index of the data : ",apiData.startIndex)
print("End Index of the data : ",apiData.endIndex)
apiData.forEach { (abc) in
let model = AdhocModel()
model.site_id = abc.value(forKey: "site_id") as! Int
model.atm_id = abc.value(forKey: "atm_id") as! String
model.site_address = abc.value(forKey: "site_address") as! String
try! realm.write {
realm.add(model)
}
}
}
You're going to want to wrap that write within a background thread (again, see the documentation) something like this
DispatchQueue(label: "background").async {
autoreleasepool {
.
.
.
try! realm.write {
realm.add(model)
}
}
}
You may ask about populating your array adhocData.append(model). We don't know what you're doing with it but if you're using it as perhaps a dataSource for a table view or some other UI element, you may want to consider using a Results object instead of an Array.
A significant advantage is, if you have 35,000 objects, that's a pretty sizable array and if you have more, it could overwhelm the device as ALL of that data is stored in memory. However, Results objects are lazily loaded so you could have a much larger dataset without overwhelming the device.
Additionally, when Realm objects are stored in an array, they 'Disconnect' from Realm and loose Realm functionality - they will not auto-update nor will changes to the actual object in Realm be reflected in that array nor can you just update the object - it doesn't appear to have a primary key.
However, if you populate a Results object with those models, they will be live updating - so if for example the atm_id changes in Realm, that object will automatically be updated. If you need to change a property you can change it directly on that object within a write transaction.
So the pattern would be to have a class var of Results and load your objects into those results within viewDidLoad. As you add more models, the results object will automatically be updated.
To keep your UI fresh, you would want to add observers (aka Notifications)to those Results so you can be notified when an object is updated so you can reload your tableView for example.

Realm swift thread safe variable

I am new to Realm and I am wondering if it is good idea to store primary key of object in View controller and object as stored property.
class VC: ViewController{
public var id:Int!
private var customer:Customer{
get{
return DBO.loadCustomer(for: id)
}
}
}
class DBO{
public static func loadCustomer(for id: Int) -> Customer{
let realm = try! Realm()
return realm.object(ofType: Customer.self, forPrimaryKey: id)!
}
}
I am assuming that this because of this my app will be thread safe, because every time thread will try to access customer it will fetch object from db. But I wonder if this is efficient for single object and array of objects.
As of Realm 2.2 you could use thread-safe references (see here):
Now, you can create thread-safe references for all types that were
previously thread-confined within a Realm, and passing objects between
threads becomes a simple, three-step process:
Initialize a ThreadSafeReference with the thread-confined object.
Pass that ThreadSafeReference to a destination thread or queue.
Resolve this reference on the target Realm by calling
Realm.resolve(_:). Use the returned object as you normally would.
And a code sample, from the source above:
let realm = try! Realm()
let person = Person(name: "Jane") // no primary key required
try! realm.write {
realm.add(person)
}
let personRef = ThreadSafeReference(to: person)
DispatchQueue(label: "com.example.myApp.bg").async {
let realm = try! Realm()
guard let person = realm.resolve(personRef) else {
return // person was deleted
}
try! realm.write {
person.name = "Jane Doe"
}
}
But, your approach should also work fine.

Swift Realm - Query Empty Set but Data is in RealmBrowser

I am new to iOS developing. I try to receive the data which is stored in realm Database but sometimes I get an empty set although I can see the data in RealmBrowser. Often it works, but it is not reliable.
At first I clear the stored data to get sure I am not going to display old data.
After that I insert it to the RealmDatabase and in addition to that I query all stored data to display it in the tableView.
Here is my code (Swift 3):
let realm = try! Realm()
try! realm.write {
// clear all old data stored as fahrt.self
//realm.delete(realm.objects(fahrt.self));
realm.delete(realm.objects(fahrt.self));
let nsArray = try! JSONSerialization.jsonObject(with: data as Data, options: []) as! NSArray;
for eintrag in nsArray{
realm.create(fahrt.self, value: eintrag, update: false)
}
try! realm.commitWrite();
}
DispatchQueue.main.async {
let temp = ui as! DisplayViewController;
let realm = try! Realm()
let result = realm.objects(fahrt.self)
ui.showData(res: result);
// sometimes result is empty , don't know why
}
RealmModel:
class fahrt: Object {
dynamic var id: Int = 0
dynamic var date = ""
dynamic var from_lang: Double = 0.0
dynamic var from_lon: Double = 0.0
dynamic var to_lang: Double = 0.0
dynamic var to_lon: Double = 0.0
override static func primaryKey() -> String? {
return "id"
}
}
First of all, you don't need to include realm.commitWrite() inside a realm.write { } block. realm.commitWrite() is used in conjunction with realm.beginWrite(), but both of these are automatically called inside realm.write { }. :)
Secondly, when you perform a write transaction on a background thread, the changes are automatically exposed on the main thread on the next iteration of the run loop. However, in some cases, when immediately calling code on the main thread after a background write, it's possible this hasn't happened yet. To ensure Realm has the very latest copy of its data on the main thread, you should call realm.refresh() in that dispatch block before you perform the query.

Re-initializing a Realm object in a different thread

Is running a query the only way of re-initializing a Realm Object for use in a different thread? Is there no way to somehow grab some kind of reference for use with Realm, so that the query doesn't have to be completely from scratch? It would be nice to have some kind of guarantee that the object is for the same record (if it exists and the query for that "ref" is successful), without having to add a primary key just for this one purpose.
This outlines my situation:
func construct(name: String, tokens: [String]) -> Document {
let doc = Document()
let realm = try! Realm()
try! realm.write {
realm.add(doc)
}
DispatchQueue.global(qos: .userInitiated).async {
let realm = try! Realm()
// Some long running task to convert `tokens` into `[Blurb]`
// var blurbs: [Blurb]
// What's the recommended way of re-initing that new `Document`?
// let sameDoc = .....
try! realm.write {
sameDoc.blurbs.append(blurbs)
}
}
return doc
}

Resources