Swift 3 - Can not use array made Alamofire request - arrays

hoping that someone can help me! Working on this one for a while now. Basically my problem is that I can not use the data in a UItableviewcontroller that is received by an Alamofire request. The request I want to put in a struct in the alamofire request. In the end I can not use the information what is put in the array. Looks like the array keeps empty outside the Function. for that I tried to make a closure, I receive the table in viewDidLoad request, but still can not use it outside that one.
I have a structure in a swift file called Section:
struct Mijnproducten {
var productname : String!
var productdesc : String!
var expanded : Bool!
init(productname: String, productdesc: String, expanded: Bool)
{
self.productname = productname
self.productdesc = productdesc
self.expanded = false
}
}
UitableviewController looks like this:
Array I made:
var mijnproducten01 = [Mijnproducten]()
Below the fund with alamofire request:
func GetUserProperty(completion: #escaping ([Mijnproducten]) -> Void) {
Alamofire.request(URL_USER_PROPERTY, method: .post, encoding: JSONEncoding.default) .responseJSON(completionHandler: { response -> Void in
//getting the json value from the server productid, tagid, status, productimage
switch response.result {
case .success(let value):
let jsonArray = JSON(value)
print(value)
var Mijnproducten01 : [Mijnproducten] = []
for (index, dict) in jsonArray {
let thisObject = Mijnproducten(productname: dict["productname"].string!, productdesc: dict["productdesc"].string!, expanded: false )
Mijnproducten01.append(thisObject) }
completion(Mijnproducten01)
case .failure(let error):
print(error)
completion([])
}
self.tableView.reloadData()
})
}
In the viedDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
GetUserProperty(completion: { data in
self.mijnproducten01 = data
print("yess", self.mijnproducten01)})
print("nooo",mijnproducten01)
}
There is printed information in the yesss print, but not in the nooo.
In the end the goal is to substract information from the array and use it in the tableCell and Header.
When I put this in the header function:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "expandableHeaderView") as! ExpandableHeaderView
header.customInit(Productname: mijnproducten01[Mijnproducten].productname, Productdesc: mijnproducten01[Mijnproducten].productdesc, section: section, delegate: self)
//print("print", mijnproducten01)
return header
}
I get an error in: mijnproducten01[Mijnproducten].productname.
I hope some one can help on the way!

Thanks! I found a solution, maybe not the best way but it works. I added an extra UI label in the rows. That UI label gets assigned the value that is also used in the header. In the end I made the UILabel not visible. In this way I get with every row in a section the wright header description that I can get in didSelectRow!

Related

Populate array using function in other file in swift

I'm learning how to get data using Github API and making a simple app in swift. Tutorials I've been watching make an empty array and geting response function in the same controller file. However, I want my view controller to keep as small as possible, and I decided to create getting data function in another file. My question is how can I access the empty array in a different file after I fetch data. Maybe this explanation make you confused, so I put my code below.
This is my view controller file, and I want to populate the repositories array after fetch the data using an API call.
import UIKit
class RepositoryListVC: UIViewController {
var tableView = UITableView()
var repositories: [Repository] = []
override func viewDidLoad() {
super.viewDidLoad()
title = "AAAAAA"
configureTableView()
Service.fetchData()
}
func configureTableView() {
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 100
tableView.register(RepositoryCell.self, forCellReuseIdentifier: Cells.repositoryCell)
tableView.pin(to: view)
}
}
extension RepositoryListVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Cells.repositoryCell) as! RepositoryCell
cell.set()
return cell
}
}
This is the file for fetching data using an API call. I want to populate the repository valuable in the view controller file above. I have successfully get data and parse JSON data, so I want to push Repository(...) to repository array, but I was not able to figure out how...
import UIKit
import SwiftyJSON
import Alamofire
struct Service {
static func fetchData() {
AF.request(API.gitHubEndpoint).responseJSON { (response) in
switch response.result {
case .success(let value):
let json = JSON(value)
let repositoryItemArr = json["items"].arrayValue
for item in repositoryItemArr {
Repository(userImageUrl: item["owner"]["avatar_url"].stringValue, userName: item["owner"]["user_name"].stringValue, repositoryName: item["owner"]["repository_name"].stringValue, starNum: item["owner"]["star_number"].intValue)
}
case .failure(let error):
print(error)
}
}
}

Swift - Tableview won't reload correctly after loading for the second time

I am a little confused about how my Tableview is reacting. My problem is that the Tableview shows the other order than Dictionary than when I print the Dictionary. When I print the Dictionary everything seems to be fine, but when I look at my phone the tableview is showing the cell at a random order again.
First I fetch the JSON.
var Aanbiedingen_bieren = [Aanbiedingen_bier]()
var Aanbiedingen_bieren_Filter = [Aanbiedingen_bier]()
Fetch data.
func fetchData() {
Aanbiedingen_bieren_Filter.removeAll()
Aanbiedingen_bieren.removeAll()
let url_Aanbiedingen_bier = URL(string: "\(Firebase_url)")
let downloadURL = url_Aanbiedingen_bier
URLSession.shared.dataTask(with: downloadURL!) { data, urlResponse, error in
let data = data
print("downloaded")
do
{
let decoder = JSONDecoder()
let downloadedBiers = try decoder.decode(SheetJS.self, from: data!)
self.Aanbiedingen_bieren = downloadedBiers.SheetJS
for jsonData in self.Aanbiedingen_bieren {
let Soort = jsonData.Title
if self.Soort_Bier == "Alles" {
self.Aanbiedingen_bieren_Filter.append(jsonData)
}else{
if self.Soort_Bier == "Krat" {
if Soort.contains(word: "Krat") {
self.Aanbiedingen_bieren_Filter.append(jsonData)
}
}
if self.Soort_Bier == "Fles" {
if Soort.contains(word: "Fles") {
self.Aanbiedingen_bieren_Filter.append(jsonData)
}
}
}
}
self.Aanbiedingen_bieren_Filter = self.Aanbiedingen_bieren_Filter.sorted(by: {$0.Voor_prijs.compare($1.Voor_prijs, options: .numeric) == .orderedAscending})
print(self.Aanbiedingen_bieren_Filter)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}.resume()
}
The first time that the code runs the Soort_Bier = "Alles". After a button tap the Soort_Bier will change in Soort_bier = "Krat". After the data was loaded for a second time I first wanted to filter the data. I have this done by implementing an if statement that checks if the Title of the JSON has a specific word in the String and if so append it to an other array. After that, I wanted to sort the price. When the sorting is finished I wanted to print the Dictionary to see if the sorting is correct. This still seems to be the case. Then I want to reload the tableView so all of the cells will show and here is something wrong. When I want to load the dictionary for the second time, it doesn't seem to reload the tableView correctly.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Aanbiedingen_bieren_Filter.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Aanbiedingen_bieren_Cell", for: indexPath) as! Aanbiedingen_bieren_Cell
let Aanbiedingen_bier = self.Aanbiedingen_bieren_Filter[indexPath.row]
cell.Aanbiedingen_bier = Aanbiedingen_bier
return cell
}
Struct:
struct SheetJS: Codable {
var SheetJS: [Aanbiedingen_bier]
init(SheetJS: [Aanbiedingen_bier]) {
self.SheetJS = SheetJS
}
}
struct Aanbiedingen_bier: Codable {
let Logo_Image: String
let Van_prijs: String
let Voor_prijs: String
let Beschrijving: String
let Item_Image: String
let Title: String
let Bestel_Online_link: String
let Footer_item: String
let Item_1: String
let Item_2: String
init(Logo_Image: String, Item_Image: String, Van_prijs: String, Voor_prijs: String, Beschrijving: String, Title: String, Item_1: String, Item_2: String, Bestel_Online_link: String, Footer_item: String) {
self.Logo_Image = Logo_Image
self.Title = Title
self.Item_Image = Item_Image
self.Beschrijving = Beschrijving
self.Van_prijs = Van_prijs
self.Voor_prijs = Voor_prijs
self.Item_1 = Item_1
self.Item_2 = Item_2
self.Bestel_Online_link = Bestel_Online_link
self.Footer_item = Footer_item
}
}
JSON:
{
"SheetJS": [
{
"Logo_Image": "https://www.biernet.nl/images/winkel/17335-agrimarkt.gif",
"Van_prijs": "€16,99",
"Voor_prijs": "€10,49",
"Beschrijving": "Krat 24x0,30",
"Item_Image": "https://www.biernet.nl/images/soort/23026-grolsch%20krat%20normale%20flesjes%2030%20cl.png",
"Title": "Grolsch Premium Pilsener",
"Bestel_Online_link": "",
"Footer_item": "t/m zaterdag 3 augustus",
"Item_1": "€6,50 korting (38%)",
"Item_2": "€1,46 per liter"
},//Some more data
]
}
Only price printed of dictionary:
€5,39
€5,94
€6,39
€6,39
€7,64
€16,19
Result
Please let me know if you want more information or code.
It's because items in Dictionary ordered randomly.
If you want to order it, you should use 'Set' another ordered collection type.
Or see the Apple Document below
Apple Docs : The order of key-value pairs in a dictionary is stable between mutations but is otherwise unpredictable. If you need an ordered collection of key-value pairs and don’t need the fast key lookup that Dictionary provides, see the KeyValuePairs type for an alternative.
I think you haven't been using wrongly data collection. You should use array to order. Because A dictionary stores associations between keys of the same type and values of the same type in a collection with no defined ordering. Unlike array, a dictionary do not have a specified order.
Click here! to read about data collection in swift

Why have a Array Empty and Error in swift4?

I've spent a few hours trying to get a fetch to work in my film sheet. I need to open film's View of my colectionview Items. I could follow different guide and post but it always give me an empty array. I'm newbie, sorry for my question but I need your help.
Here's my code:
var taskArrayScheda : NewFilm?
override func viewDidLoad() {
super.viewDidLoad()
self.fetchData()
if(self.taskArrayScheda != nil) {
let schedaOk = taskArrayScheda
mostraDatiNellaScheda(schedaOk!)
} else { print("errore array vuoto") }
}
func mostraDatiNellaScheda(_ sched:NewFilm) {
// get title
titoloScheda.text = sched.titolo
}
func fetchData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "NewFilm")
do {
taskArrayScheda = try context.fetch(NewAnime.fetchRequest())
💥ERROR ::::: Cannot assign value of type '[Any]' to type 'NewFilm?'
} catch {
print(error)
}
}
The fetch() returns an array. But currently you assign the fetch() result to the single object var taskArrayScheda.
You'll need something like:
var taskArrayScheda: [NewFilm]?
Then you should do:
taskArrayScheda = try context.fetch(NewAnime.fetchRequest()) as? [NewFilm]
I assume here that NewAnime is a subclass of NewFilm, which seems to make sense looking at these two class names.

Array returning an empty array outside of PFQuery with Parse

I'm trying to populate the array postCaptions.
I need to return this array to numberOfRowsInSection for my table view, but it returns an empty array every time.
var postCaptions = [String]()
query2.whereKey("userid", equalTo: PFUser.current()?.objectId!)
query2.findObjectsInBackground(block: { (objects, error) in
if let userPosted = objects {
for object in userPosted {
if let userPost = object as? PFObject {
self.postImageFiles.append(userPost["userPostImage"] as! PFFile)
self.postLocation.append(userPost["userPostLocation"] as! String)
self.postCaptions.append(userPost["postCaption"] as! String)
print(self.postCaptions.count) //returns value of 2 for the two posts that I've made
self.tableView.reloadData()
self.refresher.endRefreshing()
}
}
}
})
print(self.postCaptions.count) //Returns empty array
I know that the issue is with the order of the threading and I understand that, but I'm not sure exactly what I can do to make the array remain populated outside of the query. I've seen this as a method for solving this problem, but I really haven't found a straightforward answer for how I can fix this problem in my code. If someone could provide an answer that works with my code that would be amazing!:) I've been stuck with this problem for more than a week now
var postCaptions = [String]() {
didSet {
// Do any execution that needs to wait for postCaptions here.
}
}
**cellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userPhotoFeedCell", for: indexPath) as! FeedCell
if PFUser.current()?.objectId != nil {
cell.userUsername.text = PFUser.current()?.username
}
//downloads images for user profile pic
imageFiles[indexPath.row].getDataInBackground { (data, error) in
if let imageData = data {
let downloadedImage = UIImage(data: imageData)
cell.userProfilePic.image = downloadedImage
}
}
//downloades images and items to populate user post
postImageFiles[indexPath.row].getDataInBackground { (data, error) in
if let userPostImageData = data {
let downloadedUserPostImage = UIImage(data: userPostImageData)
cell.userFeedPhoto.image = downloadedUserPostImage
}
}
cell.postLocation.text = postLocation[indexPath.row]
cell.postCaption.text = postCaptions[indexPath.row]
cell.userFeedPhoto.image = UIImage(named: "OrangeFuego")
cell.userProfilePic.image = UIImage(named: "usericon.png")
return cell
}
If I am understanding your issue it's rather simple. Set var postCaptions = [String]() as a global variable within the desired class. Then append your values from iterations to it the way you've done. Then the postCaptions values can be accessed in other parts of your code and you can use the count property.
Since the method findObjectsInBackground suggests that it is executed in the background, that means it has nothing to do with your UI, therefore your array will remain empty until this function is completed. At the moment you print that array, this function has not yet completed, so postCaptions will be empty for now.
And also that's why the numberOfRows will be zero.
I think what you need to do is to put this query into viewDidLoad or your designated initialiser, and call tableView.reloadData() as you did inside the completion block. Also, you might want to declare a Bool, to determine whether the table is loading or not, and in your completion block, set the isLoading to false after successfully executing the query.
query2.findObjectsInBackground(block: { (objects, error) in
if let userPosted = objects {
self.isLoading = false
for object in userPosted {
if let userPost = object as? PFObject {
self.postImageFiles.append(userPost["userPostImage"] as! PFFile)
self.postLocation.append(userPost["userPostLocation"] as! String)
self.postCaptions.append(userPost["postCaption"] as! String)
print(self.postCaptions.count) //returns value of 2 for the two posts that I've made
}
}
self.tableView.reloadData()
self.refresher.endRefreshing()
}
})
and inside your numberOfRowsInSection :
if isLoading { //you need to declare isLoading
return 1 //maybe a dummy cell to tell user the table is loading
} else {
return postCaptions.count
}

Class proper not assigned in swift

I'm doing a practice, and find my class property(adNames) can't be assigned. But it can be assigned outside(the place where I comment out). Here is my code:
import UIKit
import Alamofire
import SwiftyJSON
class AdTableViewController: UITableViewController {
var adNames: [JSON]?
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "http://codewithchris.com/code/afsample.json").responseJSON { (Response) -> Void in
if let value = Response.result.value{
let json = JSON(value)
print(json["secondkey"].arrayValue) //["item1","item2","item3"]
self.adNames = json["secondkey"].arrayValue
}
}
// self.adNames = ["item1", "item2", "item3"]
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let names = adNames {
return names.count
}
return 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
}
}
Can anybody tell me the reason? Thanks!
Your Alamofire request runs in the background. Meanwhile, your tableView wants to display and calls numberOfRowsInSection. By this time, Alamofire has not finished its work, so adnames hasn't been set.
You need to refresh the tableView with reloadData() after adNames is set. i.e.
Alamofire.request(.GET, "http://codewithchris.com/code/afsample.json").responseJSON { (Response) -> Void in
if let value = Response.result.value{
let json = JSON(value)
self.adNames = json["secondkey"].arrayValue
self.tableView.reloadData()
}
}
Then the tableView will refresh and re-call numberOfRowsInSection, and at this time, adNames will not be nil.
You are accessing self inside a closure which is passed to the request call. Use capture lists inside the closure to hold the references to the self object.
[weak self] (Response) -> Void in (in this case you have to use self? to access the self instance)
...
or [unowned self] (Response) -> Void in
If you are sure that the object will be present after the request call completes.

Resources