I am getting the opposite value of a boolean when using .boolValue
I have this code.
let _ = channel.bind(eventName: "like-unlike-cake", callback:
{(data:Any?)->Void in
let likedCake = JSON(data!)["like"]
print("liked cake: \(likedCake)")
print("Boolean: \(likedCake["liked_by_current_user"].boolValue)")
liked cake: {
"liked_by_current_user" : true
}
}
Boolean: false
But when I use .bool it gives me nil. Does someone help me on this.
UPDATE This would be the json I want to fetch
{
"like": {
"id": "66642122-7737-4eac-94d2-09c9a35cbef8",
"liked_by_current_user": true
}
}
Try the following:
let _ = channel.bind(eventName: "like-unlike-cake", callback {
(data:Any?)->Void in
if let jsonData = data {
let json = JSON(jsonData)
let isLikedByCurrentUser = json["like"]["liked_by_current_user"].boolValue
print(isLikedByCurrentUser)
} else {
// your data is nil
debugPrint("data error")
}
}
also you can try to cast your data: Any into Data by replacing the line:
if let jsonData = data as? Data {
let json = JSON(data: jsonData)...
cause sometimes SwiftyJSON have problems with creating JSON objects from Any.
Related
I'm having a problem figuring out how to handle dynamic keys with a single value one level deep. I've seen a few examples but they appear to handle other cases.
Here's an example.
[
{ "asdofjiodi": "asdofidj.com" },
{ "sadjlkj": "iejjol.com" },
{ "ijijwjljlijl": "adsijf.com" },
{ "jgncmkz": "mlkjaoijf.com" }
]
Any ideas on how I accomplish this with Codable or CodingKey? This is what I'd like for the end result.
["asdofidj.com", "iejjol.com", "adsijf.com", "mlkjaoijf.com"]
let url = Bundle.main.url(forResource: "file", withExtension: "json")!
let data = try! Data(contentsOf: url)
let decoder = JSONDecoder()
// NOTE: The below line doesn't work because I'm not sure how to do the encoding/decoding
try? decoder.decode([[String: String]].self, from: data)
First of all, is not a valid JSON. A "Valid JSON" can be a JSON Array (multiple json objects) or a single JSON object (starts and ends with { and })
After cleaning this...
You are trying to make a dictionary (json object) from a data. You don't need JSONDecoder to accomplish this. Try using JSONSerialization with jsonObject static function...
let data = Data("{\"asdofjiodi\": \"asdofidj.com\",\"sadjlkj\": \"iejjol.com\",\"ijijwjljlijl\": \"adsijf.com\",\"jgncmkz\": \"mlkjaoijf.com\"}".utf8)
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
let values = json?.map({ $1 })
print(values)
I Hope I helped!
JSONSerialization Apple documentation
Here is a minimal working example, given the JSON in your question:
let json = """
[
{ "asdofjiodi": "asdofidj.com" },
{ "sadjlkj": "iejjol.com" },
{ "ijijwjljlijl": "adsijf.com" },
{ "jgncmkz": "mlkjaoijf.com" }
]
"""
let data = Data(json.utf8)
let decoder = JSONDecoder()
do {
let decodedJson = try decoder.decode([[String: String]].self, from: data)
let values = decodedJson.compactMap(\.values.first)
print(values)
} catch {
print("Error: \(error.localizedDescription)")
}
If this doesn't appear to work for you, it may be related to how you load in the JSON.
There is not enough documents about firebase functions.httpsCallable("findItems").call() usage. I have deployed functions on firebase:
exports.findItems = functions.https.onCall((data, context) => {
// Grab the long parameter.
functions.logger.log(data.long, typeof data.long);
functions.logger.log(data.lat, typeof data.lat);
const long = parseFloat(data.long);
const lat = parseFloat(data.lat);
functions.logger.log(long, typeof long);
functions.logger.log(lat, typeof lat);
//...
}
I make a request in swift using functions.httpsCallable("findItems").call() but the log always says there is no correct parameters:
functions.httpsCallable("findItems").call(["lat": userLocation?.latitude, "long": userLocation?.longitude]) { (result, error) in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
let code = FunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
}
// ...
print(error)
return
}
print(result?.data)
do{
let json = try JSONDecoder().decode([ItemLocation].self, from:result?.data as! Data)
print(json)
}catch{
}
// if let text = (result?.data as? [String: Any])?
//(result?.data as? [String: Any])?["text"] as? String {
//self.resultField.text = text
}
You can find the offcial documents from : https://firebase.google.com/docs/functions/callable
-----Update---------
I have tried everything by debugging and the passed parameters are always undefined but I follow every step on the official doc.
I have made changes and use onCall but it still doesn't work?
You are mixing up HTTP functions with callable functions. Your client code is trying to call a callable function, but your function itself is defined as an HTTP function. This won't work at all. You should review the documentation for callable function and use functions.https.onCall instead of functions.https.onRequest to define your function. They are completely different APIs.
I have trouble with fetching multiples places from Google Places API. The problem is... if i fetch only one pin type like Bars, its ok, no problem. But if i trying to get Restaurants, Bars, Casino... multiples types, it gives my only first place, in our case Restaurants.
I tried make same request with Postman with link below... but for example
restaurants 1030 lines of JSON
political 83 lines
trying to get restaurants + political = 965 lines of JSON.
I use this code to get places i want:
func fetchPlacesNearCoordinate(_ coordinate: CLLocationCoordinate2D, radius: Double, types:[String], completion: #escaping PlacesCompletion) -> Void {
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(coordinate.latitude),\(coordinate.longitude)&radius=\(50000)&rankby=prominence&sensor=true&key=\(googleApiKey)"
let typesString = types.count > 0 ? types.joined(separator: "|") : "food"
urlString += "&types=\(typesString)"
urlString = urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? urlString
guard let url = URL(string: urlString) else { completion([]); return }
if let task = placesTask, task.taskIdentifier > 0 && task.state == .running {
task.cancel()
}
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
placesTask = session.dataTask(with: url) { data, response, error in
if data != nil{
for el in data!{
//print(el)
}
}
var placesArray: [PlaceContent] = []
defer {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
completion(placesArray)
}
}
guard let data = data else { return }
do{
let decode = try JSONDecoder().decode(GooglePlacesAnswer.self, from: data)
placesArray = (decode.results?.map{ $0.toPlaceContent() }) ?? []
} catch let value{
print(value.localizedDescription)
}
}
placesTask?.resume()
}
You can't get places for multiple types as stated in official documentation. You have to make multiple requests and combine the results.
https://developers.google.com/maps/documentation/javascript/places
type — Restricts the results to places matching the specified type.
Only one type may be specified (if more than one type is provided, all
types following the first entry are ignored). See the list of
supported types.
When I run the Firestore transaction it gives me the error :
"Every document read in a transaction must also be written in that transaction."
But all the documents that I am trying to read in the transaction are also being written to in the code below ?
db.runTransaction({ (transaction, errorPointer) -> Any? in
let doc1: DocumentSnapshot
let doc2: DocumentSnapshot
let doc3: DocumentSnapshot
do {
try doc1 = transaction.getDocument(self.doc1Ref)
try doc2 = transaction.getDocument(self.doc2Ref)
try doc3 = transaction.getDocument(self.doc3Ref)
} catch let fetchError as NSError {
errorPointer?.pointee = fetchError
return nil
}
// WRITES TO DOC1, DOC2, DOC3
guard let userPostCount = doc1.data()?["postCount"] as? Int,
let locationPostCount = doc2.data()?["postCount"] as? Int,
let itemPostCount = doc3.data()?["postCount"] as? Int,
let locationAvgRating = doc2.data()?["averageRating"] as? Float,
let itemAvgRating = doc3.data()?["averageRating"] as? Float else { return nil }
// Create post on another document not in transaction
transaction.setData(post.dictionary, forDocument: self.doc4Ref)
// Update counts for #userPosts, #locationPosts, #itemPosts, avgLocationRating, avgItemRating
transaction.updateData(["postCount": userPostCount + 1], forDocument: self.doc1Ref)
let newAvgLocationRating = ((avgLocationRating * Float(locationPostCount)) + Float(post.rating)) / (Float(locationPostCount) + 1.0)
transaction.updateData(["postCount": locationPostCount + 1, "averageRating": newAvgLocationRating], forDocument: self.doc2Ref)
let newAvgItemRating = ((avgItemRating * Float(itemPostCount)) + Float(post.rating)) / (Float(itemPostCount) + 1.0)
transaction.updateData(["postCount": locationPostCount + 1, "averageRating": newAvgItemRating], forDocument: self.doc3Ref)
// Add postID to user and location
transaction.setData(["postID": self.postRef.documentID, "locationID": post.locationID, "itemID": post.itemID, "rating": post.rating, "timestamp": post.timestamp], forDocument: self.doc1Ref.collection("posts").document(self.postRef.documentID))
transaction.setData(["postID": self.postRef.documentID, "rating": post.rating, "timestamp": post.timestamp], forDocument: self.doc3Ref.collection("posts").document(self.postRef.documentID))
return nil
}) { (object, error) in
if let error = error {
print(error)
} else {
print("done")
}
}
Is it just not possible to do multiple .getDocument(documentReference) in a transaction?
Is there another way to accomplish this issue?
you should put all your if operations in do and else operations in catch
don't use them outside the block.
thats what the error is saying "Every document read in a transaction must also be written in that transaction.".
for ex. your code
// WRITES TO DOC1, DOC2, DOC3
guard let userPostCount = doc1.data()?["postCount"] as? Int,
let locationPostCount = doc2.data()?["postCount"] as? Int,
let itemPostCount = doc3.data()?["postCount"] as? Int,
let locationAvgRating = doc2.data()?["averageRating"] as? Float,
let itemAvgRating = doc3.data()?["averageRating"] as? Float else { return nil }
is doing operations with doc1,doc2 outside the scope of do catch block
I just noticed you are using Apple's swift scripting. My answer is standards based JavaScript but I hope it helps you. I don't use or know anything about Swift...
As far as reading data from 3 documents simultaneously, the way to do this is using javascript's .promise.all. While the below code is not precisely your solution, you can get the just of how to do it.
You basically push each .get into an array then you .promise.all that array .then you iterate over the returned data.
try {
targetRef.get().then(function(snap) {
if (snap.exists) {
for (var key in snap.data()) {
if (snap.data().hasOwnProperty(key)) {
fetchPromise = itemRef.doc(key).get();
fetchArray.push(fetchPromise);
}
}
Promise.all(fetchArray).then(function(values) {
populateSelectFromQueryValues(values,"order-panel-one-item-select");
if(!selectIsEmpty("order-panel-one-item-select")){
enableSelect("order-panel-one-item-select");
}
});
}
}).catch(function(error) {
toast("error check console");
console.log("Error getting document:", error);
});
}
I use this piece of code but the reload table always seems to load directly after the alamofire request instead of filling the data first. The braces are put in the correct way but it still loads Step 3 before Step 2.
Output:
- The filter list is not empty filling data according to filters.
- step 1
- step 3
- step 2: found 35 houses for Haarlem
- step 2: found 100 houses for Amsterdam
println("The filter list is not empty filling data according to filters.")
if let castedFilters = filters as? [Filter] {
println("step 1")
for filter in castedFilters{
var parameters : [String : NSObject] = ["apisleutel": "#########", "module": "Objecten", "get": "Huur", "plaats": filter.plaats, "pt": filter.maximumprijs, "pv": filter.minimumprijs, "wov": filter.oppervlakte, "ka": filter.kamers, "output": "json"]
self.makeCall(parameters) { responseObject, error in
let json = JSON(responseObject!)
/**/
let count: Int? = json["Response"]["objecten"]["object"].array?.count
if((count) != nil)
{
println("step 2: found \(count!) houses for "+filter.plaats)
if let ct = count {
for index in 0...ct-1 {
//Adding house to houses array
var adres = json["Response"]["objecten"]["object"][index]["adres"].string
let newHouse = House(straat: adres!)
self.houses.append(newHouses)
}
}
}
else
{
let alert = UIAlertView()
alert.title = "Fout"
alert.message = "No houses found, consider changing the filters."
alert.addButtonWithTitle("Ok")
alert.show()
}
return
}
}
println("step 3")
tableView.reloadData()
}
Call function
func makeCall(parameters: [String : NSObject], completionHandler: (responseObject: NSDictionary?, error: NSError?) -> ()) {
var heap: NSDictionary
Alamofire.request(.GET, "http://www.huizenzoeker.nl/api/v2/", parameters: parameters)
.responseJSON { request, response, responseObject, error in
completionHandler(responseObject: responseObject as? NSDictionary, error: error)
}
}