Data Persistence - ios

When the app is loaded, I want to know whether I have already saved the data to disk. If it is saved, I just load from disk, if not, I save.
for example, I want to save an array(person) using NSUserDefaults.standardUserDefaults()
Save Method:
let defaults = NSUserDefaults.standardUserDefaults()
if let savedPeople = defaults.objectForKey("people") as? NSData
people = NSKeyedUnarchiver.unarchiveObjectWithData(savedPeople) as! [Person]
But how can I know if I have saved the data or not in viewDidLoad()? I don't want to save every time I open the app, and it will also overwrite the data I modified before.

This is how you will do it:
override func viewDidLoad() {
super.viewDidLoad()
if let savedPeople = NSUserDefaults.standardUserDefaults().objectForKey("people") {
// Use savedPeople based on your need
} else {
NSUserDefaults.standardUserDefaults().setObject(myArray, forKey: "people")
}
}

Related

How to use static variable to keep track of the increment?

I am using static variable to keep track of the number of hotels added in firebase. Let's say, in start, the value of static variable is 1, then when data is added in firebase, the number is incremented to 2. But, when again the data is added and this view controller is loaded again, the value of static variable gets back to 1 and the new data posted replace the older data. How can I manage that thing? I know that pretty basic and silly question, but sometimes the brain just don't work. Below is the code.
class OwnerAddListing2ViewController: UIViewController {
static var numberOfHotels:Int = 1
let DataForCurrency : [String] = ["USD", "Rs"]
let DataForDays : [String] = ["PerNight", "PerWeek", "PerMonth"]
override func viewDidLoad() {
super.viewDidLoad()
currencyField.inputView = currencyPicker
daysField.inputView = daysPicker
}
#IBAction func nextButtonTapped(_ sender: UIButton) {
let currency = currencyField.text
let charges = chargesField.text
let days = daysField.text
let phone = phoneField.text
let email = emailField.text
//Get reference to firebase Database
let db = Firestore.firestore()
//Post data tw database
db.collection("Property").document("\(Auth.auth().currentUser!.uid)").collection("Hotel").document("\(OwnerAddListing2ViewController.numberOfHotels)").setData(["Currency": currency!, "Charges" : charges!, "Days" : days!, "Phone" : phone!, "EmailAddress" : email!], merge: true) {(error) in
if error != nil {
}
else {
print("Data Posted Succesfully")
OwnerAddListing2ViewController.numberOfHotels = OwnerAddListing2ViewController.numberOfHotels + 1
}
}
}
Static Variables only keep the data saved in One application Life cycle. As soon as you restart the application, the static variable will be initialized from the default value. If you want to persist the value of your variables throughout , may be you should consider using UserDefaults which can store small amount of information. But be careful not to store any sensitive data like passwords.

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.

How to check which items are already saved in Core Data from a given array

My goal is
I want to store an array of data into coreData.
Here is my array
let storageArr = ["Teja", "Teja two", "Teja Ri", "Bhanu", "Stack", "Stack over", "Stack over flow"] as NSObject
And if user is typing in the Textfield I need to show them(related to that character) in drop down (tableView).
Let's say user typed "Te". I need to show Teja, Teja two, Teja Ri in table view.
I have done everything. But am unable to fetch only Teja, Teja two, Teja Ri from array.
Here is the code which I tried
override func viewDidLoad() {
super.viewDidLoad()
savingDataToLocalDB()
}
#IBAction func searchBtnClicked(_ sender: Any) {
setUpData(searchKeyword: searchTF.text)
}
func savingDataToLocalDB(){
let saveContext = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext
let data: SearchList = NSEntityDescription.insertNewObject(forEntityName: "SearchList",
into: saveContext!) as! SearchList
data.names = storageArr
do {
try saveContext?.save()
print("data saved")
} catch {
print(error)
}
}
func setUpData(searchKeyword: String){
let fetchContext = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext
let fetchReq = NSFetchRequest<NSManagedObject>(entityName: "SearchList")
do {
let data = try fetchContext?.fetch(fetchReq)
var filteredData = data as! [SearchList]
print(filteredData[0].names) // Am getting data here
//following line is not working
let wasfilteredData = filteredData.filter { $0.names!.lowercased().contains(searchKeyword.lowercased()) } as NSArray
} catch {
print(error)
}
}
Here in viewDidLoad() am calling savingDataToLocalDB() which means storing in coreData
And in #IBAction am fetching data from coreData.
But in setUpData(searchKeyword: String) method am unable to filer data consists of "Te"(user entered in textfiled)
Please find the following image. That's how I created entity
Where I am doing wrong?
It appears that you have a SINGLE entity in your database, with a transformable property of 'names'. That property is a transformable Array. Which means that it is stored in the database as Data and is encoded and decoded automatically. The database cannot search or sort this array because 1) it is a single property, not collection of entities and 2) it is stored as data. So all management must be done in memory, which means you have none of the benefits of core-data while having all of the cost.
Next, in your code you fetch ALL of the SearchList. Your database contains one so you get back and array of length 1. You then filter that Array of SearchList - NOT the array of names, and you get back the same searchList Array that you started with because that SearchList indeed passes the test.
Perhaps you want:
let wasfilteredData = filteredData.first?.names.filter { $0.lowercased().contains(searchKeyword.lowercased()) } as NSArray
Or perhaps you should consider having each entity contain only term, and then you search using predicates.

Deleting items that have been saved using NSUserDefaults

I have a table view that has its data saved with the following function.
func saveWebsites() {
let websitesData = NSKeyedArchiver.archivedDataWithRootObject(loadedWebsites)
NSUserDefaults.standardUserDefaults().setObject(websitesData, forKey: KEY_WEBSITES)
NSUserDefaults.standardUserDefaults().synchronize()
}
I have tried to allow the user to delete an item from the table view, but what happens is that the user can delete an item from the table view, but when the app reloads the item is still there. The code I am using to try and delete it is this:
func deleteWebsites() {
if let data = NSUserDefaults.standardUserDefaults().objectForKey(KEY_WEBSITES) as? NSData {
let site = NSKeyedUnarchiver.unarchiveObjectWithData(data)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
Anybody know my problem?

Saving And Loading An Integer On Xcode Swift

I am trying to save an integer so it shows up after I switch the page or close the game. I made this to change the number but how do I save that number when I switch pages and load it when I go back to that page.
Change Code:
#IBAction func MoneyPress(sender: AnyObject) {
Money += 1
var MoneyNumberString:String = String(format: "Dollars:%i", Money)
self.DollarsLabel.text = (string: MoneyNumberString)
}
If it isn't a lot of data, the strategy I use to save data, pass it between pages, and persist it between app runs is to store the value in NSUserDefaults.
Setting A Value: When you first get or when you change the data, store it in NSUserDefaults.
#IBAction func MoneyPress(sender: AnyObject) {
Money += 1
var MoneyNumberString:String = String(format: "Dollars:%i", Money)
self.DollarsLabel.text = (string: MoneyNumberString)
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults() //This class variable needs to be defined every class where you set or fetch values from NSUserDefaults
defaults.setObject(MoneyNumberString, forKey: "money")
defaults.synchronize() //Call when you're done editing all defaults for the method.
}
Loading A Value: When you need to get the values, just grab it from NSUserDefaults.
#IBAction func loadButton(sender: UIButton) {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var money = defaults.valueForKey("money") as? String
dollarLabel.text! = money
}
To remove the stored data, all you need to do is call the removeObjectForKey function for each key previously set.
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.removeObjectForKey("money")
defaults.synchronize()
Helpful Source on NSUserDefaults:
NSUserDefulats Class Reference: Link here.
You can use NSUserDefaults for this.
Save Value
NSUserDefaults.standardUserDefaults().setInteger(money, forKey: "MoneyKey");
Retrieve Value
NSUserDefaults.standardUserDefaults().integerForKey("MoneyKey");
So can retrieve the value in viewDidLoad and load the data:
override func viewDidLoad()
{
super.viewDidLoad()
loadWebView()
var money = NSUserDefaults.standardUserDefaults().integerForKey("MoneyKey");
}
When you come to the view for the first time the value of money will be 0.
Remove Value
If you need to remove a value from NSUserdefaults, you can use:
NSUserDefaults.standardUserDefaults().removeObjectForKey("MoneyKey")

Resources