Append result of CloudKit into array in swift - arrays

i declare the contact variable as an empty string array
var contact = [String] ()
then I made a query to output the results from CloudKit, when I accessed the controller once, var contact succeeded in adding an array
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Note", predicate: predicate)
database.perform(query, inZoneWith: nil) { (record, error) in
for record: CKRecord in record! {
let name = record.value(forKeyPath: "content") as! String
print ("There is a note \"\(name)\"")
self.contact.append(name)
}
}
self.contact.append (name)
print ("There is a note \" \ (name) \ "")
but when accessing for the second time, the var contact becomes empty again
print ("check all array append \ (contact)")
Succes append firstime access the controller
Failed append secondtime access the controller
i use variable contact in other function
func sendSOS() {
if canSendText() {
//compese message with google link
let googleLink = "https://www.google.com/maps/place/" + String(myLatitude!) + "+" + String(myLongtitude!)
let SMStext = "EMERGENCY!!, Tolong Bantu saya di lokasi Latitude: " + String(myLatitude!) + "\n Longtitude: " + String(myLongtitude!) + " " + googleLink
let messsageCompose = MFMessageComposeViewController()
messsageCompose.messageComposeDelegate = self
messsageCompose.recipients = contact;
messsageCompose.body = SMStext
present(messsageCompose, animated: true, completion: nil)
}else{
// create the alert
let alert = UIAlertController(title: "No SMS available.", message: "Please find a better location and try again!", preferredStyle: UIAlertController.Style.alert)
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
return
}
}

It's difficult to determine what's going on because you're leaving out how the contact array is initialized relative to your CKQuery.
The database.perform line looks a little suspicious. I'm pretty sure that returns an array of CKRecords so you should have:
database.perform(query, inZoneWith: nil) { records, error in
//records is an optional array
if let records = records{
for record in records{
//You might have to parse record.values to get its key/value pairs
let name = record["content"] as? String ?? "No Name"
print("There is a note: \(name)")
//:::
self.contact.append(name)
}
}
}
As a side note, I recommend using CKQueryOperation for all queries (docs). It's a cleaner way to manage data flowing from CloudKit.

Related

Why do the values in the array suddenly disappear? Swift

I am new to Swift and I am wondering why the array is empty at the end of the function at the print("array printed2: \(newArr)") line, when at the first print statement print("array printed1: \(newArr)") it is filled with data.
Does anyone have any corrections or suggestions on how to make the data in the array stay?
Thanks in advance, the code for the function is below.
func testRdArr(){
var newArr=[Establishment]()
print("Downloading club data...")
let db = Firestore.firestore()
db.collection("clubs").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let data = document.data()
let name = data["Name"] as? String ?? "Name Unknown"
let description = data["description"] as? String ?? "Description Uknown"
let imageURL1 = data["imgUrl"] as? String ?? "Image Unknown"
let coordX = data["coordX"] as? String ?? "Unknown"
let coordY = data["coordY"] as? String ?? "Unknown"
let openingTimes=data["openingTimes"] as? [String] ?? ["Unknown"]
let weeklyEvents=data["weekly events"] as? [String] ?? ["Unknown"]
let postcode=data["postcode"] as? String ?? "Unknown"
let id = document.documentID
let x = Establishment(name: name, imageURL1: imageURL1, coordX: coordX, coordY: coordY, postcode: postcode, openingTimes: openingTimes, weeklyEvents: weeklyEvents, description: description, type: "club", id: id)
newArr.append(x)
}
}
//line below prints the populated array
print("array printed1: \(newArr)")
print("end of printing array")
}
//at this point array becomes empty, why does this happen and how would you suggest
//fixing it?
print("array printed2: \(newArr)")
}
The download happens in the background and completes after your print at the bottom of the function. It did not become empty. That line happened before the download was done.
See: Waiting for Asynchronous function call to complete

Swift dictionary returns nil after setting key and value pair [duplicate]

This question already has an answer here:
Dictionary with values as array on appending one at a time, remains empty
(1 answer)
Closed 3 years ago.
I'm using a table view controller to section users based off of their occupation. I'm starting off with a dictionary and then converting it into a struct to display the different sections.
The dictionary takes a string and array of user objects:
var userByOccupation: [String: [User]] = [:]
I pull the occupations from the backend (firestore), then the user, then I append the user to the specified occupation. However, whenever I set the value & key, then print out the value count from the dictionary, it's returning nil.
Im getting the error in getUsers() function:
(see the last 3 lines that are also marked with their output)
func getOccupations(){
let db = Firestore.firestore()
db.collection("occupations").getDocuments { (snapshot, err) in
if let error = err {
print("There was an error fetching documents: \(error)")
} else {
guard let documents = snapshot?.documents else { return }
for document in documents {
var occupationID = document.documentID
db.collection("occupations").document(occupationID).collection("users").getDocuments(completion: { (secondSnapshot, error) in
if let err = error {
print("There was an error fetching documents: \(err)")
} else {
guard let secondDocuments = secondSnapshot?.documents else { return }
for document in secondDocuments {
self.getUsers(occupationID: occupationID, userID: document.documentID)
}
}
})
}
}
}
}
func getUsers(occupationID: String, userID: String) {
let db = Firestore.firestore()
db.collection("users").document(userID).getDocument(completion: { (snapshot, error) in
if let err = error {
print("There was an error fetching documents: \(err)")
} else {
if let dictionary = snapshot?.data() {
let user = User(dictionary: dictionary as [String: AnyObject])
user.id = snapshot?.documentID
print(occupationID) //MARK - prints: Janitor
print(user.name) //MARK - prints: Jason
self.userByOccupation[occupationID]?.append(user) //MARK: Setting the key & values
print(self.userByOccupation.keys.count) //MARK - prints: nil.
}
}
})
}
Using ? with self.userByOccupation[occupationID] which is nil at first makes the statement affectless
self.userByOccupation[occupationID]?.append(user)
Change to
self.userByOccupation[occupationID] = [user] // or use +=

Load Firestore data to Table view Swift 4

I have a question related to load data from Firestore to table view. Basically, i understand how to do it, but in this case, i kind of confuse if the structure of Firestore as below:
"fund" -> "randomId" -> "agensi: id"
i was able to load the agensi from collection which is "WJ18kBvDLhVrvml2slXB". To get the real agensi name i have to get data from collection "Agensi" as image below:
below is the code that what i have already did:
var agencyname: [String] = []
func getData() {
db.collection("fund").getDocuments()
{
(querySnapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
}
else
{
// var agencyNumber = 0
for document in querySnapshot!.documents {
let data = document.data()
let agency = data["agensi"] as? String ?? ""
let agencyId = document.documentID
print(agency)
print(agencyId)
//MARK get name
let newdocRef = Firestore.firestore().document("Agensi/\(agency)")
newdocRef.getDocument { (docSnapshot, error) in
guard let docSnapshot = docSnapshot, docSnapshot.exists else { return }
let dataDetails = docSnapshot.data()
let agencyNew = dataDetails!["name"] as? String ?? ""
self.agencyname.append(agencyNew)
print("List of the agency: \(self.agencyname.append(agencyNew))")
}
}
self.tableView.reloadData()
}
}
}
i try to print:
self.agencyname.append(agencyNew)
but its display nothing. so, i cannot load the name of the agency into my table view cell. By the way sorry for my bad english. Thanks in advance
There are few things I would like to tell:
1. FireStore queries are asynchronous so there is a callback function when it finishes. Reload your tableView inside the callback instead of end of the loop only then it will load the data properly.
DispatchQueue.main.async {
self.tableView.reloadData()
}
Write above code just below print("List of the agency: \(self.agencyname.append(agencyNew))") line.
2. You're printing "self.agencyname.append(agencyNew)" and it is void by the way so will not print anything so print(\(self.agencyname)) instead.
3. When using a guard in a loop then don't use return because it will break the loop for next iteration if any error occurs. We should use continue here to let the loop execute completely.

CloudKit nil optional error

I am trying to load, then modify and resave an array.
Here is code, modify func is the top one:
func modifyUserGroupsRequestee(){
print("step2")
acceptedUsersArray.append(groupNameLbl.text!)
//error
userGroupRecordToUpdate.setObject(acceptedUsersArray as CKRecordValue?, forKey: "userGroups")
database.save(recordToUpdate) { (savedRecord, error) in
if error != nil{
print(error.debugDescription)
}else{
print("SAVED RECORD")
}
}
}
func resaveUserGroups(){
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
//operation.resultsLimit = CKQueryOperationMaximumResults
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil{
self.userGroupRecordToUpdate = record
// self.acceptedUsersArray = (record.object(forKey: "userGroups") as! Array)
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
// self.feedTableView.reloadData()
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
// }
//self.feedTableView.reloadData()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
//self.tableView.reloadData()
// print(leaderboardInfo.count)
}
}
The function prints step1 but never gets to step2. In the bottom function, I have an if let statement I tried to create to solve my nil issue (I commented my previous code above that line- self.acceptedUsersArray... Anyway, I believe I am implementing the if let statement incorrectly, because no data is loaded, even though there is data in cloud kit.
And I do have my personal user cloudKit records set up, here's a pic:
You should try to keep your code always indented consistently.
(In Xcode editor, Cmd+A (Select All), then Ctrl+I (Re-Indent).)
With confusing comments removed, your resaveUserGroups shows as:
func resaveUserGroups() {
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
self.userGroupRecordToUpdate = record
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
}
}
Omitting some parts to clarify:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
database.add(operation)
}
}
The line database.add(operation) exists inside the recordFetchedBlock.
You may need to fix some more parts (that's another story), but at least, you need to move the line out of the closure to execute the operation you have created:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
//database.add(operation)
} //↓
database.add(operation)
}
I just solved it. Apparently there always needs to be some kind of value inside the array even if it was just created. I was trying to query an array that existed in the recordType, but not yet under the specific record.

How get Single Url From an Array of urls?

This is My Code:
urll = NSURL(string: "http://xxxxxxxxxxx.com/api/?slider=uij6sdnb")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(urll) {(NSData, response, error) -> Void in
do {
let records = try NSJSONSerialization.JSONObjectWithData(NSData!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
for record in records {
// let urlid = Int(record["slide_id"] as! String)
let urimage = record["slide_url"] as! String
self.urls = [urimage]
print(self.urls.count)
}
}
catch {
print("Json Error")
}
}
task.resume()
When I Print :
print(urimage)
it gaves me 4 url like this:
http://sdkladlkasjd1.jpg
http://sdkladlkasjd2.jpg
http://sdkladlkasjd3.jpg
http://sdkladlkasjd4.jpg
When I print:
print(urimage[1])
It gaves me :
'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion
When i put it in another value :
var urls = [String]()
self.urls = [urimage]
and I print:
print(self.urls.count)
it gaves me
1
1
1
1
How on earch I can access one of this urls !?
I want to Show them on imageview but I can !
As Julian Kniephoff rightly mentioned, you are printing each URL in the for loop, thus you cannot access one particular one. However, there is also another issue, in that you are replacing the urls array with the latest url each time.
To solve this, simply replace the line self.urls = [urimage] with self.urls.append(urimage).
You can then access a particular image outside the for loop by doing something like self.urls[1].
This is also why printing the count of the array returns 1, since each time around you are setting the array to just the one latest element in the loop.
In the end, your code may look something like this
url = NSURL(string: "http://xxxxxxxxxxx.com/api/?slider=uij6sdnb")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) {(data, response, error) -> Void in
do {
let records = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
for record in records {
//let urlid = Int(record["slide_id"] as! String)
let urimage = record["slide_url"] as! String
self.urls.append(urimage)
}
print(self.urls[1]) //Prints http://sdkladlkasjd2.jpg
}
catch {
print("Json Error")
}
//print(self.urls[1])
}
task.resume()

Resources