Using parse API in swift 3, only some query results are saving? - ios

I'm new to Swift and programming. I'm using Xcode 8.2.1 and the Parse AWS (free) server.
I'm using the following Syntax in order to save all query results for specific object IDs in to one row of a new parse class.
Most of the query results are saving (e.g. primarycityquery, secondcityquery, citydetailsquery) but not all (e.g. multicityquery).
I'm using the exact same syntax in another UIViewController (with just 3 queries, though) and it's working fine.
Is the issue here that I'm running too many queries? This is all within viewDidLoad. Please advise if there is a better way to do this, as well.
Thank you!
let primaryCityQuery = PFQuery(className: "primaryCityDetails")
primaryCityQuery.whereKey("objectId", equalTo: passedPrimaryCityDetailsObjectIdString)
primaryCityQuery.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
for primaryCityQuery in objects {
if let primaryCity = primaryCityQuery["primaryCity"] as? String {
self.primaryCity = String(primaryCity)
print(self.primaryCity) }
if let primaryCountry = primaryCityQuery["primaryCountry"] as? String {
self.primaryCountry = String(primaryCountry)
print(self.primaryCountry) }
if let primaryState = primaryCityQuery["primaryState"] as? String {
self.primaryState = String(primaryState)
print(self.primaryState) }
if let numberDays = primaryCityQuery["numberDays"] as? String {
self.numberDays = String(numberDays)
print(self.numberDays) }
if let numberNights = primaryCityQuery["numberNights"] as? String {
self.numberNights = String(numberNights)
print(self.numberNights) }
} } else {
print(error!)
} })
//second city info
let secondCityQuery = PFQuery(className: "secondCityDetails")
secondCityQuery.whereKey("objectId", equalTo: passedSecondCityDetailsObjectIDString)
secondCityQuery.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
for secondCityQuery in objects {
if let secondCity = secondCityQuery["secondCity"] as? String {
self.secondCity = String(secondCity)
print(self.secondCity) }
if let secondCountry = secondCityQuery["secondCountry"] as? String {
self.secondCountry = String(secondCountry)
print(self.secondCountry) }
if let secondState = secondCityQuery["secondState"] as? String {
self.secondState = String(secondState)
print(self.secondState) }
} } else {
print(error!)
} })
//
let multiCityQuery = PFQuery(className: "multiCityDetails")
multiCityQuery.whereKey("objectId", equalTo: passedCityBeforeDetailsObjectIdString)
multiCityQuery.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
for multiCityQuery in objects {
if let beforeCity = multiCityQuery["beforeCity"] as? String {
self.beforeCity = String(beforeCity)
print(self.beforeCity) }
if let beforeCountry = multiCityQuery["beforeCountry"] as? String {
self.beforeCountry = String(beforeCountry)
print(self.beforeCountry) }
if let beforeState = multiCityQuery["beforeState"] as? String {
self.beforeState = String(beforeState)
print(self.beforeState) }
} } else {
print(error!)
} })
let inputtedCityDetailsQuery = PFQuery(className: "primaryTripDetails")
inputtedCityDetailsQuery.whereKey("objectId", equalTo: primTripDetails)
inputtedCityDetailsQuery.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
for inputtedCityDetailsQuery in objects {
if let primaryTripWhen = inputtedCityDetailsQuery["primaryTripWhen"] as? String {
self.primaryTripWhen = String(primaryTripWhen)
print(self.primaryTripWhen) }
if let primaryTripWhatSort = inputtedCityDetailsQuery["primaryTripWhatSort"] as? String {
self.primaryTripWhatSort = String(primaryTripWhatSort)
print(self.primaryTripWhatSort) }
if let primaryTripWhoWith = inputtedCityDetailsQuery["primaryTripWhoWith"] as? String {
self.primaryTripWhoWith = String(primaryTripWhoWith)
print(self.primaryTripWhoWith) }
if let primaryTripBudget = inputtedCityDetailsQuery["primaryTripBudget"] as? String {
self.primaryTripBudget = String(primaryTripBudget)
print(self.primaryTripBudget)
let fullCityInput = PFObject(className: "fullCityInputRow")
fullCityInput["primaryCity"] = self.primaryCity
fullCityInput["primaryCountry"] = self.primaryCountry
fullCityInput["primaryState"] = self.primaryState
fullCityInput["secondCity"] = self.secondCity
fullCityInput["secondCountry"] = self.secondCountry
fullCityInput["secondState"] = self.secondState
fullCityInput["beforeCity"] = self.beforeCity
fullCityInput["beforeCountry"] = self.beforeCountry
fullCityInput["beforeState"] = self.beforeState
fullCityInput["primaryTripWhoWith"] = self.primaryTripWhoWith
fullCityInput["primaryTripBudget"] = self.primaryTripBudget
fullCityInput["primaryTripWhen"] = self.primaryTripWhen
fullCityInput["primaryTripWhatSort"] = self.primaryTripWhatSort
fullCityInput["numberDays"] = self.numberDays
fullCityInput["numberNights"] = self.numberNights
fullCityInput.saveInBackground(block: { (success, error) in
if success {
print("full city input row saved")
} else {
if error != nil {
print (error!)
} else {
print("blanks")
}
}
})
}
}
} else {
print(error!)
} })

Thank you. It does seem the magic number is 3. I've saved the values that were not saving in this view controller to another and then query them in one of my 3 queries here. All of my inputs are now successfully saved in one row of another class.

Related

translate string with YandexAPI in for loop

I want to translate googleMap Api result for location address to Persian. i'm using YandexApi for translate address. separate address components with "،" and pass this array to for api. my problem is after every service call my userInterface updated. how can solve this problem.
for example when I pass this array to method:["road","avenue"]
address label text update after every translate response
first text for label = "جاده"
last text for label = "جاده ، خیابان"
there is my code:
func translateText(text:[String],closure:#escaping ((_ success:String?,_ error:Error?) -> Void)) {
var translateString: String = ""
var responseError: Error?
for item in text {
myGroup.enter()
let urlString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20171105T134956Z.795c7a0141d3061b.dc25bae76fa5740b2cdecb02396644dea58edd24&text=\(item)&lang=fa&format=plain&options=1"
if let allowString = Utilities.shareInstance.getQueryAllowedString(url: urlString) {
if let url = URL(string:allowString){
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let res = json as? [String:Any] {
if let code = res["code"] as? Int {
if code == 200 {
if let textArr = res["text"] as? [AnyObject] {
let flattArr = Utilities.shareInstance.flatStringMapArray(textArr)
if flattArr.count > 0 {
translateString += " ، " + flattArr[0]
}
}
}
}
}
}catch {
responseError = error
}
self.myGroup.leave()
closure(translateString, responseError)
}
}
}
}
}
private func translatingAddressArrayForChecking(address:[String]) {
var addressString = ""
let addressComponent = address
if addressComponent.count > 0 {
YandexTranslateAPI.shateInstance.translateText(text: addressComponent, closure:{ success,_ in
if let res = success {
addressString = res
OperationQueue.main.addOperation {
self.layoutActivity(start: false)
if self.searchState == .Origin && self.destinationButton.isHidden == false{
if let selectOrigin = self.originSelectedCity {
if let city = selectOrigin.city_title {
if addressString.contains(city) {
self.originAddressLabel.text = addressString
if let location = self.startCoordinate {
self.setOrigin(location: location)
}
}else {
self.CreateCustomTopField(text: "wrong city", color: Constants.ERROR_COLOR)
}
}
}
}else if self.searchState == .Destination || self.destinationButton.isHidden == true {
if let selectOrigin = self.destinationSelectedCity {
if let city = selectOrigin.city_title {
if addressString.contains(city) {
self.destinationAddressLabel.text = addressString
if let location = self.endCoordinate {
self.setDestination(location: location)
}
}else {
self.CreateCustomTopField(text: "wrong city", color: Constants.ERROR_COLOR)
}
}
}
}
}
}
})
}
addresses label text update multiple with array component.

Firebase Realtime Array count mismatch

I have an iOS swift app using Firebase realtime database. If I use the app normally so far I cannot find any issue. However, I want to anticipate edge cases.
I am trying to stress test my app before I push the update, and one way I am doing it is quickly going back and forth from a VC with a tableView to the next VC which is a detail VC. If I do it several times eventually the tableview will show lots of duplicate data.
I have tested my app by having a tableview open on my simulator and going into my Firebase Console and manually changing a value and instantly on the device the string changes.
So I am confused as to why my tableview would show an incorrect amount of children if it is constantly checking what the value should be.
// MARK: Firebase Methods
func checkIfDataExits() {
DispatchQueue.main.async {
self.cardArray.removeAll()
self.ref.observe(DataEventType.value, with: { (snapshot) in
if snapshot.hasChild("cards") {
self.pullAllUsersCards()
} else {
self.tableView.reloadData()
}
})
}
}
func pullAllUsersCards() {
cardArray.removeAll()
let userRef = ref.child("users").child((user?.uid)!).child("cards")
userRef.observe(DataEventType.value, with: { (snapshot) in
for userscard in snapshot.children {
let cardID = (userscard as AnyObject).key as String
let cardRef = self.ref.child("cards").child(cardID)
cardRef.observe(DataEventType.value, with: { (cardSnapShot) in
let cardSnap = cardSnapShot as DataSnapshot
let cardDict = cardSnap.value as! [String: AnyObject]
let cardNickname = cardDict["nickname"]
let cardType = cardDict["type"]
let cardStatus = cardDict["cardStatus"]
self.cardNicknameToTransfer = cardNickname as! String
self.cardtypeToTransfer = cardType as! String
let aCard = CardClass()
aCard.cardID = cardID
aCard.nickname = cardNickname as! String
aCard.type = cardType as! String
aCard.cStatus = cardStatus as! Bool
self.cardArray.append(aCard)
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
})
}
I got help and changed my code drastically, so now it works
func checkIfDataExits() {
self.ref.observe(DataEventType.value, with: { (snapshot) in
if snapshot.hasChild("services") {
self.pullCardData()
} else {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
})
}
func pullCardData() {
let cardRef = self.ref.child("cards")
cardRef.observe(DataEventType.value, with: { (snapshot) in
for cards in snapshot.children {
let allCardIDs = (cards as AnyObject).key as String
if allCardIDs == self.cardID {
if let childId = self.cardID {
let thisCardLocation = cardRef.child(childId)
thisCardLocation.observe(DataEventType.value, with: { (snapshot) in
let thisCardDetails = snapshot as DataSnapshot
if let cardDict = thisCardDetails.value as? [String: AnyObject] {
self.selectedCard?.cardID = thisCardDetails.key
self.selectedCard?.nickname = cardDict["nickname"] as? String ?? ""
self.selectedCard?.type = cardDict["type"] as? String ?? ""
self.pullServicesForCard()
}
})
}
}
}
})
}
func pullServicesForCard() {
if let theId = self.cardID {
let thisCardServices = self.ref.child("cards").child(theId).child("services")
thisCardServices.observe(DataEventType.value, with: { (serviceSnap) in
if self.serviceArray.count != Int(serviceSnap.childrenCount) {
self.serviceArray.removeAll()
self.fetchAndAddAllServices(serviceSnap: serviceSnap, index: 0, completion: { (success) in
if success {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
})
}
})
}
}
func fetchAndAddAllServices(serviceSnap: DataSnapshot, index: Int, completion: #escaping (_ success: Bool) -> Void) {
if serviceSnap.hasChildren() {
if index < serviceSnap.children.allObjects.count {
let serviceChild = serviceSnap.children.allObjects[index]
let serviceID = (serviceChild as AnyObject).key as String
let thisServiceLocationInServiceNode = self.ref.child("services").child(serviceID)
thisServiceLocationInServiceNode.observeSingleEvent(of: DataEventType.value, with: { (thisSnap) in
let serv = thisSnap as DataSnapshot
if let serviceDict = serv.value as? [String: AnyObject] {
let aService = ServiceClass(serviceDict: serviceDict)
self.serviceCurrent = serviceDict["serviceStatus"] as? Bool
self.serviceName = serviceDict["serviceName"] as? String ?? ""
self.serviceURL = serviceDict["serviceURL"] as? String ?? ""
self.serviceFixedBool = serviceDict["serviceFixed"] as? Bool
self.serviceFixedAmount = serviceDict["serviceAmount"] as? String ?? ""
self.attentionInt = serviceDict["attentionInt"] as? Int
self.totalArr.append((serviceDict["serviceAmount"] as? String)!)
// self.doubleArray = self.totalArr.flatMap{ Double($0) }
// let arraySum = self.doubleArray.reduce(0, +)
// self.title = self.selectedCard?.nickname ?? ""
// if let titleName = self.selectedCard?.nickname {
// self.title = "\(titleName): \(arraySum)"
// }
aService.serviceID = serviceID
if serviceDict["serviceStatus"] as? Bool == true {
self.selectedCard?.cStatus = true
} else {
self.selectedCard?.cStatus = false
}
if !self.serviceArray.contains(where: { (service) -> Bool in
return service.serviceID == aService.serviceID
}) {
self.serviceArray.append(aService)
self.serviceArray.sort {$1.serviceAttention < $0.serviceAttention}
}
}
self.fetchAndAddAllServices(serviceSnap: serviceSnap, index: index + 1, completion: completion)
})
}
else {
completion(true)
}
}
else {
completion(false)
}
}

Add multiple query results to array

I am trying to run 4 queries from my parse database, each pulling just 1 object. I am trying to add each of the objects to an array that can then be used in a collection view. The queries run successfully but the app crashes because it is pulling null values. Here is my code:
var query1desc = ""
var query2desc = ""
var descs = [String]()
fileprivate func fetchUsers() {
let query1 = PFQuery(className: "Messages")
query1.limit = 1
query1.findObjectsInBackground{
(objects, error) -> Void in
if error == nil {
if objects?.count == 0 {
} else {
let object = objects![0] as! PFObject
self.query1desc = object["message"] as! String
}
}
}
let query2 = PFQuery(className: "Messages")
query2.limit = 1
query2.findObjectsInBackground{
(objects, error) -> Void in
if error == nil {
if objects?.count == 0 {
} else {
let object = objects![0] as! PFObject
self.query2desc = object["message"] as! String
}
}
}
self.descs = [self.query1desc, self.query2desc]
self.collectionView.reloadData()
Does anyone know of a way to fix this so that self.descs does not just provide nil values? When I print within the query itself I know that objects are being pulled. Thanks in advance.
I think desc variable holds nil value because your program is executing an array assignment code part while completion handlers are still processing query requests. You need to wait until all your query requests finish and then can collect all the results.
let workGroup = DispatchGroup()
let queueForQuery1 = DispatchQueue(label: "firstQuery")
let query1 = PFQuery(className: "Messages")
query1.limit = 1
workGroup.enter()
queueForQuery1.async(group:workGroup) {
query1.findObjectsInBackground{ (objects, error) -> Void in
if error == nil {
if objects?.count == 0 {
} else {
let object = objects![0] as! PFObject
self.query1desc = object["message"] as! String
}
}
workGroup.leave()
}
}
let queueForQuery2 = DispatchQueue(label: "secondQuery")
let query2 = PFQuery(className: "Messages")
query2.limit = 1
workGroup.enter()
queueForQuery2.async(group:workGroup) {
query2.findObjectsInBackground{ (objects, error) -> Void in
if error == nil {
if objects?.count == 0 {
} else {
let object = objects![0] as! PFObject
self.query1desc = object["message"] as! String
}
}
workGroup.leave()
}
}
workGroup.notify(queue: DispatchQueue.main){
self.descs = [self.query1desc, self.query2desc]
self.collectionView.reloadData()
}

Swift 2. using Parse I can't save results of a PFUser.query() in an username array

Well I'm trying to save the results of a query and when I try to save it in an array it just doesn't do it.
Here's my code:
let query = PFUser.query()
query?.orderByDescending("puntaje")
query?.limit = 50
query?.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
if let objects = users {
for object in objects {
self.usernames.removeAll(keepCapacity: true)
self.scores.removeAll(keepCapacity: true)
if let user = object as? PFUser {
print(user.username)
self.usernames.append(user.username!)
self.scores.append((user["puntaje"] as? Int)!)
}
}
}
print(self.usernames.count)
})
while printing user.username appears all the usernames.
and in the print it shows that I have 0 usernames.
You need to move
self.usernames.removeAll(keepCapacity: true)
self.scores.removeAll(keepCapacity: true)
above the "for" loop. Right under
if let objects = users {
I found another way.
I used:
let query = PFUser.query()
query?.orderByDescending("puntaje")
query?.limit = 50
do {
if let users = try query?.findObjects() {
for user in users as! [PFUser] {
let name = user.username!
self.usernames.append(name)
self.scores.append((user["puntaje"] as? Int)!)
}
}
}catch {
print(error)
}

Converting PFObject (Parse) into JSON in Swift?

Is there a way to convert a PFObject from Parse into JSON? I saved as JSON, but when I try to load I'm getting [AnyObject] back. Casting to JSON won't work:
class func loadPeople() -> [String : Person] {
var peopleDictionary: [String : Person] = [:]
let query = PFQuery(className: "userPeeps")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
//this only returns the first entry, how do I get them all?
if let peopleFromParse = objects?.first?.objectForKey("userPeeps") as? JSON {
for name in peopleFromParse.keys {
if let personJSON = peopleFromParse[name] as? JSON,
let person = Person(json: personJSON) {
peopleDictionary[name] = person
}
}
}
below is my save function, which works and saves the JSON into Parse like I want:
class DataManager {
typealias JSON = [String: AnyObject]
class func savePeople(people: [String : Person]) {
var peopleDictionary = people
var peopleJSON: JSON = [:]
for name in peopleDictionary.keys {
peopleJSON[name] = peopleDictionary[name]!.toJSON()
}
let userPeeps = PFObject(className: "userPeeps")
userPeeps.setObject(peopleJSON, forKey: "userPeeps")
userPeeps.saveInBackgroundWithBlock { (succeeded, error) -> Void in
if succeeded {
println("Object Uploaded")
} else {
println("Error: \(error) \(error!.userInfo!)")
}
}
}
So the answer (as Paulw11 points out above) is that "objects" is sort of a wrapper for the real data, so it was necessary to iterate through the array and store each value as JSON:
var peopleDictionary: [String : Person] = [:]
//1 load the dictionary of JSON for key people from Parse
let query = PFQuery(className: "userPeeps")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
if let unwrappedObjects = objects {
for object in unwrappedObjects {
if let peopleFromParse = object as? JSON {
for name in peopleFromParse.keys {
if let personJSON = peopleFromParse[name] as? JSON,
let person = Person(json: personJSON) {
peopleDictionary[name] = person
}
}
}
}
}

Resources