How to use BFTask with Swift and AWS DynamoDB? - swift

I'm using DynamoDb with swift to make an iPhone application.
I'm trying to query a table I have in my database and then use that queried data to load(query) a different table with the data received.
I'm querying a friends table and then querying a books table. To do this I have a function called queryFriends and queryBooks, which looks like this:
//these are mapper classes for DynamoDB
func queryFriendsTable(hash: Int) -> BFTask! {
let mapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var item = Friends()
item.UserId = 0
item.friendId = 0
let task1 = mapper.save(item)
let exp = AWSDynamoDBQueryExpression()
exp.hashKeyValues = hash
return mapper.query(Friends.self, expression: exp)
}
func loadBooksTable(hash: Int) -> BFTask! {
let mapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var item = Books()
item.UserId = 0
item.name = ""
item.bookId = 0
item.author = ""
let task1 = mapper.save(item)
return mapper.load(Books.self, hashKey: hash, rangeKey: nil)
}
I then call these functions in the viewDidLoad section. I use the queryFriendsTable() with a BFTask to call the first query on the friends table and then put the loadBooksTable() inside that function call with another BFTask call.
Here is the code:
//the function is called and takes in "1" as a parameter to query the table with.
self.queryFriendsTable(1).continueWithSuccessBlock{(task: BFTask!) -> BFTask! in
let results = task.result as! AWSDynamoDBPaginatedOutput
for r in results.items {
//this loads the books table and takes the friendId's gotten as an argument
self.loadBooksTable(r.friendId).continueWithSuccessBlock{ (task: BFTask!) -> BFTask! in
let books = task.result as! Books
//this is just a label on the screen to test what I'm doing
self.testLabel.text = books.name
return nil
}
}
return nil
}
This works but it takes about 15 seconds for the label to be replaced by the name. I know its not taking that long to get the data though because if I were to do just println(books.name) it takes less then a second to print the name.
So I'm wondering why its taking so long. I tried to google it but I couldn't figure it out. I figure it has something to do with the BFTasks inside eachother but I'm not sure.
Any help is appreciated. Thanks!

Related

How to use NSSet created from Core Data

I have the following core data model:
where Person to Codes is a one-to-many relationship.
I have a function which returns a Person record and if the code person.codes returns an NSSet of all the codes associated with that Person. The issue that I am having is how to use the NSSet.
person.codes.allObjects.first returns this data:
<Codes: 0x60000213cb40> (entity: Codes; id: 0xb978dbf34ddb849 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Codes/p1> ; data: {
code = 4LQ;
number = 1;
whosAccount = "0xb978dbf34ddb869 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Person/p1>";
})
I thought if I made person.codes.allObjects.first of type Codes, I would be able to access the code and number elements but I get an error: error: value of type 'Any?' has no member 'number'
Also, how can I search this data set for a particular code or number.
I appreciate that this is proabably a simple question but have searched and read the documentation to no avail. I suspect that may base knowledge is not sufficient.
Update
I have a CoreDataHandler class which contains the following code:
class CoreDataHandler: NSObject {
//static let sharedInstance = CoreDataHandler()
private static func getContext() -> NSManagedObjectContext {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static func fetchPerson() -> [Person]? {
let context = getContext()
do {
let persons: [Person] = try context.fetch(Person.fetchRequest())
return persons
} catch {
return nil
}
}
I can fetch a person using:
let row = personTableView.selectedRow
let person = CoreDataHandler.fetchPerson()?[row]
Core Data supports widely native Swift types.
Declare codes as Set<Codes> in the Person class.
It's much more convenient than typeless NSSet.
You get a strong type and you can apply all native functions like filter, sort, etc. without type cast.
let codes = person.codes as! Set<Code>
Once that is done you can access the properties. Searching can be done by filtering for instance
let filteredCodes = codes.filter({ $0.code == "XYZ" })
will return all objects that has the code "XYZ". Or to get only one you can use
let code = codes.first(where: {$0.id == 1})
which will return the first object that has id = 1
A simple example getting all Person objects that has a given code
func findWithCode(_ code: String) -> [Person] {
guard let persons = CoreDataHandler.fetchPerson() else {
return []
}
var result = [Person]()
for person in persons {
let codes = person.codes as! Set<Code>
if codes.contains(where: { $0.code == code }) {
result.append(person)
}
}
return persons
}

databaseReference.observe(DataEventType.value, with:{(DataSnapshot) not working properly all the time

func checkPaid(utilityId : String) -> Int{
var amount:String = ""
var status = 0
print("inside new function ")
print ("\(utilityId) inside new function ")
self.databaseRefPayment.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for payments in DataSnapshot.children.allObjects as! [DataSnapshot]{
var paymentsObject = payments.value as? NSDictionary
/*
if(paymentsObject!["month"] as! String == monthCheck && paymentsObject!["year"] as! String == monthCheck && paymentsObject!["utilityid"] as! String == utilityId as! String){ */
if(paymentsObject!["utilityId"] as! String == utilityId){
amount = paymentsObject!["amount"] as! String
print(amount)
print("Ypur program is working perfect")
status = 1
}
}
}
})
return status
}
The above function is filtering the data present in payments node based on the value for utilityId getting passed in the function . But the strange thing is observe(DataEventType.value, with:{(DataSnapshot) this event is not getting triggered all the time . Its just skipping that portion unnecessarily . I am very new to firebase and getting really mad with these kind of unpredicted behaviours . Please help me in this . feel free to ask for any clarifications .
The firebase executes firebase query functions in different thread , so after u call check paid(), it runs the checkpaid() firebase query in another thread,and it will return from the function , eventhough ur query is running in the background..so it will seem like,checkpaid() is not working , but actually it's running on another thread.
I think you first fetch all the required data from payment, and store it in a list , and then use that list to compare with utility.
Every time this function is called it adds/resets the Key-Value Observer for whichever child node you are observing it doesn't actually check the value unless it is changed. I believe it is your intention to call checkPaid(utilityId:) to check the child is 'paid' by some means. There is no need to add a KVO if you are directly reading the value for a single snapshot. consider the following:
func checkPaid(utilityId: String) -> Bool {
//Assume it is not paid if we cannot verify it.
var isPaid = false
//Create a new reference to Firebase Database
var ref: DatabaseReference!
ref = Database.database().reference().child(utilityId)
//Get the values for the child, test if it is paid or not.
ref.queryOrderedByValue().observeSingleEvent(of: .value) { (snapshot) in
if (snapshot.value is NSNull) {
print("No Child With \(utilityId) Exists")
} else {
//child with utilityId exists, in case multiple utilityId's exist with the same value..
for child in snapshot.children.allObjects as! [DataSnapshot] {
if let values = child.value as? [String : AnyObject] {
let uid = child.key //utilityId
var month:String = ""
var year:String = ""
var amount:String = ""
//var amount:Double = 0.0
//get values from parent
if let m = values["month"] as? String {
month = m
}
if let y = values["year"] as? String {
year = y
}
if let a = values["amount"] as? String {
amount = a
}
/*
if let a = values["amount"] as? Double {
amount = a
}
*/
//??
if ((month == monthCheck) && (year == monthCheck)) {
isPaid = true
}
}
}
}
return isPaid
}
I am making one assumption here; that utilityId is the key for the child.
if you have parent nodes to utilityId you'll have to transverse those as well when you reference the database:
ref = Database.database().reference().child(utilities).child(utilityId) ..etc
If you need a KVO to update a local property I suggest adding/calling it in viewDidLoad, it's completion handler should take care of updating whichever properties are updated when they change in Firebase.

Objects in Swift: Value of 'Object' has no member

Here's my doozy.
I've got this lovely little function in a file called functions.swift
//functions.swift
func latestActiveGoal() -> Object {
let realm = try! Realm()
let currentGoal = realm.objects(Goal).filter("Active == 1").sorted("CreatedOn").last
return currentGoal!
}
which returns a Goal object. (A Goal might be wanting to lose weight, or stop being so inept at Swift).
In a different view controller, I want to access this object. Here's what I'm trying:
//viewController.swift
#IBOutlet weak var aimText: UILabel!
let funky = functions()
func getGoals(){
var currentGoal = funky.latestActiveGoal()
print(currentGoal)
aimText.text = currentGoal.Title
}
The print(CurrentGoal) output shows this:
Goal {
id = 276;
Title = Goal Title;
Aim = Aim;
Action = Nothing;
Active = 1;
CreatedOn = 2016-02-12 00:14:45 +0000;
}
aimText.text = currentGoal.Title and aimText = currentGoal.Title both throw the error:
Value of 'Object' has no member 'Title'
By printing the contents of the object, I can see the data, but can't figure out how. Any help greatly appreciated.
As the error message said, currentGoal is a value of Object type which doesn't have member Title.
This is because function latestActiveGoal returns Object instead of Goal. You just need to make it return Goal by change the return type:
func latestActiveGoal() -> Goal {
Just replace your functions with below code.
It will works perfect.
This fuction will check if goal available, then only it will return.
func latestActiveGoal() -> Object? {
let realm = try! Realm()
let currentGoals = realm.objects(Goal).filter("Active == 1").sorted("CreatedOn")
if currentGoals.count > 0 {
return currentGoals.last;
}
return nil;
}
Your getGoals method will be as follow.
func getGoals(){
if let currentGoalObject = funky.latestActiveGoal() {
print(currentGoalObject)
let goal = currentGoalObject as! Goal
print(goal.Title)
aimText.text = goal.Title
}
}

"unexpectedly found nil while unwrapping an Optional value" error when returning a row in SQLite DB

I have integrated the FMDB Sqlite Wrapper into my iOS application and I have written a couple of DB functions, that have all worked perfectly until now.
I used the same approach for a simple method that is supposed to return a single element in a Resultset item, but I get the above error. There is nothing different in the logic from the other methods so I have no idea why the error occurs and why it happens on both lines with stringForColumn and not in the line with intForColumn (as the debugger tells me).
Any help or tips would be greatly appreciated!
func fetchExercise(exerciseId: Int) -> Exercise {
sharedInstance.database!.open()
let resultSet: FMResultSet! = sharedInstance.database!.executeQuery("Select * from Exercises where ExerciseId = ?", withArgumentsInArray: [String(exerciseId)])
let fetchedExercise: Exercise = Exercise()
if (resultSet != nil) {
fetchedExercise.exerciseId = Int(resultSet.intForColumn("ExerciseId"))
fetchedExercise.exerciseCategory = resultSet.stringForColumn("ExerciseCategory")
fetchedExercise.exerciseTitle = resultSet.stringForColumn("ExerciseTitle")
}
sharedInstance.database!.close()
return fetchedExercise
}
The resultSet is as follows:
You have to call resultsSet.next() to retrieve the results. For example:
func fetchExercise(exerciseId: Int) -> Exercise {
sharedInstance.database!.open()
defer { sharedInstance.database!.close() }
let resultSet = try! sharedInstance.database!.executeQuery("Select * from Exercises where ExerciseId = ?", values: [exerciseId])
let fetchedExercise = Exercise()
if resultSet.next() {
fetchedExercise.exerciseId = resultSet.longForColumn("ExerciseId")
fetchedExercise.exerciseCategory = resultSet.stringForColumn("ExerciseCategory")
fetchedExercise.exerciseTitle = resultSet.stringForColumn("ExerciseTitle")
}
return fetchedExercise
}
BTW, I'm not sure why you're converting exerciseId to a string in the query. I'd personally just pass the Int value, as shown above.

Modifying struct instance using call to name in function parameter

I am attempting to use Parse to call up some variables and put them into a struct that is already initialized. The calling of the variables is happening smoothly and the data is available, but the inputing of the class into the function is not happening.
'unit' is a struct that has the name, hp, attack, etc. variables contained within it.
Is it not possible to pass along an instance of a struct and modify it's values like this? It would save me a lot of copy-pasting code to do it this way.
Thanks for your help!
func fetchStats(name: String, inout nameOfClass: unit) {
var unitStatArray = []
let query = PFQuery(className: "UnitStats")
query.whereKey("name", equalTo: name)
query.findObjectsInBackgroundWithBlock{(objects:[PFObject]?, error: NSError?)->Void in
if (error == nil && objects != nil){ unitStatArray = objects! }
nameOfClass.name = "\(unitStatArray[0].objectForKey("name")!)"
print("class name is \(nameOfClass.name)")
print("cannon name is \(cannon.name)")
nameOfClass.hitPoints = unitStatArray[0].objectForKey("hitPoints") as! Double
nameOfClass.hitPointsMax = unitStatArray[0].objectForKey("hitPointsMax") as! Double
nameOfClass.attack = unitStatArray[0].objectForKey("attack") as! Double
nameOfClass.defense = unitStatArray[0].objectForKey("defense") as! Double
nameOfClass.rangedAttack = unitStatArray[0].objectForKey("rangedAttack") as! Double
nameOfClass.rangedDefense = unitStatArray[0].objectForKey("rangedDefense") as! Double
nameOfClass.cost = unitStatArray[0].objectForKey("cost") as! Int
}
}
fetchStats("3-inch Ordnance Rifle", nameOfClass: &cannon)
This is an attempt to explain what I had in mind when writing my comment above.
Because there's an asynchronous call to findObjectsInBackgroundWithBlock, the inout won't help you here. The idea is to add a callback fetched like this:
func fetchStats(name: String, var nameOfClass: unit, fetched: unit -> ()) {
// your code as above
query.findObjectsInBackgroundWithBlock {
// your code as above plus the following statement:
fetched(nameOfClass)
}
}
This can be called with
fetchStats("3-inch Ordnance Rifle", nameOfClass: cannon) { newNameOfClass in
nameOfClass = newNameOfClass
}
(all of this code has not been tested)
The point is that you understand that your code is asynchronous (I know, I'm repeating myself). After you have called fetchStats you don't know when the callback (here: the assignment nameOfClass = newNameOfClass) will be executed. You cannot assume the assignment has been done after fetchStats has returned.
So whatever you need to do with the changed nameOfClass: the corresponding statements must go into the callback:
fetchStats("3-inch Ordnance Rifle", nameOfClass: cannon) { newNameOfClass in
// do whatever you want with the received newNameOfClass
}
Hope this helps.