NSFetchedResultsController returns duplicates - ios

I parse JSON file, than I create new objects in Core Data managed context. At this point everything is ok, I get all valid objects. But when I try to renew data at tableView using NSFetchedResultsControllerDelegate method something goes wrong- NSFetchedResultsController returns the same object for 2 different index path. Moreover, if I try to catch the problem with breakpoints, 1 time at 3 NSFetchedResultsController start return valid objects. It's a bug of Core Data or else?
EDIT
Parsing JSON and save to Core Data:
private func performRequest(withURL url: URL) {
Alamofire.request(url).responseJSON(completionHandler: { (response) in
guard let result = response.result.value as? [String: Any] else {
return
}
DispatchQueue.main.async {
self.deleteAllPreviousData()
self.processResponse(result)
self.coreDataStack.saveContext()
}
})
}
private func deleteAllPreviousData() {
let fetchCity = NSFetchRequest<NSFetchRequestResult>(entityName: "City")
let fetchWeather = NSFetchRequest<NSFetchRequestResult>(entityName: "Weather")
let requestCityDelete = NSBatchDeleteRequest(fetchRequest: fetchCity)
requestCityDelete.affectedStores = coreDataStack.managedContext.persistentStoreCoordinator?.persistentStores
let requestWeatherDelete = NSBatchDeleteRequest(fetchRequest: fetchWeather)
requestWeatherDelete.affectedStores = coreDataStack.managedContext.persistentStoreCoordinator?.persistentStores
do {
try coreDataStack.managedContext.execute(requestWeatherDelete)
try coreDataStack.managedContext.execute(requestCityDelete)
} catch let error as NSError {
print("Delete fetching error: \(error), \(error.userInfo)")
}
}
private func processResponse(_ dictionary: [String: Any]) {
guard let cityList = dictionary["list"] as? [Any] else {
return
}
let newUpdateDate = Date()
for (id, city) in cityList.enumerated() {
//parse data from JSON
guard let city = city as? [String: Any] else {
return
}
let name = city["name"] as? String ?? ""
//FIXME: For debug
print(name)
guard let coordinates = city["coord"] as? [String: Any] else {
return
}
let lat = coordinates["lat"] as? Double ?? 0.0
let lon = coordinates["lon"] as? Double ?? 0.0
guard let main = city["main"] as? [String:Any] else {
return
}
let temperature = main["temp"] as? Int ?? 0
guard let weather = city["weather"] as? [Any] else { return }
guard let firstWeatherDescriptor = weather.first as? [String: Any] else { return }
let condition = firstWeatherDescriptor["main"] as? String ?? ""
let description = firstWeatherDescriptor["description"] as? String ?? ""
let iconName = firstWeatherDescriptor["icon"] as? String ?? "default_weather"
let newCity = City(context: coreDataStack.managedContext)
newCity.name = name
newCity.id = Int16(id)
newCity.renewData = newUpdateDate as NSDate
newCity.latitude = lat
newCity.longtitude = lon
let newWeather = Weather(context: coreDataStack.managedContext)
newWeather.city = newCity
newWeather.temperature = Int32(temperature)
newWeather.condition = condition
newWeather.conditionDescription = description
newWeather.iconName = iconName
}
}
Read data from Core Data
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//if no data was previous downloaded, than show Loading cell
if tableView.numberOfRows(inSection: 0) == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: updatingCellIdentifier, for: indexPath)
getLocation()
return cell
}
//FIXME: For debug
print("****")
print(indexPath)
print("****")
if indexPath.row == 0 {
return configureThisCityCell(at: indexPath)
} else {
return configureLocalCityCell(at: indexPath)
}
}
private func configureThisCityCell(at indexPath: IndexPath) -> CurrentCityTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: currentCityCellIdentifier, for: indexPath) as! CurrentCityTableViewCell
//TODO: configure cell
let currentCity = fetchedResultController.object(at: indexPath)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
cell.statusLabel.text = String(format: "Update at: %#", dateFormatter.string(from: currentCity.renewData! as Date))
cell.cityNameLabel.text = currentCity.name ?? NSLocalizedString("Unnown", comment: "Unnown city description")
cell.tempLabel.text = "\(String(describing: currentCity.weather!.temperature))°C"
cell.weatherDescriptionLabel.text = currentCity.weather?.conditionDescription
guard let conditionIconName = currentCity.weather?.iconName else { return cell }
guard let conditionIcon = UIImage(named: conditionIconName) else { return cell }
cell.weatherIconView.image = conditionIcon
return cell
}
private func configureLocalCityCell(at indexPath: IndexPath) -> LocalCityTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: localCityCellIdentifier, for: indexPath) as! LocalCityTableViewCell
//TODO: configure cell
let currentCity = fetchedResultController.object(at: indexPath)
//FIXME: For debug
print(indexPath)
print(currentCity.name)
print(currentCity.id)
print("__________")
cell.cityNameLabel.text = currentCity.name ?? NSLocalizedString("Unnown", comment: "Unnown city description")
cell.tempLabel.text = "\(String(describing: currentCity.weather!.temperature))°C"
cell.weatherDescriptionLabel.text = currentCity.weather?.conditionDescription
guard let conditionIconName = currentCity.weather?.iconName else { return cell }
guard let conditionIcon = UIImage(named: conditionIconName) else { return cell }
cell.weatherIconView.image = conditionIcon
return cell
}
extension CurrentWeatherTableViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
//TODO: this is works, but it is not solution, it's for time
do {
try controller.performFetch()
} catch let error as NSError {
print("Update error: \(error), \(error.userInfo)")
}
self.tableView.reloadData()
}
}

Related

Swift Firebase Selected UITableView Prototype Cells - UI Problem

There are users being downloaded from firebase and displayed on a UITableView where the cells are selectable and once selected will have a check mark. So from firebase is it is asynchronously downloaded so I think this could be the start of me solving the problem but not sure. When selecting lets say two cells when the view appears and then you begin scrolling through the list other cells will appear to be selected when the user did not select them. Below will be code and pictures of the occurrence.
Firebase Call
func getTableViewData() {
Database.database().reference().child("Businesses").queryOrdered(byChild: "businessName").observe(.childAdded, with: { (snapshot) in
let key = snapshot.key
if(key == self.loggedInUser?.uid) {
print("Same as logged in user, so don't show!")
} else {
if let locationValue = snapshot.value as? [String: AnyObject] {
let lat = Double(locationValue["businessLatitude"] as! String)
let long = Double(locationValue["businessLongitude"] as! String)
let businessLocation = CLLocation(latitude: lat!, longitude: long!)
let latitude = self.locationManager.location?.coordinate.latitude
let longitude = self.locationManager.location?.coordinate.longitude
let userLocation = CLLocation(latitude: latitude!, longitude: longitude!)
let distanceInMeters: Double = userLocation.distance(from: businessLocation)
let distanceInMiles: Double = distanceInMeters * 0.00062137
let distanceLabelText = String(format: "%.2f miles away", distanceInMiles)
var singleChildDictionary = locationValue
singleChildDictionary["distanceLabelText"] = distanceLabelText as AnyObject
singleChildDictionary["distanceInMiles"] = distanceInMiles as AnyObject
self.usersArray.append(singleChildDictionary as NSDictionary)
self.usersArray = self.usersArray.sorted {
!($0?["distanceInMiles"] as! Double > $1?["distanceInMiles"] as! Double)
}
}
//insert the rows
//self.followUsersTableView.insertRows(at: [IndexPath(row:self.usersArray.count-1,section:0)], with: UITableViewRowAnimation.automatic)
self.listedBusiness.reloadData()
}
}) { (error) in
print(error.localizedDescription)
}
}
TableView Setup
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != ""{
return filteredUsers.count
}
return self.usersArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomerAddSelectedBusinessesCell
var user : NSDictionary?
if searchController.isActive && searchController.searchBar.text != ""{
user = filteredUsers[indexPath.row]
} else {
user = self.usersArray[indexPath.row]
}
if cell.isSelected == true {
var user = self.usersArray[indexPath.row]
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .notDetermined, .restricted, .denied:
print("No access")
cell.businessName.text = String(user?["businessName"] as! String)
cell.businessStreet.text = String(user?["businessStreet"] as! String)
cell.businessCity.text = String(user?["businessCity"] as! String)
//cell.selectedCell.image = UIImage(named: "cellSelected")
let businessProfilePicture = String(user?["profPicString"] as! String)
if (businessProfilePicture.count) > 0 {
let url = URL(string: (businessProfilePicture))
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
cell.businessImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.businessImage.image = image
}
}
} else {
let image = UIImage(named: "default")?.potter_circle
cell.businessImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.businessImage.image = image
}
//cell.profileImage.image =
//cell.businessDistance.text = String(user?["distanceLabelText"] as! String)
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
print("****Called")
cell.businessName.text = String(user?["businessName"] as! String)
cell.businessStreet.text = String(user?["businessStreet"] as! String)
cell.businessCity.text = String(user?["businessCity"] as! String)
cell.businessDistance.text = String(user?["distanceLabelText"] as! String)
//cell.selectedCell.image = UIImage(named: "cellSelected")
let businessProfilePicture = String(user?["profPicString"] as! String)
if (businessProfilePicture.count) > 0 {
let url = URL(string: (businessProfilePicture))
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
cell.businessImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.businessImage.image = image
}
}
} else {
let image = UIImage(named: "default")?.potter_circle
cell.businessImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.businessImage.image = image
}
}
} else {
print("Location services are not enabled")
}
} else if cell.isSelected == false {
var user = self.usersArray[indexPath.row]
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .notDetermined, .restricted, .denied:
print("No access")
cell.businessName.text = String(user?["businessName"] as! String)
cell.businessStreet.text = String(user?["businessStreet"] as! String)
cell.businessCity.text = String(user?["businessCity"] as! String)
//cell.selectedCell.image = UIImage(named: "cellNotSelected")
let businessProfilePicture = String(user?["profPicString"] as! String)
if (businessProfilePicture.count) > 0 {
let url = URL(string: (businessProfilePicture))
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
cell.businessImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.businessImage.image = image
}
}
} else {
let image = UIImage(named: "default")?.potter_circle
cell.businessImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.businessImage.image = image
}
//cell.profileImage.image =
//cell.businessDistance.text = String(user?["distanceLabelText"] as! String)
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
print("%called")
cell.businessName.text = String(user?["businessName"] as! String)
cell.businessStreet.text = String(user?["businessStreet"] as! String)
cell.businessCity.text = String(user?["businessCity"] as! String)
cell.businessDistance.text = String(user?["distanceLabelText"] as! String)
//cell.selectedCell.image = UIImage(named: "cellNotSelected")
let businessProfilePicture = String(user?["profPicString"] as! String)
if (businessProfilePicture.count) > 0 {
let url = URL(string: (businessProfilePicture))
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
cell.businessImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.businessImage.image = image
}
}
} else {
let image = UIImage(named: "default")?.potter_circle
cell.businessImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.businessImage.image = image
}
}
} else {
print("Location services are not enabled")
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomerAddSelectedBusinessesCell
let user = usersArray[indexPath.row]
let name = user!["uid"] as? String
var NSdata = NSDictionary()
var realArray = [NSDictionary]()
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
if cell.accessoryType == .checkmark{
cell.accessoryType = .none
print("Deleted \(name!)")
if let idx = data.index(of:name!) {
data.remove(at: idx)
print(data)
}
} else {
cell.accessoryType = .checkmark
data.append(name!)
print(data)
}
}
print(data)
}
Selected Cells after view loads
Cells that appear to selected but are not
It's most common cell reusable issue. Cells which are visible to screen will be reused when you scroll.
Eg. You've 5 cells visible. when you select 2,3 and scroll down 7,8 will be selected.
To avoid this you've 2 options.
You can use external Bool array to manage this(Bool array count must be same as your array count).
You can put bool variable in your user dictionary to manage that.
So whenever your scroll, newly visible cell will not selected automatically.

Swift Firebase UITableViewCell loads before Data to populate cell is available

I am pushing data which is an array of strings to a tableview controller. These strings are "uid's" which are users in my database. With this array I make a call to firebase to extract all users and then do a match to the uid's. I am getting the correct data, yet I print out everything to make sure when the data is available and the data is available only after the tableview cell loads which causes the data to be nil causing a crash or just empty data. How can I make the data load first and then the cell so the data is available for display?
I've created functions for the data and now I have it in my viewDidLoad. Also, you'll see I have tried adding the firebase call into the Cell setup but of course that does not work.
Array of strings
var data = [String]()
viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
Database.database().reference().child("Businesses").observe(.value, with: { snapshot in
if snapshot.exists() {
self.businessUID = snapshot.value as? NSDictionary
if let dict = snapshot.value as? NSDictionary {
for item in dict {
let json = JSON(item.value)
let businessUid = json["uid"].stringValue
for uid in self.data {
if uid == businessUid {
let customerValue = self.businessUID?[uid]
self.businessDictionary = customerValue as! NSDictionary
print(self.businessDictionary)
print("Just printed the business dictionary")
}
}
}
}
} else {
print("does not exist")
}
})
}
Tableview Cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomerViewsSelectedBusinessesCell
print(self.businessDictionary)
print("Print the dictionary here to check the values")
let businessValues = self.businessDictionary
let uid = self.data.description
print(businessValues)
print("printed the business values")
if let dict = businessValues {
for item in dict {
let json = JSON(item.value)
let businessUid = json["uid"].stringValue
for uid in self.data {
if uid == businessUid {
let customerValue = self.businessUID?[uid]
self.businessData = customerValue as? NSDictionary
print(self.businessData)
print("Printing matching the uid values")
}
}
}
}
cell.businessName.text = businessData?["businessName"] as? String
cell.businessStreet.text = businessData?["businessStreet"] as? String
cell.businessCity.text = businessData?["businessCity"] as? String
cell.businessState.text = businessData?["businessState"] as? String
let businessProfilePicture = businessData?["profPicString"] as? String
if (businessProfilePicture!.characters.count) > 0 {
let url = URL(string: (businessProfilePicture!))
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
cell.profileImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.profileImage.image = image
}
}
} else {
let image = UIImage(named: "default")?.potter_circle
cell.profileImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.profileImage.image = image
}
return cell
}
Here is my solution. Got it to work. Appened and used "usersArray" to get and display the data.
var data = [String]()
var usersArray = [NSDictionary?]()
override func viewDidLoad() {
super.viewDidLoad()
Database.database().reference().child("Businesses").observe(.value, with: { snapshot in
if snapshot.exists() {
self.businessUID = snapshot.value as? NSDictionary
if let dict = snapshot.value as? NSDictionary {
for item in dict {
let json = JSON(item.value)
let businessUid = json["uid"].stringValue
for uid in self.data {
if uid == businessUid {
let customerValue = self.businessUID?[uid]
self.usersArray.append(customerValue as! NSDictionary)
self.followUsersTableView.reloadData()
}
}
}
}
} else {
print("does not exist")
}
})
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.usersArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomerViewsSelectedBusinessesCell
let user : NSDictionary?
user = self.usersArray[indexPath.row]
cell.businessName.text = String(user?["businessName"] as! String)
cell.businessStreet.text = String(user?["businessStreet"] as! String)
cell.businessCity.text = String(user?["businessCity"] as! String)
cell.businessState.text = String(user?["businessState"] as! String)
let businessProfilePicture = String(user?["profPicString"] as! String)
if (businessProfilePicture.characters.count) > 0 {
let url = URL(string: (businessProfilePicture))
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
cell.profileImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.profileImage.image = image
}
}
} else {
let image = UIImage(named: "default")?.potter_circle
cell.profileImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.profileImage.image = image
}
return cell
}

how to save indexpaths temporarily in swift 3?

In filter selection view controller i will select the brands and then when i press apply it will pop to another screen and if the user to come back again to same page the previously selected should have check mark and that should save temporarily until the user leaves from filter page to list page
func downloadJsonWithURL() {
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil { print(error!); return }
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] {
self.products = jsonObj.map{ Product(dict: $0) }
DispatchQueue.main.async {
self.tableDetails.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "filterSelectionCell", for: indexPath) as! FilterSelectionCell
activityIndicator.stopAnimating()
activityIndicator.hidesWhenStopped = true
tableDetails.isHidden = false
let product = products[indexPath.row]
cell.brandProductName.text = product.name
cell.accessoryType = product.selected ? .checkmark : .none
if namesArray.count != 0 {
for name in namesArray{
if product.name.contains(name){
print(product.name)
cell.accessoryType = .checkmark
}
else {
cell.accessoryType = .none
}
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selected = products[indexPath.row].selected
products[indexPath.row].selected = !selected
tableView.reloadRows(at: [indexPath], with: .none)
let selectedItems = products.filter{ $0.selected }
let selectedNames = products.filter{ $0.selected }.map{ $0.name }
let cell = tableView.cellForRow(at: indexPath)
selectedIndexPathArray.append(indexPath as NSIndexPath)
}
struct Product {
let name : String
let value : String
let img : String
let id : Int
var selected = false
init(dict : [String:Any]) {
self.name = dict["name"] as? String ?? ""
self.value = dict["value"] as? String ?? ""
self.img = dict["img"] as? String ?? ""
self.id = dict["id"] as? Int ?? 0
}
}

Some cells won't show

I'm trying to create a chat applications with tableview to show the messages. Everything works fine except that some cells just won't show. It aren't always the same cells. I'm getting the messages from my Firebase database.
My Code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = cellCache.object(forKey: indexPath as AnyObject) as? UITableViewCell{
return cell
}
let cell = messagesTableView.dequeueReusableCell(withIdentifier: "otherMessageCell") as! OtherMessageTableViewCell
DispatchQueue.global(qos: .userInteractive).async {
let message = self.messages[indexPath.row]
let ref = FIRDatabase.database().reference()
let uid = FIRAuth.auth()?.currentUser?.uid
if(uid == message.sender) {
cell.sender.textAlignment = .right
cell.message.textAlignment = .right
}else{
cell.sender.textAlignment = .left
cell.message.textAlignment = .left
}
let uidReference = ref.child("Users").child(message.sender!)
uidReference.observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let username = dictionary["Username"] as! String
let imageLink = dictionary["Image Link"] as! String
cell.sender.text = username
cell.message.text = message.message
cell.profileImage.image = nil
cell.profileImage.loadImageWithURLString(urlString: imageLink)
}
}, withCancel: nil)
}
DispatchQueue.main.async {
self.cellCache.setObject(cell, forKey: indexPath as AnyObject)
}
return cell
}
Example:
I hope someone will be able to help me. Thanks
I've found a solution:
var savedSenders = [Int: String]()
var savedMessages = [Int: String]()
var savedImages = [Int: UIImage]()
var savedAlignments = [Int: NSTextAlignment]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = messagesTableView.dequeueReusableCell(withIdentifier: "otherMessageCell") as! OtherMessageTableViewCell
if(savedSenders[indexPath.row] != nil && savedMessages[indexPath.row] != nil && savedImages[indexPath.row] != nil && savedAlignments[indexPath.row] != nil) {
cell.sender.textAlignment = savedAlignments[indexPath.row]!
cell.message.textAlignment = savedAlignments[indexPath.row]!
cell.sender.text = savedSenders[indexPath.row]
cell.message.text = savedMessages[indexPath.row]
cell.profileImage.image = savedImages[indexPath.row]
return cell
}
cell.sender.text = ""
cell.message.text = ""
cell.profileImage.image = nil
DispatchQueue.global(qos: .userInteractive).async {
let message = self.messages[indexPath.row]
let ref = FIRDatabase.database().reference()
let uid = FIRAuth.auth()?.currentUser?.uid
if(uid == message.sender) {
cell.sender.textAlignment = .right
cell.message.textAlignment = .right
self.savedAlignments.updateValue(.right, forKey: indexPath.row)
}else{
cell.sender.textAlignment = .left
cell.message.textAlignment = .left
self.savedAlignments.updateValue(.left, forKey: indexPath.row)
}
let uidReference = ref.child("Users").child(message.sender!)
uidReference.observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let username = dictionary["Username"] as! String
let imageLink = dictionary["Image Link"] as! String
cell.sender.text = username
cell.message.text = message.message
cell.profileImage.image = nil
cell.profileImage.loadImageWithURLString(urlString: imageLink)
let url = URL(string: imageLink)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if(error != nil){
print(error as Any)
return
}
DispatchQueue.main.async {
if let downloadedImage = UIImage(data: data!) {
let image = downloadedImage
self.savedSenders.updateValue(username, forKey: indexPath.row)
self.savedMessages.updateValue(message.message!, forKey: indexPath.row)
self.savedImages.updateValue(image, forKey: indexPath.row)
}
}
}.resume()
}
}, withCancel: nil)
}
return cell
}
The data for every cell is saved into the arrays (savedSenders, savedMessages, savedImages and savedAlignments). If I don't save it into arrays, the cells will have to load all the data from Firebase and this will take longer. If it takes longer, it won't look good.
I tested everything and it works.

Swift Adding objects from on array to another array correctly?

So I have 2 NSMutableArrays, one called testArray and the other called jsonArray. jsonArray gets its objects from a mysql server using json and php. Then those same objects in jsonArray are inserted in testArray. I did print(jsonArray, testArray) and what showed in the logs were this.
I also have a NSObject class called Test, if that helps..
For the jsonArray
{
testName = GreenCorn;
testStatus1 = 12;
testStatus2 = 13;
testURL = "";
id = 1;
}
For the testArray
"<CustomCellSwift.Test: 0x17414df70>"
Now I'm new to iOS Swift but I don't know if I inserted the jsonArray into testArray correctly. Here is the code I used. Also, I'm using a custom tableview and its supposed to show testArray.count, its empty cells but its showing the several rows that I have in jsonArray.
var followedArray: NSMutableArray = []
var testArray: NSMutableArray = []
var jsonArray: NSMutableArray = []
var filteredArray: NSArray = []
var isFiltered: Bool = false
// Number of Rows in Section
internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if !isFiltered {
if section == 0 {
return followedArray.count
}
else if section == 1 {
return testArray.count
}
}
return filteredArray.count
}
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let CellIdentifier = "Cell"
var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! CustomCell
if cell != cell {
cell = CustomCell(style: UITableViewCellStyle.default, reuseIdentifier: CellIdentifier)
}
// Coloring TableView
myTableView.backgroundColor = UIColor.white
// Configuring the cell
var testObject: Test
print("before ifFiltered")
if !isFiltered {
if indexPath.section == 0 {
print("isFiltered if")
testObject = followedArray[indexPath.row] as! Test
cell.populateCell(testObject, isFollowed: true, indexPath: indexPath, parentView: self)
}
else if indexPath.section == 1 {
print("isFiltered if 2")
testObject = testArray[indexPath.row] as! Test
cell.populateCell(testObject, isFollowed: false, indexPath: indexPath, parentView: self)
}
}
else {
print("isFiltered else")
testObject = filteredArray[indexPath.row] as! Test
cell.populateCell(testObject, isFollowed: false, indexPath: indexPath, parentView: self)
}
return cell
}
// Retrieving Data from Server
func retrieveData() {
let getDataURL = "http://exampleip.org/json.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: Data = try Data(contentsOf: url as URL)
jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Setting up testArray
let testArray: NSMutableArray = []
// Looping through jsonArray
for i in 0..<jsonArray.count {
// Create Test Object
let tID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
let tName: String = (jsonArray[i] as AnyObject).object(forKey: "testName") as! String
let tStatus1: String = (jsonArray[i] as AnyObject).object(forKey: "testStatus1") as! String
let tStatus2: String = (jsonArray[i] as AnyObject).object(forKey: "testStatus2") as! String
let tURL: String = (jsonArray[i] as AnyObject).object(forKey: "testURL") as! String
// Add Test Objects to Test Array
testArray.add(Test(testName: tName, andTestStatus1: tStatus1, andTestStatus2: tStatus2, andTestURL: tURL, andTestID: tID))
print("retrieveData")
print(jsonArray, testArray)
}
}
catch {
print("Error: (Retrieving Data)")
}
myTableView.reloadData()
}
Am I doing this correctly? Why does my tableview have empty cells?
First, your networking/serialization code shouldn't be in your ViewController, but this is a better way to do things:
func retrieveData() {
let getDataURL = "http://exampleip.org/json.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: Data = try Data(contentsOf: url as URL)
guard let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [[String : AnyObject]] else {
print("Error Retrieving Data")
return
}
let testArray = jsonArray.flatMap(Test.init)
// make sure the add the result in your viewController
self.myTableView.models = testArray
}
catch {
print("Error: (Retrieving Data)")
}
myTableView.reloadData()
}
extension Test {
convenience init?(with jsonDictionary: [String : AnyObject]) {
guard let tID = jsonDictionary["id"] as? String, let tName = jsonDictionary["testName"] as? String, let tStatus1 = jsonDictionary["testStatus1"] as? String,
let tStatus2 = jsonDictionary["testStatus2"] as? String, let tURL = jsonDictionary["testURL"] as? String else {
return nil
}
self(testName: tName, andTestStatus1: tStatus1, andTestStatus2: tStatus2, andTestURL: tURL, andTestID: tID)
}
}
I couldn't really test it so there may be some errors, but that should point you in the right direction.
Removing this line of code worked.
let testArray: NSMutableArray = []

Resources