How to fix: Fatal error: Index out of range - arrays

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

Related

Index out of range Outside Function of Alamofire Swift

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.

How to update value of an item in Class based on UISwitch action?

I have item class as shown below:
struct Cur: Decodable {
let id: String?
let name: String?
let symbol: String?
let rank: String?
let switchVal: Bool?
}
This class fills an array and array fills UITableView.
Here how I access UISwitch action:
#IBAction func switchBtn(_ sender: UISwitch) {
if sender.isOn {
// See if user is in Search Mode
if inSearchMode {
// if user in search mode get the Cur from filtered array
if let switchRank = filterCur.index(where: {$0.switchVal}) {
print("This is the rank: \(filterCur[switchRank].rank!)")
}
} else {
if let switchRank = Cur.index(where: {$0.switchVal}) {
print("This is where it is: \(Cur[switchRank])")
}
}
} else {
// Handle deleting previously selected Curs
}
}
In order for me to find which item in the array is Switched On/Off I have to have the array already updated (items switchVal = true?). But, to update the array I need to know which item's switch is triggered.
So, where is my mistake?
Thank you
One way to solve this problem is to give every UISwitch the index of Cur by assigning it to the tag attribute.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.switch.tag = indexPath.row
...
}
Since you now know that every switch has the index, you can get the item by accessing your array with the switch's tag.
#IBAction func switchBtn(_ sender: UISwitch) {
let index = sender.tag
let cur = Cur[index]
}

swift 3 my array is empty even after I've added values to it

I'm currently working with swift 3 in order to create an application and I'm stumbling across a problem. Basically, what is happening is in my function, I add values to an array in a block and I print out the array and it seems fine, but when I exit the block, the array is the same as how I declared it globally.
This is my code..search for the comments to know where I append and print the array:
class listUsers: UITableViewController {
var usernames = [""]
var userIDs = [""]
var isFollowing = ["" : true]
//My array declared globally
var theArray = [1]
override func viewDidLoad() {
super.viewDidLoad()
let query = PFUser.query()
query?.findObjectsInBackground(block: { (objects, error) in
if error != nil {
print(error)
}else if let users = objects {
self.usernames.removeAll()
self.userIDs.removeAll()
self.isFollowing.removeAll()
//Notice here How i remove all the elements from my array so that it's empty
self.check.removeAll()
for object in users {
if let user = object as? PFUser {
self.usernames.append(user.username!)
self.userIDs.append(user.objectId!)
let query = PFQuery(className: "Followers")
query.whereKey("Follower", equalTo: (PFUser.current()?.objectId)!)
query.whereKey("Following", equalTo: user.objectId!)
query.findObjectsInBackground(block: { (objects, error) in
if let objects = objects {
if objects.count > 0 {
self.isFollowing[user.objectId!] = true
self.theArray.append(5)
//This prints out [5,5..]..the number of 5 is dependent on the for loop.
print(self.theArray)
}else{
self.isFollowing[user.objectId!] = false
}
if self.isFollowing.count == self.usernames.count {
self.tableView.reloadData()
}
}
})
}
}
}
self.tableView.reloadData()
})
//But when I reprint it here, it prints out [1] and I don't know how to fix it
print(theArray)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = usernames[indexPath.row]
print(theArray)
return cell
}
So in the cellForRowAt function when I print theArray, it prints it out as [1] a few times and than empty and than it prints it out with the actual numbers in it properly. But that's a problem since I only did that to debug but I will be user it to index and I'd need the actual values.
Any help would be great, thanks! (Also this piece of code simply queries through a table in a server but I believe what the rest of it is not that relevant to this question since elements are 100 percent being added to the array)
I figured it out...
I just had to delete the:
self.tableView.reloadData()
That was near the end of the viewDidLoad function

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