I have a Firebase structure like this:
"userObjects" : {
"fP8g5kxrjnBhYTiUxjF6Pdc5xfgP" : {
"objectsInLists" : {
"603648351" : {
"Top" : true
},
"097598765" : {
"Roof" : true
}
}
},
I would like to get all true values under an objectID (097598765 is one objectID).
So I want check if 097598765 exists, and if it does I want to print "Roof"
This is as far I have come:
self.ref?.child("userObjects").child(userID!).child("objectsInLists").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild(self.objectID) {
print(snapshot.value)
}
})
What you're looking for is queryOrderedByValue(), since you're looking to filter on the value of the child nodes that you're querying:
self.ref?.child("userObjects").child(userID!).child("objectsInLists/097598765")
.queryOrderedByValue().queryEqual(toValue: true)
.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
print(snap.key)
}
})
Related
I have checkboxes required for product return on a page with more than one product, and when I press a checkbox, I want to store the id and quantity of the selected product in the items in the post parameter that I want to send.But, I cannot send the correct data. How can I send correct data?
{
"reasonId" : "001",
"cancel" : "true",
"description": "" ,
"items": [
{
"id": "874a8064-bebf-41c3-98a8-6ac39a54156a",
"quantity" : 480
},
{
"id": "d7af8722-58cb-4bd0-9927-47de44ba2e0b",
"quantity" : 2
},
{
"id": "f799d66e-cfcd-4f2b-9603-1facf2fedbea",
"quantity" : 1
},
{
"id": "5ea0c31f-952a-4fbc-b623-9086030193ad",
"quantity" : 17
}
]
}
I can print the id and quantity of the selected product.
private func createCheckBox(_ model : OrderDetailItems, tag: Int) -> UIView {
let rootView = UIView()
returnItemCount = model.definition?.count
var checkBox: ViewCheckLabel!
checkBox = ViewCheckLabel(text: "",
range: "",
action: {
// CHECKK SELECT OR NOT SELECT
checkBox.buttonCheck.checkboxAnimation {
if checkBox.buttonCheck.isSelected {
print(model.quantity)
print(model.id)
} else {
}
}
})
This is my post request
func cancelOrderApi() {
if let parentVC = parentViewController as? MyProfileViewController {
parentVC.startActivityIndicator(mainView: parentVC.view)
let parameters : Parameters = [
"reasonId" : reasonId,
"cancel" : isOrdrReturnable,
"description": "",
"items" : ""
]
NetworkLayer.request(Router.returnOrder(parameters, orderReturnId ?? "")).responseDecodable(of: OrderCancel.self) { (response) in
switch response.result {
case .failure(let error):
Logger.shared.debugPrint("Error while fetching tags: \(String(describing: error))")
return
case .success(let response):
if response.code == 200 {
parentVC.showAlert(mesg: response.message ?? "Siparişiniz İade edilmiştir", title: "Başarılı")
} else {
parentVC.showAlert(mesg: response.message ?? "", title: "Hata")
Logger.shared.debugPrint(response.message ?? "")
}
Logger.shared.debugPrint(response)
}
DispatchQueue.main.async {
parentVC.stopActivityIndicator()
parentVC.profileTableView.reloadData()
}
}
}
}
I am not entirely sure what you want to achieve, since the question is unclear, however here is a tip how to create Data object (can be later inserted into Url request) from your JSON file. Firstly manually copy your JSON file somewhere to your project and give it name yourJSONFile , then you can create Data object with create createDataFromJson() function.
func createDataFromJson() -> Data? {
if let path = Bundle.main.path(forResource: "yourJSONFile", ofType: "json") {
do{
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
return data
} catch {
print("catched : \(error.localizedDescription) ❌")
return nil
}
} else {
return nil
}
}
How can I sort the following sections made dynamically from the datestamp field? For example the sections called 2021-11-3 needs to be displayed in the tableview above 2021-11-2
datestamp format: 2021-11-3
var sections = [mySections]()
var structure = [myStructure]()
private func fetchJSON() {
guard let url = URL(string: "test.com")
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "id=\1".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
self.structure.sort { $0. datestamp > $1.datestamp }
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0. datestamp })
let keys = grouped.keys.sorted()
self.sections = keys.map({mySections(date: $0, items: grouped[$0]!
)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print(error)
}
}.resume()
}
I have tried doing the following under self.sections but it does not do anything:
self.sections.sorted { $0.date > $1.date }
Struct:
struct mySections {
let date : String
var items : [myStructure]
}
struct myStructure: Decodable {
let recordid: Int
let testname: Int
let datestamp: String
}
Example of Data:
[
{
"recordid": 1,
"testname": "Jen",
"datestamp": "2021-11-3"
},
{
"recordid": 1,
"testname": "Jake",
"datestamp": "2021-11-2"
}
]
Right now, you're sorting structure based on dateTimeStamp, which doesn't do anything, because structure doesn't have any data in it.
In my playground example, like your code, I used Dictionary(grouped:). Then, I map those results to mySections and sort by date.
The result of the print at the end is:
["2021-11-4", "2021-11-3", "2021-11-2"]
let data = """
[
{
"recordid": 1,
"testname": "Jake",
"datestamp": "2021-11-2"
},
{
"recordid": 2,
"testname": "Jen",
"datestamp": "2021-11-3"
},
{
"recordid": 3,
"testname": "Bob",
"datestamp": "2021-11-3"
},
{
"recordid": 4,
"testname": "Bill",
"datestamp": "2021-11-4"
}
]
""".data(using: .utf8)!
struct mySections {
let date : String
var items : [myStructure]
}
struct myStructure: Decodable {
let recordid: Int
let testname: String
let datestamp: String
}
var sections = [mySections]()
var structure = [myStructure]()
do {
let decoder = JSONDecoder()
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.datestamp })
sections = grouped.map { mySections(date: $0.key, items: $0.value) }
.sorted { $0.date > $1.date }
print(sections.map(\.date))
} catch {
print(error)
}
It's probably worth noting that right now your dates are stored in a uniform format that works well for string-based sorting. However, if your endpoint returned dates that weren't uniformly-formatted (like 2021-11-04 and 2021-11-3) you would want to convert to an actual Date first and sort based on that.
I have added a function to search user on firebase.
Here is the code:
func searchUser(search: String, includeCurrentUser: Bool = true, completion: #escaping ([User]) -> (), withCancel cancel: ((Error) -> ())?) {
Database.database().reference().child("users").queryOrdered(byChild: "username").queryStarting(atValue: search , childKey: "username").queryEnding(atValue: search + "\u{f8ff}", childKey: "username").observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String: Any] else {
completion([])
return
}
var users = [User]()
dictionaries.forEach({ (key, value) in
if !includeCurrentUser, key == Auth.auth().currentUser?.uid {
completion([])
return
}
guard let userDictionary = value as? [String: Any] else { return }
let user = User(uid: key, dictionary: userDictionary)
users.append(user)
})
users.sort(by: { (user1, user2) -> Bool in
return user1.username.compare(user2.username) == .orderedAscending
})
completion(users)
}){ (err) in
print("Failed to fetch all users from database:", (err))
cancel?(err)
}
}
When searchUser is called, my Xcode console states:
[Firebase/Database][I-RDB034028] Using an unspecified index. Your data will be downloaded and filtered on the client. Consider adding ".indexOn": "username" at /users to your security rules for better performance
Here are my firebase rules:
{
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
"rules": {
".read": true,
".write": true,
"posts" : {
".indexOn": "creationDate"
},
"purchaser" : {
"$postID" : {
".indexOn": ".value"
}
},
"users" : {
"$uid": {
".indexOn" : "username"
}
}
}
}
And here is my database structure:
So the searchUser function works, but the xcode console continue to asks for .indexOn for username.
Why is going wrong here?
Indexes needs to be defined on the location where you use them in your query. So to define an index on /users with the username property of each child node:
"users" : {
".indexOn" : "username"
}
I’m stuck with some problem. I have a JSON response from server:
{
"days" : [
{
"id" : 1,
"name" : "Day 1 - first day",
"url": "http://example.com/days/1"
},
{
"id" : 2,
"name" : "Day 2 - second day",
"url": "http://example.com/days/2"
},
...
],
"week" : [
{
"id" : 1,
"dayIds" : [1, 2, 6, 9, 23, 44, 2345],
"name" : "Rest week"
},
{
"id" : 35,
"dayIds" : [34,77,23,67,126,224],
"name" : "Educational week"
},
],
"plan" : {
"weekIds: [1, 6, 23, 74]
}
}
My data models (without mapping):
class Day: Object {
#objc dynamic var id: Int = -1
#objc dynamic var name: String = ""
#objc dynamic var url: String = ""
}
class Week: Object {
var dayIds = List<String>()
#objc dynamic var name: String = ""
#objc dynamic var id: Int = -1
var days: List<Week>? = nil
}
class Plan: Object {
var weekDays = List<String>()
var weeks: List<Week>? = nil
}
Mapping code:
let json: [String: Any] = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
let plans: [Plan] = Mapper<Plan>().mapArray(JSONArray: json["plans"] as! [[String: Any]])
let days: [Day] = Mapper<Day>().mapArray(JSONArray: json["days"] as! [[String: Any]])
let weeks: [Week] = Mapper<Week>().mapArray(JSONArray: json["weeks"] as! [[String: Any]])
So, I need to tell realm that an array weeks belong to plan.weeks and an array days belong to object week.days and related by theirs id’s. How can I do this more simply? Do you have any ideas?
The alternative solution is in-head brute force like this.
for week in weeks {
for dayId in week.dayIds {
for day in days {
if day.id == dayId {
week.days.append(day)
}
}
}
}
for plan in plans {
for week in weeks {
for weekId in plans.weekIds {
if weekId == week.id {
plan.weeks.append(week)
}
}
}
}
I believe that somewhere exist more pure and simple solution :)
Thanks.
Your data structures seem very nested, so you're going to have to do the internal looping. If you want something more swifty, use map and filter here instead of for loops:
let days = weeks.map({
$0.dayIds.map({
$0.filter({
$0.id == dayId
})
})
})
I've just started using Firebase for my Swift programming . I managed to fetch some data from the Json using the following :
let ref = FIRDatabase.database().reference()
ref.child("Posts").child("1").child("Post").child("Like").observeSingleEvent(of: .value, with: { (Snapshot) in
let Value = Snapshot.value as? Int
print(Value!)
})
However , I am trying to fetch the whole object first and then get the value of each element later . So here is my attempt but my app keeps getting terminated :
let ref = FIRDatabase.database().reference()
ref.child("Posts").child("1").child("Post").observeSingleEvent(of: FIRDataEventType.childAdded, with: { (snapshot) in
print(snapshot.children.value(forKey: "Like") as? Int)
print(snapshot.children.value(forKey: "Image") as? String)
})
Any idea where am I making a mistake ?
here is my Json :
{
"Posts": {
"1": {
"Post": {
"Poster_ID": "ID",
"Post_Time_stamp": "12:12:12AM",
"Image": "img.png",
"Video": "video.mov",
"Like": "3",
"Dislike": "0",
"Massage": {
"Massage_ID": "MSG_ID",
"Massager": "id",
"Massage_Time_stamp": "12:12:12PM",
"Massage_content": "comment"
}
}
},
"2": {
"Post": {
"Poster_ID": "ID",
"Post_Time_stamp": "12:12:12AM",
"Image": "img.png",
"Video": "video.mov",
"Like": "3",
"Dislike": "0",
"Massage": {
"Massage_ID": "MSG_ID",
"Massager": "id",
"Massage_Time_stamp": "12:12:12PM",
"Massage_content": "comment"
}
}
}
},
"User": {
"1": {
"User_name": "name",
"User_pass": "pass",
"User_avatar": "img.png"
},
"2": {
"User_name": "name",
"User_pass": "pass",
"User_avatar": "img.png"
}
}
}
Try printing snapshot first and see if you are able to retrieve anything if no, Try this
Use FIRDataEventType as .value instead of .childAdded. Since .childAdded is only fired when a child node is added to that reference node, .value retrieves the current value as that reference point..
let ref = FIRDatabase.database().reference()
ref.child("Posts").child("1").child("Post").observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot)
print(snapshot.children.value(forKey: "Like") as? Int)
print(snapshot.children.value(forKey: "Image") as? String)
})
if you want to get the post object you need to access snapshot.value also observe for value event instead of childAdded.
let ref = FIRDatabase.database().reference()
ref.child("Posts").child("1").child("Post")
.observeSingleEvent(of: .value, with: { (snapshot) in
if let dict = snapshot.value as? [String:Any] {
print(dict["Like"] as? Int ?? 0)
}
})