how to deal with alamofire's async? - swift

I am trying to save the data I got from the JSON fire however, because of Alamofire's async nature I dont get the data I need instantly but only when I tap on the tableviewcell again (and the data is wrong too)
I am wondering what I should do here so that when I tap the tableviewcell it will get the data I needed (instead of empty arrays)
Here's my code:
class CurrencyExchangeViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
let exchange = currencyExchangeModel()
var past30DaysDateValueToPass = [String]()
var past30DaysPriceValueToPass = [Double]()
var valueToPass = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath) as? CurrencyExchangeTableViewCell
valueToPass = Array(self.exchange.currencyToGetExchangesDictionary.keys)[indexPath.row]
self.getPastData(currency: valueToPass)
performSegue(withIdentifier: "currencyHistorySegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "currencyHistorySegue") {
var viewController = segue.destination as? CurrencyHistoricalDataViewController
viewController?.historicalCurrency = valueToPass
viewController?.past30DaysPrice = self.exchange.currencyPast30DaysPriceArray
viewController?.past30DaysDate = self.exchange.currencyPast30DaysDatesArray
}
}
func getPastData(currency: String){
Alamofire.request("https://api.coindesk.com/v1/bpi/historical/close.json?currency=\(currency)").responseJSON{ (responseData) in
if responseData.result.value != nil {
let responseJSON = JSON(responseData.result.value)["bpi"].dictionaryObject
self.exchange.currencyPast30DaysDatesArray = Array(responseJSON!.keys)
self.exchange.currencyPast30DaysPriceArray = Array(responseJSON!.values) as! [Double]
}
}
}
}

If you want performSegue execute after getting data from Alamofire then remove
performSegue(withIdentifier: "currencyHistorySegue", sender: self)
this line to from didselect and place at getPastData.
func getPastData(currency: String){
Alamofire.request("https://api.coindesk.com/v1/bpi/historical/close.json?currency=\(currency)").responseJSON{ (responseData) in
if responseData.result.value != nil {
let responseJSON = JSON(responseData.result.value)["bpi"].dictionaryObject
self.exchange.currencyPast30DaysDatesArray = Array(responseJSON!.keys)
self.exchange.currencyPast30DaysPriceArray = Array(responseJSON!.values) as! [Double]
performSegue(withIdentifier: "currencyHistorySegue", sender: self)
}
}
This will help.

Related

TextFields don't get updated with values in unwind function Swift

I have textfields that should get a value displayed once returning from a TableViewController where user selects a cell. I get that value in unwindfunction, but textfieldsdon't get updated. When printing the value it prints correctly on unwinding, so unwindshould be set correctly, but it just don't get displayed in it's textfield. I also tried prepare(for unwind:in TableViewControllerbut with same results. Can you see what I'm doing wrong?
As always many thanks.
unwind function:
#IBAction func unwindToDetailsVc(segue: UIStoryboardSegue) {
//Insert function to be run upon dismiss of VC2
print("unwindSegue triggered")
if let vc = segue.source as? CityTableViewController {
print("segue source is city vc : \(String(describing: vc.city!))")
self.cityTextField.text = vc.city
}
if let vc = segue.source as? RegionTableViewController {
print("segue source is region vc : \(String(describing: vc.region!))")
self.regionTextField.text = vc.region
}
if let vc = segue.source as? CountryTableViewController {
print("segue source is country vc : \(String(describing: vc.country!))")
self.countryTextField.text = vc.country
}
}
didSelect in TableView:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! CityTableViewCell
self.city = cell.cityLabel.text ?? ""
performSegue(withIdentifier: "unwindSegue", sender: self)
// self.dismiss(animated: true, completion: nil)
}
prepare for unwind:
override func prepare(for unwind: UIStoryboardSegue, sender: Any?) {
if unwind.identifier == "unwindSegue" {
if let detailsVc = unwind.destination as? ShopDetailsTableViewController {
detailsVc.cityTextField.text! = city
}
}
}
textfield delegate function:
func textFieldDidBeginEditing(_ textField: UITextField) {
print("Editing textfield")
if textField.accessibilityIdentifier == "city" {
print("Editing city textfield")
performSegue(withIdentifier: "citySegue", sender: self)
} else if textField.accessibilityIdentifier == "region" {
print("Editing regio textfield")
performSegue(withIdentifier: "regionSegue", sender: self)
} else if textField.accessibilityIdentifier == "country" {
print("Editing country textfield")
performSegue(withIdentifier: "countrySegue", sender: self)
}
}
You can simply use a closure to solve this kind of problem statement,
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
#IBAction func openTableVC(_ sender: UIButton) {
if let controller = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "TableViewController") as? TableViewController {
controller.handler = {[weak self](city) in
DispatchQueue.main.async {
self?.textField.text = city
}
}
self.navigationController?.pushViewController(controller, animated: true)
}
}
}
class TableViewController: UITableViewController {
var handler: ((String)->())?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let city = "Delhi"
self.handler?(city)
self.navigationController?.popViewController(animated: true)
}
}
The above code is generic and will work in every case from where you want to open TableViewController.
I finally found out what I was doing wrong, I was calling the functions loading from CoreDatauser details and displaying them in viewWillAppear. Once I moved them in saveButtonafter the saving to CoreDatafunction call, it all works as expected. Textfield get updated with select values from tableviews.
Many thank to #PGDev for sharing a more convenient way of coding this, without all the if elsestatements and unwinds. Great example of higher level coding.

Pass Firebase Data to DetailView using CollectionViewCell _Swift

I am using Firebase Database to keep my data there. I can save all of the items successfully and basically can see them in the database console. I can also populate the collectionView cell with the stored data.
The only problem is that I cannot pass the data to detail view successfully.
I have looked at my other iOS app where I segue from tableview to view controller successfully but couldn't make it work with the collectionView in this new app.
This is the view controller that gets the data from firebase database. observeRecipes() function prints out the data in the console.
class RecipesViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var recipesCollectionView: UICollectionView!
var ref: DatabaseReference!
var recipes = [Recipe]()
override func viewDidLoad() {
super.viewDidLoad()
recipesCollectionView.delegate = self
recipesCollectionView.dataSource = self
observeRecipes()
IHProgressHUD.showImage(UIImage(named:"disabled")!, status: "Loading")
}
func observeRecipes() {
let ref = Database.database().reference(withPath: "recipes")
ref.observe(.value, with: { snapshot in
print(snapshot.value as Any)
var newRecipes: [Recipe] = []
for child in snapshot.children {
if let childSnapshot = child as? DataSnapshot,
let dict = childSnapshot.value as? [String:Any],
let showTitle = dict["RecipeName"] as? String,
let showTime = dict["RecipeTime"] as? String,
let recipeImage = dict["RecipePhoto"] as? String,
let ingridients = dict["Ingridients"] as? String,
let firstImageViewURL = dict["imageOne"] as? String,
let firstIngridients = dict["firstIngridientList"] as? String,
let firstInstructions = dict["firstInstructionList"] as? String,
let secondImageViewURL = dict["imageTwo"] as? String,
let secondIngridients = dict["secondIngridientList"] as? String,
let secondInstructions = dict["secondInstructionList"] as? String,
let thirdImageViewURL = dict["imageThird"] as? String,
let thirdIngridients = dict["thirdIngridientList"] as? String,
let thirdInstructions = dict["thirdInstructionList"] as? String,
let url = URL(string:recipeImage),
let urlFirst = URL(string: firstImageViewURL),
let urlSecond = URL(string: secondImageViewURL),
let urlThird = URL(string: thirdImageViewURL) {
let recipe = Recipe(title: showTitle, time: showTime, recipeImageURL: url, ingridients: ingridients, firstImageViewURL: urlFirst, firstIngridients: firstIngridients, firstInstructions: firstInstructions, secondImageViewURL: urlSecond, secondIngridients: secondIngridients, secondInstructions: secondInstructions, thirdImageViewURL: urlThird, thirdIngridients: thirdIngridients, thirdInstructions: thirdInstructions)
newRecipes.append(recipe)
}
}
self.recipes = newRecipes
self.recipesCollectionView.reloadData()
})
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return recipes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let recipeCell = collectionView.dequeueReusableCell(withReuseIdentifier: "recipeCell", for: indexPath) as! RecipeCollectionViewCell
recipeCell.set(cell: recipes[indexPath.row])
return recipeCell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showRecipeDetails" {
let destination = segue.destination as! RecipeDetailViewController
destination.selectedRecipe = sender as! Recipe
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = recipesCollectionView.cellForItem(at: indexPath) as! RecipeCollectionViewCell
performSegue(withIdentifier: "showRecipeDetails", sender: cell)
// performSegue(withIdentifier: "showRecipeDetails", sender: indexPath)
}
}
}
This is the detailViewController
class RecipeDetailViewController: UIViewController, UIScrollViewDelegate {
var recipes = [Recipe]()
var ref: DatabaseReference?
var selectedRecipe: Recipe?
// Recipe Details
#IBOutlet weak var recipeTitleLabel: UILabel!
#IBOutlet weak var recipeTimeLabel: UILabel!
#IBOutlet weak var recipeImageView: UIImageView!
#IBOutlet weak var ingridientsLabel: UILabel!
//General Outlets & Variables
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var closeButtonView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
recipeTitleLabel.text = selectedRecipe?.title
}
Below is the code that I have been using for prepareForSegue in my other app which works fine. I have tried to tweak this to fit into collectionView but somehow I cannot get it work.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetails" {
let destination = segue.destination as! StoryDetailsViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
destination.selectedStory = self.posts[indexPath.row]
print(indexPath.row)
}
}
}
You can try
performSegue(withIdentifier: "showRecipeDetails", sender: recipes[indexPath.item])
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showRecipeDetails" {
let destination = segue.destination as! RecipeDetailViewController
destination.selectedRecipe = sender as! Recipe
}
}

Blank labels after data passing in Swift 4?

I am currently trying to pass data from my table view controller to a second view controller but my labels and image are appearing as blank in the second view controller.
This is my main event view controller:
class EventsTableViewController: PFQueryTableViewController {
override func queryForTable() -> PFQuery<PFObject> {
let query = PFQuery(className: "Events")
//query.order(byAscending: "location")
query.order(byAscending: "date")
return query
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Upcoming Events"
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MasterToDetail" {
let detailVC = segue.destination as! DetailViewController
detailVC.myEventCell = sender as? EventCell
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath, object: PFObject?) -> PFTableViewCell? {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! EventCell
cell.dateLabel.text = object?.object(forKey: "date") as? String
cell.locationLabel.text = object?.object(forKey: "location") as? String
cell.nameLabel.text = object?.object(forKey: "name") as? String
let imageFile = object?.object(forKey: "image") as? PFFile
cell.eventImage.image = UIImage(named: "download")
cell.eventImage.file = imageFile
cell.eventImage.loadInBackground()
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "MasterToDetail", sender: EventCell())
}
#IBAction func reloadTable(_ sender: Any) {
self.loadObjects()
}
#IBAction func onSignOutTapped(_ sender: Any) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
performSegue(withIdentifier: "signOutSegue", sender: nil)
} catch {
print (error)
}
}
}
and my second view controller:
class DetailViewController: UIViewController {
#IBOutlet weak var detailImageView: UIImageView!
#IBOutlet weak var detailNameLabel: UILabel!
#IBOutlet weak var detailDescriptionLabel: UILabel!
#IBOutlet weak var detailLocationLabel: UILabel!
var myEventCell: EventCell?
override func viewDidLoad() {
super.viewDidLoad()
setUI()
}
func setUI () {
detailNameLabel.text = myEventCell?.nameLabel.text
detailImageView.image = myEventCell?.eventImage.image
detailLocationLabel.text = myEventCell?.locationLabel.text
}
}
As you send an empty cell here
performSegue(withIdentifier: "MasterToDetail", sender: EventCell())
You need
let cell = tableView.cellForRow(at:indexPath)
performSegue(withIdentifier: "MasterToDetail", sender:cell)

how to handle two collection view with push segues

I'm having two UICollectionView
#IBOutlet weak var collectionView1: UICollectionView!
#IBOutlet weak var collectionview2: UICollectionView!
i'm getting indexPath for each collection view separately with functions.
func getIndexPathForSelectedCell() -> NSIndexPath?
{
var indexPath:NSIndexPath?
if collectionview1.indexPathsForSelectedItems()!.count > 0 {
indexPath = collectionview1.indexPathsForSelectedItems()![0]
}
return indexPath
}
func getIndexPathForSelectedCell2() -> NSIndexPath?
{
var indexPath2:NSIndexPath?
if collectionView2.indexPathsForSelectedItems()!.count > 0 {
indexPath2 = collectionView2.indexPathsForSelectedItems()![0]
}
return indexPath2
}
I'm Performing segue for cell touch as follows.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if let indexPath = getIndexPathForSelectedCell()
{
let DealsdetailViewController = segue.destinationViewController as! DealsDetailViewController
DealsdetailViewController.Dealsdata = Dealsdata[indexPath.row]
}
else if let indexPath2 = getIndexPathForSelectedCell2()
{
let ContainerviewController = segue.destinationViewController as! ContainerViewController
ContainerviewController.BTdata = BTdata[indexPath2.row]
}
}
if i click on a cell in first collection view segue performs correctly, when i click on a cell in second collection view
i got error
in
let DealsdetailViewController = segue.destinationViewController as! DealsDetailViewController
which is first if statement condition value, i'm stuck here
please help me, how to handle performing both segue on cell click on each collection view.
Use the method from UICollectionView protocol
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = cellForItemAtIndexPath(indexPath)!
if collectionView == self.collectionView1 {
self.performSegueWithIdentifier("segue1", sender: cell)
} else if collectionView == self.collectionView2 {
self.performSegueWithIdentifier("segue2", sender: cell)
}
}
func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if segue.identifer == "segue1" {
let detailVC:DetailViewController = segue.destinationViewController as DetailViewController
// Your sender is cell. You have indexPath of them and can get his identity in dataSource.
//detailVC.name = ...
//detailVC.surname = ...
} else if segue.identifier == "segue2" {
//...
}
}

Search bar will only show first Cell in detail view

I have created a TableView with a Search Bar in Swift.
I have created 3 searchable objects. (131, 132, 137)
When i search for 137 and press 137(which is the only option to press), i still get to 131 detailView. Anyone know why? Thanks
Here is my code:
import UIKit
class TableViewController: UITableViewController, UISearchResultsUpdating {
let TitleList = ["131","132","137"]
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = self.resultSearchController.searchBar
self.tableView.reloadData()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! TableViewCell
cell.LabelTitle.text = TitleList[indexPath.row]
cell.CellDescription.text = DescriptionList[indexPath.row]
let imagename = UIImage(named: ImageList[indexPath.row])
cell.CellImage.image = imagename
if self.resultSearchController.active {
cell.LabelTitle?.text = self.filteredfarger[indexPath.row]
}
else {
cell.LabelTitle?.text = self.TitleList[indexPath.row]
}
return cell
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredfarger.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.TitleList as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredfarger = array as! [String]
self.tableView.reloadData()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DetailView") {
let VC = segue.destinationViewController as! DetailedViewController
if let indexpath = self.tableView.indexPathForSelectedRow {
let Title = TitleList[indexpath.row] as String
VC.SentData1 = Title
}
Because when you do the search the row for 137 is 0, and then in prepareForSegue you use the tableView.indexPathForSelectedRow (which means indexPath.row == 0) to get the title data. Try doing something like in your tableView.cellForRowAtIndexPath function where you check if resultSearchController is active or not, and if it is use filteredfarger instead of TitleList. Something like this maybe:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DetailView") {
let VC = segue.destinationViewController as! DetailedViewController
if let indexpath = self.tableView.indexPathForSelectedRow {
if self.resultSearchController.active {
let Title = self.filteredfarger[indexpath.row]
VC.SentData1 = Title
}
else {
let Title = TitleList[indexpath.row] as String
VC.SentData1 = Title
}
}
}
}
Better yet, you already give the cell some info, why not just use that, kinda like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DetailView") {
let VC = segue.destinationViewController as! DetailedViewController
if let cell = sender as? TableViewCell {
VC.SentData1 = cell.LabelTitle.text
}
}
}