How to remove all data from DynamoDB without for loop? - ios

Suppose i have 100 data in DynamoDB then to remove all data i have to execute for loop .So is there any way to remove all data without for loop ?
let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
for var key in 0 ..<100 {
let itemToDelete: CategoriesDB = CategoriesDB()
itemToDelete._index = key
dynamoDbObjectMapper.remove(itemToDelete, completionHandler:
{(error: Error?) -> Void in
if let error = error {
print(" Amazon DynamoDB Save Error: \(error)")
return
}
print("A Category was deleted.")
})
}

If you want to remove all the data in a DynamoDB table, it is better to just delete the table and create it again as that wouldn't cost Write Capacity Units.

Ignoring the swift SDK for a moment, DynamoDB has the DeleteItem method to remove a single item from a table, but it also has a batch method.
Quote
In addition to DeleteItem, Amazon DynamoDB supports a BatchWriteItem
action for deleting multiple items at the same time.
The problem is, I can't see a way to access BatchWriteItem through the swift SDK. Even if you could use BatchWriteItem, it can only take 25 items at a time, so you would still end up having to write a loop.
Sorry its not a more complete answer, but thought this might be useful. As far as I know there isn't a simple (single) method for doing this.

Related

Is there a way to pull the last CoreData Entry only?

I have a dashboard view that I'd like to display a few variables from my last entered CoreData entry. However, I can't figure out how to fetch only the last data entered into a variable so I can display it. Any ideas?
EDIT: I'm trying to setup a NSFetchRequest inside of a called function that is called only onappear. However, I'm getting errors and am lost.
func singleEntryPull() -> [Item] {
let request: NSFetchRequest<Item> = Item.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "todaysDate", ascending: false)]
request.fetchLimit = 1
let singleEntry = FetchRequest(fetchRequest: request)
return singleEntry
}
And then the return from the function should only show 1 result and I can then use the returned value to display the variables I need?
Well, not sure if this is the cleanest or best way to do this but I got it working like I want for now until a better solution comes up. I'm still using #FetchRequest which im now aware is pulling data live and updating it, but that might work as if someone keeps the app open overnight and updates it in the morning, I'd want it to display that latest entry. I used this:
ForEach(singleEntry.prefix(1)) { item in
A fetch limit of 1 on a fetch request will return you a single value. However, when you're setting up a #FetchRequest, you're doing more than this - you're making the initial fetch and then continuing to monitor the context for changes, so it live updates. This monitoring only uses the predicate of your fetch request.
Depending on your order of operations, you could be seeing the latest data, and then any new data inserted since you started that view. My experiments with the SwiftUI core data template project prove this out - on initial run you get the a single latest entry, but as you add newer ones, the fetch-limited screen picks up the new entries.
Depending on how this view is actually used, you have two choices - you can do an actual fetch request on appear of the view and store the result as an observable object, or you can make sure you only ever use the first record from the fetch request's results array, which will always be the latest record because of your sort ordering:
var body: some View {
if let latest = singleEntry.first {
// Some view describing the latest entry
} else {
Text("No record")
}
}

Does simply pulling all users from firebase database and putting them into an array efficient?

I have a simple iOS app that part of the app grabs all the users from firebase database so you can search them, and do different functions. Now my question is, if/when the app grows and there are thousands of users, does pulling all the users from the database and adding them to an array of [user]'s, still not crash or slow the app? I see so many people on youtube just loop through firebase and grab all the users. Please note I am excluding profile photos so there is no downloading images involved, just strings. I have some code I thought could solve this possible problem, but I am starting to wonder if there even is a problem with just fetching all the users from firebase and putting them into and array and then displayed in a tableview.
Here is some of my code right now, but it still I notice when I type in one letter, then turn airplane mode on, it downloaded all the users. I really need some help or some advice on this one, thanks.
var checklers = [String]()
func updateSearchResults(for searchController: UISearchController) {
if searchController.searchBar.text == "" {
filteredUsers = users
}
else {
print("refreshing")
if let uidi = FIRAuth.auth()?.currentUser?.uid {
view.addSubview(activityInd)
activityInd.startAnimating()
filteredUsers.removeAll()
checklers.removeAll()
let ref = FIRDatabase.database().reference()
ref.child("users").queryOrderedByKey().observe(.value, with: { snapshot in
if let userr = snapshot.value as? [String : AnyObject] {
for (_, velt) in userr {
if let usernamerr = velt["Username"] as? String {
if usernamerr.lowercased().contains(searchController.searchBar.text!.lowercased()) {
let userNew = usera()
if let name = velt["Full Name"] as? String, let uidd = velt["uid"] as? String {
userNew.name = name
userNew.username = usernamerr
userNew.uid = uidd
if self.checklers.contains(uidd) {
print("already")
}
else {
if userNew.uid != uidi {
self.filteredUsers.append(userNew)
self.activityInd.stopAnimating()
self.checklers.append(uidd)
}
print("added a user")
}
}
}
}
self.tableViewSearchUser.reloadData()
}
}
})
ref.removeAllObservers()
}
// filteredUsers = users.filter( { ($0.username?.lowercased().contains(searchController.searchBar.text!.lowercased()))! })
}
tableViewSearchUser.reloadData()
}
Please add any advice, thanks.
Just for searching one or two users, each time a user would need to fetch all the records and putting them all in an array (all in memory). You want SQL-where query function, but Firebase is just different and doesn't have it.
Problem with storing fetching all data approach:
1) Storing just all the user's information in an array of user objects is NOT scalable on client's device.
2) When the number of users gets to ten of thousands, a day worth of search by a single user will eat up a sizable amount of real time database read quota.
3) Stale user data, an user has to re-download all the users just becauase on user changed his name to Doggie1 to doggieTwo.
Solutions:
1) If you haven't done so already, I suggest the options of doing some server-side filtering first by following the best practice here:
Firebase Retrieving Data - Filtering
Downloading a sub-set of user that fits some criteria and then do a bit of client-side filtering. Still is problematic when users get to tens of thousands.
Firebase has a client-size data persistence feature, but in your case if there filtering rule doesn't fit your need, you need do you own caching with some persistent storage solution. Instead of putting the fetched object in an Array[User], I would store each in a database SQLite on iOS and Android apps.
2) Implement a ElasticSearch with the FlashLight plugin, this involves some extra setup (I know, I've been through it, I learned quite a bit), but it is well worth it for autocomplete and search functions that Firebase currently doesn't support.
A pluggable integration with ElasticSearch to provide advanced content searches in Firebase.

One To Many Relationship setter

this is my first time working with Core Data in swift. I'm really enjoying it but it's also a challenge making sure my Appdelegate saves etc.
The Problem
Basically I am creating an budgeting app. Once a budget ends I need to take the current budget and store it away into a history entity. Now I have 2 different entities that work here:
NewBudgetCreateMO and HistoryBudgetHolderMO. What should happen is that the HistoryBudgetHolder should add a budget (newBudgetCreateMO) into it's One-To-Many relationship. Here is an image of my graph and their relationship.
Now if I've set this up right I should be allow to have as many NewBudgetCreateMOs in my History as I like by adding them? The code below is the generated code for my History entity which shows that it contains an NSSet
extension HistoryBudgetHolderMO {
#nonobjc public class func fetchRequest() -> NSFetchRequest<HistoryBudgetHolderMO> {
return NSFetchRequest<HistoryBudgetHolderMO>(entityName: "HistoryBudgetHolder");
}
#NSManaged public var budgets: NSSet?
}
extension HistoryBudgetHolderMO {
#objc(addBudgetsObject:)
#NSManaged public func addToBudgets(_ value: NewBudgetCreateMO)
#objc(removeBudgetsObject:)
#NSManaged public func removeFromBudgets(_ value: NewBudgetCreateMO)
#objc(addBudgets:)
#NSManaged public func addToBudgets(_ values: NSSet)
#objc(removeBudgets:)
#NSManaged public func removeFromBudgets(_ values: NSSet)
}
So I assumed that I could just use "addToBudgets" to add a set piece of data and it does seem to work but for only one instance.
Where I'm doing the adding
So I do a fetch request on the HistoryBudgetHolderMO to see if I have any in the data base. If not then I create a new one from my App Delegate (Please NOTE: I have done the app delegate casting etc in a method above and then have passed the App Delegate and Context to this method)
private func SaveAndDeleteCurrentBudget(context : NSManagedObjectContext, appDele : AppDelegate){
let fetchHistory : NSFetchRequest<HistoryBudgetHolderMO> = HistoryBudgetHolderMO.fetchRequest()
//Saves the budget to the history budget. If we don't have oen we created one and add it to that
do{
let historyBudgets : [HistoryBudgetHolderMO] = try context.fetch(fetchHistory)
if historyBudgets.count <= 0{
let newHistoryBudget : HistoryBudgetHolderMO = HistoryBudgetHolderMO(context: context)
newHistoryBudget.addToBudgets(budgetData.first!)
print("entered new historyBudget")
}else{
historyBudgets.first!.addToBudgets(budgetData.first!)
}
appDele.saveContext()
}catch{
print("Error when looking for history fetch result")
}
//Deletes all budget data and budget entries that are currently used
for object in budgetData{
context.delete(object)
}
let fetchAllDataEntries = NSFetchRequest<NSFetchRequestResult>(entityName: "BudgetEntry")
let deleteReq = NSBatchDeleteRequest(fetchRequest: fetchAllDataEntries)
do{
try context.execute(deleteReq)
}catch{
print("Error when deleting budget entries")
}
appDele.saveContext()
}
I do the fetch request and check if a history entity is there. If not then I create a new one, add the budget entry and then save the context.
If not then I grab the first instance of the history holder (as there should only ever be one as it's just a container) and I add the budget entry and then save.
Where it gets bad
So the first time I do this and it's in state 2 I get a value of Optional(1) which means it has stored one entry of the History. However any more additions after this keep saying it's Optional(1). I've tried looking up countless solutions, tried messing around with the extensions etc. I figured this would be a simple Get/Set operation but It's just not working.
Any help with this would be greatly appreciated.
Thanks for taking the time to read this.
Your solution seems good now. I also would have suggested to get rid of the HistoryBudgetHolderMO class. May I suggest to add another field/property to the NewBudget class: a creationDate (Date type). That way you can always fetch the latest one (e.g. fetch all of them and sort by creationDate). You could als add an active/historic boolean property to mark Budgets as active/inactive. Another suggestion is try to avoid force unwrapping. Instead of writing
budgetData.first!.attributeName
try to work with the 'if let' construct
if let budget = budgetData.first {
budget.attributeName
}
Solution For Anyone Interested
As I mentioned before I'm still learning Core Data and I'm grateful for KaraBenNensi for his comment to get me thinking.
Right so there was no need for a "holder" type object. Instead what I have done is I have used the last index of my budgets. So everytime I create a new budget I simply keep them all in the array. So instead of saying:
budgetData.first!.attributeName
I now use
budgetData.last!.attributeName.
This means that my database will grow but it would have grown with the history holder anyway. Now when I want to display history I just fetch all the results from the budgetData core data model. When I want to display my actual budget I just use .last so I get the most recently created budget.
I hope this helps someone and I'm glad I could figure it out. If anyone needs help in the future just reply to this and I'll try to help (But I'm no expert!)

Parse - downloading only updated rows of Data

I am running parse server on AWS. Data is being stored on mlab. At app launch I make a query:
let query = PFQuery(className: foodDataClassName_)
query.findObjectsInBackgroundWithBlock({(objects : [PFObject]?,error : NSError?) -> Void in
})
It returns me all the rows of data. I save them locally using CoreData. When any row of data is updated, I delete the previous locally stored data and download all the data again and save it. This is not a good approach. What I want is that I only download the rows which are updated not all the rows. How can I achieve that? Thanks.
what you can do is the following:
The first time the user log in to your app you need to query for all the user rows from the server so in this case you will execute the query without any condition. In this query you can use limit in order to limit the results that will be returned by the server. When you get results from the server you will need to:
Store all rows in your local database
Store the current NSDate inside NSUserDefaults
The next call to your server will be to get only the updated rows. In order to achieve it you will need to:
Get the last sync date from your NSUserDefaults (the one that we save above)
Execute the query but this time with a condition of greaterThan your lastSyncDate
at the end your code to fetch the items should look like the following:
// check if we synced the date before
let lastSyncDate = NSUserDefaults.standardUserDefaults().objectForKey("lastSyncDate")
let query = PFQuery(className: "MyParseObjectClassName")
if (lastSyncDate != nil){
// get only records that were created/updated since the last sync date
query.whereKey("updatedAt", greaterThan: lastSyncDate!)
}
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
// store objects in parse local data store
if (objects?.count > 0){
PFObject.pinAllInBackground(objects)
}
// save the last sync date in user defaults
NSUserDefaults.standardUserDefaults().setValue(NSDate(), forKey: "lastSyncDate")
NSUserDefaults.standardUserDefaults().synchronize()
}
Please notice here i used parse local data store which allows you to store parse objects to your local data base easily without using core data. Parse local data store is provided by parse iOS SDK and save a lot of time and effort for you so i strongly recommend you to leverage it.
local data store also take care for you to objects that were created/updated and it will automatically create new object and will update existing ones.
you can read more about it in here

Cloudkit fetch data (strings and image asset) take a long time to appear after call

I was hoping that someone can help a coding newbie with what might be considered a stupid question. I'm making a blog type app for a community organization and it's pretty basic. It'll have tabs where each tab may be weekly updates, a table view with past updates and a tab with general information.
I setup cloudkit to store strings and pictures, and then created a fetchData method to query cloud kit. In terms of the code (sample below) it works and gets the data/picture. My problem is that it takes almost 5-10 seconds before the text and image update when I run the app. I'm wondering if that's normal, and I should just add an activity overlay for 10 seconds, or is there a way to decrease the time it takes to update.
override func viewDidLoad() {
fetchUpcoming()
}
func fetchUpcoming() {
let container = CKContainer.defaultContainer()
let publicData = container.publicCloudDatabase
let query = CKQuery(recordType: "Upcoming", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
publicData.performQuery(query, inZoneWithID: nil) { results, error in
if error == nil { // There is no error
println(results)
for entry in results {
self.articleTitle.text = entry["Title"] as? String
self.articleBody.text = entry["Description"] as? String
let imageAsset: CKAsset = entry["CoverPhoto"] as! CKAsset
self.articlePicture.image = UIImage(contentsOfFile: imageAsset.fileURL.path!)
self.articleBody.sizeToFit()
self.articleBody.textAlignment = NSTextAlignment.Justified
self.articleTitle.adjustsFontSizeToFitWidth = true
}
}
else {
println(error)
}
}
}
Another question I had is about string content being stored on cloud kit. If I want to add multiple paragraphs to a blood entry (for example), is there a way to put it in one record, or do I have to separate the blog entry content into separate paragraphs? I may be mistaken but it seems like CloudKit records don't recognize line breaks. If you can help answer my questions, I'd be really appreciative.
It looks like you might be issuing a query after creating the data, which isn't necessary. When you save data, as soon as your completion block succeeds (with no errors) then you can be sure the data is stored on the server and you can go ahead and render it to the user.
For example, let's say you're using a CKModifyRecordsOperation to save the data and you assign a block of code to the modifyRecordsCompletionBlock property. As soon as that block runs and no errors are passed in, then you can render your data and images to your user. You have the data (strings, images, etc.) locally because you just sent them to the server, so there's no need to go request them again.
This provides a quicker experience for the user and reduces the amount of network requests and battery you're using on their device.
If you are just issuing normal queries when your app boots up, then that amount of time does seem long but there can be a lot of factors: your local network, the size of the image you're downloading, etc. so it's hard to say without more information.
Regarding the storage of paragraphs of text, you should consider using a CKAsset. Here is a quote from the CKRecord's documentation about string data:
Use strings to store relatively small amounts of text. Although
strings themselves can be any length, you should use an asset to store
large amounts of text.
You'll need to make sure you're properly storing and rendering line break characters between the user input and what you send to CloudKit.

Resources