Index out of range Outside Function of Alamofire Swift - arrays

I don't get when I call the let item = stationData[0] it says that its index is out of range... But inside my Alamofire request it returns that it has data...
Below is my code.
my alamofire is inside the viewdid load.
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default).responseJSON {
responseData in
if responseData.result.value != nil {
let swiftyJson = JSON(responseData.result.value as Any)
if let data = swiftyJson["data"].arrayObject as? [[String: Any]] {
if data.isEmpty{
print("NO DATA FOUND")
}
else {
self.stationData = data.map(StationData.init)
}
}
}
}
Here is my code where I use stationData
extension ParameterViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch itemType {
case .items:
return parameterName1.count
case .items1:
return parameterName2.count
case .items2:
return parameterName3.count
default:
print("No count to get")
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ValuesViewCell", for: indexPath) as! ValuesViewCell
let item = stationData[0]
switch itemType {
case .items:
cell.setData(parameterName: self.parameterName1[indexPath.row], parameterValue: item.vakue1)
case .items1:
cell.setData(parameterName: self.parameterName2[indexPath.row], parameterValue: item.value2)
case .items2:
cell.setData(parameterName: self.parameterName3[indexPath.row], parameterValue: item.value3)
default:
print("No cell to insert")
}
return cell
}
}

You should not access array directly by index if your array is not ready at the time of loading.
Instead of using
let item = stationData[0]
Try something like this
guard let item = stationData.first else {
return emptyCell
}
With this, you will always check if your array has the element.
Also you need to refresh the tableView when your data is ready from Alamofire.
Try this when you set stationData
else {
self.stationData = data.map(StationData.init)
self.tableView.reloadData()
}
Now, when your data is ready, tableView will reload and it will return proper cell with stationData's first element.

Related

Unable to add json key values in array why in swift

My json contains image, type and id.. here i want my ids in separate individual array called idArray.. here i am able to get single id in log, i have append ids to idArray but i am not getting ids in array it shows nil why?
i have taken idArray as string. please help me in code.
here is my json structure:
{
"financer": [
{
"id": "45",
"icon": "https://hello.com//images/img1.png"
"tpe": "bank"
}
{
"id": "40",
"icon": "https://hello.com//images/img2.png"
"tpe": "wallet"
}
.
.
.
]
}
here is my code:
import UIKit
import SDWebImage
struct JsonData {
var iconHome: String?
var typeName: String?
init(icon: String, tpe: String) {
self.iconHome = icon
self.typeName = tpe
}
}
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UITextFieldDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var itemsArray = [JsonData]()
var idArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
homeServiceCall()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeCollectionViewCell
let aData = itemsArray[indexPath.row]
cell.paymentLabel.text = aData.typeName
cell.paymentImage.sd_setImage(with: URL(string:aData.iconHome!), placeholderImage: UIImage(named: "GVMC_icon"))
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let nextViewController = self.storyboard?.instantiateViewController(withIdentifier: "MakePaymentViewController") as? MakePaymentViewController
{
nextViewController.financerId = idArray[indexPath.row]
self.navigationController?.pushViewController(nextViewController, animated: true)
}
else{
AlertFun.ShowAlert(title: "", message: "will update soon..", in: self)
}
}
//MARK:- Service-call
func homeServiceCall(){
let urlStr = "https://webservices/getfinancer"
let url = URL(string: urlStr)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
guard let respData = data else {
return
}
guard error == nil else {
print("error")
return
}
do{
let jsonObj = try JSONSerialization.jsonObject(with: respData, options: .allowFragments) as! [String: Any]
//print("the home json is \(jsonObj)")
let financerArray = jsonObj["financer"] as! [[String: Any]]
print("home financerData \(financerArray)")
for financer in financerArray {
let id = financer["id"] as? String
let pic = financer["icon"] as? String
let typeName = financer["tpe"] as! String
print("home financer id \(String(describing: id))")
self.idArray.append(id ?? "")
print("the home financer idsArray \(self.idArray.append(id ?? ""))")
self.itemsArray.append(JsonData(icon: pic ?? "", tpe: typeName))
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
catch {
print("catch error")
}
}).resume()
}
}
Unable to json ids in separate array please help me in my code.
Don't use multiple arrays as data source. That's very bad practice.
Create two structs conforming to Decodable
struct Root : Decodable {
let financer : [Financer]
}
enum Type : String, Decodable {
case bank, wallet
}
struct Financer : Decodable {
let id : String
let icon : URL
let tpe : Type
}
Declare the data source array
var itemsArray = [Financer]()
and delete
var idArray = [String]()
Replace homeServiceCall with
func homeServiceCall() {
let url = URL(string: "https://dev.com/webservices/getfinancer")
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
if let error = error { print(error); return }
do {
DispatchQueue.main.async {
self.activityIndicator.startAnimating()
}
let result = try JSONDecoder().decode(Root.self, from: data!)
self.itemsArray = result.financer
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
catch {
print(error) -- print always the error instance.
}
}).resume()
}
In cellForRow get the id value with
let aData = itemsArray[indexPath.row]
cell.paymentLabel.text = aData.id
Important note:
Never never ever use synchronous Data(contentsOf to load data from a remote URL. Use an API which loads the data asynchronously and caches the images

How to fix: Fatal error: Index out of range

I am attempting to limit my [String] array to only five values using swifts .prefix
First I take the original array items and splice it using .prefix
let testSlice = Array(items.prefix(5))
let newArray = Array(testSlice)
Then I validated the array holds only five values with a print line.
print("DEV: newArray value: \(newArray)")
if newArray != [] {
cell.profilePicture.playPortalProfilePic(forImageId: newArray[indexPath.item], { error in
if let error = error {
print("Error requesting profile pic: \(String(describing: error))")
}
})
} else {
print("items array was empty, value: \(items)")
}
newArray is then passed to a method provided by the SDK I am using to make requests for profilePictures. The newArray holds those values so [indexPath.item] is appropriate here. When this is functioning correctly it creates cells in a collection view dependent on how many values are in the array.
I am currently seeing Fatal error: Index out of range when this line attempts to run cell.profilePicture.playPortalProfilePic(forImageId: newArray[indexPath.item]
EDIT: Code requested by comments
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
Full method for cell.profilePicture.playPortalProfilePic(forImageId: newArray[indexPath.item] line
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
cell.profilePicture.layer.masksToBounds = true
let testSlice = Array(items.prefix(5))
let newArray = Array(testSlice)
print("DEV: newArray value: \(newArray)")
if newArray != [] {
cell.profilePicture.playPortalProfilePic(forImageId: newArray[indexPath.item], { error in
if let error = error {
print("Error requesting profile pic: \(String(describing: error))")
}
}
)
} else {
print("items array was empty, value: \(items)")
}
cell.backgroundColor = UIColor.cyan
cell.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
return cell
}
You use items as dataSource in numberOfItemsInSection while inside cellForItemAt use another newArray with less size by prefix(5)
hence the crash , so you should either return the count of newArrray.count inside numberOfItemsInSection or use testSlice alone

How to Convert URL Images to UIImages in Swift5 and append into a array

I have an API call GET in Swift 5 Code for fetching the Images, I am getting url of the images , I have to change the url into UIImage to append the urls to a arrayimages=UIImage, data of the url is there but it is not appending to the arrayImages. my task is to put all the data images into the collection view ,if there is another way then guide me , Thanks.
--->. let arrayImages = UIImages
guard let data = response?.data(using: .utf8) else {
return
}
do {
let jsonObj = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
if jsonObj["error"] as! Bool == false {
print("galleryResponse/\(jsonObj)")
let jsonResponse = jsonObj["response"] as! [[String: Any]]
for i in 0...jsonResponse.count-1 {
let strGalleryImage = jsonResponse[i]["Gallery_Full"] as? String
if let imgurl = strGalleryImage {
self.userImageString1 = "\(USER_IMAGE_BASE_URL)\(imgurl)"
}
var imageString1: String?
var url1: URL!
imageString1 = self.userImageString1
if let imageUrlString1 = imageString1 {
url1 = URL(string: imageUrlString1)
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: url1!){
if let imagedata = UIImage(data: data){
print("YES_IMG")
if data != nil {
DispatchQueue.main.async {
print("append_IMG")
self!.arrimages.append(imagedata)
}
}
}
}
}
}
//}
}
}
} catch {
print("Unable_to_load_data:/\(error)")
}
})
}
You can use AlamofireImage pod to convert the image URL to the image.
First, you need to install the pod file pod 'AlamofireImage'. Then import AlamofireImage in your ViewController.
Here is the way to implement that.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCollectionViewCellIdentifier", for: indexPath) as! YourCollectionViewCell
Alamofire.request("your image URL in String formate").responseImage { response in
debugPrint(response)
debugPrint(response.result)
if let image = response.result.value {
cell.YourImage.image = image
}
}
return cell
}
Hi I found out the Best solution i.e, through SDWebImages.
* Fist I get the response into url and then append it to a array = (String)[]
* then I called the sdwebimages in cellforitem function...
####CODE
// getting the url images into an array of string
let jsonResponse = jsonObj["response"] as! [[String: Any]]
print("galleryResponse/\(jsonObj)")
for i in 0...jsonResponse.count-1 {
let strGalleryImage = jsonResponse[i]["Gallery_Full"] as? String
print("o12\(strGalleryImage!)")
let str = String((strGalleryImage?.dropFirst(11))!) as? String
print("LAAL\(str)")
if let imgurl = str {
self.arrimages.append(imgurl)
print("kl\(self.arrimages)")
self.collectionView.reloadData()
}
THEN ->
//Calling the images into cellforitem
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
// called the url array into sd_setimages
cell.imgProfile.sd_setImage(with: URL(string: arrimages[indexPath.row]), placeholderImage: UIImage(named: "placeholder.jpg"))
return cell
}
Thanks for the answers but this is simplest solution of all...:)

How to add json data to array or similar using Swift

I am fairly new to Swift and I am having a few issues with getting understanding how to do what I want to do.
I am currently testing some stuff with json.
What I am trying to do is to append the data I get from my json data into an array. And when my array contains the data I wish to present it to my UICollectionView. I am assuming that I should be using an array.
import UIKit
import Foundation
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getData() {
let path = "http://myurl/test.json"
let url = URL(string: path)
let session = URLSession.shared
let task = session.dataTask(with: url!) { (data: Data?, response: URLResponse?, error: Error?) in
let json = JSON(data: data!)
for result in json["dokumentstatus"]["dokutskott"]["utskott"].array! {
let punkter = result["punkt"].string!
print("punkt: \(punkt)")
let rubrik = result["rubrik"].string
print("rubrik: \(rubrik)")
let forslag = result["forslag"].string
print("förslag: \(forslag)")
}
}
task.resume()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return //someArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCollectionViewCell
cell.customLabel.text = //Put the data form rubrik variable
cell.customTW.text = //Put the data from foreleg variable
return cell
}
}
the function getData() gets the correct json data I just need help understanding how to put this data to an array.
(Also, I know I probably shouldn't be getting the data in the ViewController, but this is only a test.)
import Foundation
class Information: NSObject {
var punkter: String?
var rubrik: String?
var forslag: String?
}
I'm thinking that maybe I should be using an array that looks something like this:var someArray = [Information]()
But I do not know how to then use my getData()
Or maybe I should be using three different arrays, one for each of my json variables.
Since you're still using a custom class (well done!, three different arrays are horrible) it's correct to declare a data source array like you suggested
var someArray = [Information]()
Most likely a struct is sufficient, I recommend to use non-optional strings
struct Information {
var punkter : String
var rubrik : String
var forslag : String
}
If the properties won't change you could even use let to make the properties constants.
To populate the array use the nil coalescing operator to check for nil,create the Information instance with the memberwise initializer and append the instance to the datasource array. Then reload the collection view on the main thread.
...
for result in json["dokumentstatus"]["dokutskott"]["utskott"].array! {
let punkter = result["punkt"].string ?? ""
let rubrik = result["rubrik"].string ?? ""
let forslag = result["forslag"].string ?? ""
let information = Information(punkter:punkter, rubrik:rubrik, forslag:forslag)
self.someArray.append(information)
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
...
Edit:
To display the data in cellForItemAtIndexPath use
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCollectionViewCell
let information = someArray[indexPath.row]
cell.customLabel.text = information.rubrik
cell.customTW.text = information.foreleg
return cell
}

Table View Number of Rows not returning array value

I am adding a bunch of values to an array and I am then returning the number of values as a number of rows in a table view. This is how I add my values to the array:
func getCurrentUserSchoolOrWorkAddressAndDisplayOtherUsers() {
let currentUser = (FIRAuth.auth()?.currentUser!)
let userID = currentUser?.uid
FIRDatabase.database().reference().child("users").child(userID!).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let schoolOrWorkAddress = snapshot.value!["schoolOrWorkAddress"] as! String
FIRDatabase.database().reference().child("schoolOrWorkAddress").child(schoolOrWorkAddress).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if(!snapshot.exists()){
return
}
let locations = snapshot.value! as! NSDictionary
for (index, location) in locations.enumerate() {
FIRDatabase.database().reference().child("users").child(location.key as! String).observeEventType(.ChildAdded, withBlock: { (snapshot: FIRDataSnapshot) in
if(snapshot.exists()){
print(snapshot)
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
user.id = snapshot.key
user.setValuesForKeysWithDictionary(dictionary)
self.users.append(user)
print(self.users)
}
}
}, withCancelBlock: nil)
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
})
})
}
I then return the array count as the number of table view rows like this:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
The only problem is for some reason it does not seem to return any count other than 0. I think the problem stems from the dispatch async code above because it is the first thing that is being called in the function. It is not being called after I append all my elements to an array which should be the problem. However, when I place the dispatch async inside of the for loop instead of outside it does not even get read. I tested this using break points.
the following code is mycellforrowatindexpath code block:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId, forIndexPath: indexPath) as! UserCell
let user = users[indexPath.row]
cell.textLabel?.text = user.fullName
cell.detailTextLabel?.text = user.email
if let userPhoto = user.userPhoto {
cell.profileImageView.loadImageUsingCacheWithUrlString(userPhoto)
}
return cell
}
The following is some print statement debugging I did which shows what is being appended to the array and what is the users.count value:
Basically, my question is how do I get it so that the table view is returning my actual value of my array. Once again, I think the problem stems from the dispatch async command.
Any help you have would be appreciated!

Resources