I get this error when I run the application.
Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444
I pull data from firebase and upload it to MapKit in UITableViewCell. It was working on my friend's computer but when I run it again on my own computer, I got this error. How can I solve this problem?
ViewController:
import UIKit
import Firebase
import SDWebImage
import MapKit
import CoreLocation
class anasayfaViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var tableView: UITableView!
var UserNameArray = [String]()
var UserCommentArray = [String]()
var UserImageArray = [String]()
var DocumentIDarray = [String]()
var UserLocationArray = [GeoPoint]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return UserNameArray.count
}
/*
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
*/
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! HomeCell
let username = UserNameArray[indexPath.row]
let comment = UserCommentArray[indexPath.row]
let imageURL = UserImageArray[indexPath.row]
let location = UserLocationArray[indexPath.row]. ------ (Error Line )
cell.prepare(username: username, imageURL: imageURL, comment: comment, location: location)
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
//tableView.estimatedRowHeight = 100
getdataFromFirestore()
}
func getdataFromFirestore(){
let firestoreDatabase = Firestore.firestore()
firestoreDatabase.collection("Posts").order(by: "Date", descending: true).addSnapshotListener { [weak self] (snapshot, error) in
if let error = error {
print(error.localizedDescription)
return
}
if snapshot?.isEmpty != true {
self?.UserCommentArray.removeAll(keepingCapacity: false)
self?.UserNameArray.removeAll(keepingCapacity: false)
self?.UserImageArray.removeAll(keepingCapacity: false)
self?.documentIDarray.removeAll(keepingCapacity: false)
self?.UserLocationArray.removeAll(keepingCapacity: false)
for document in snapshot!.documents {
let documentID = document.documentID
self?.documentIDarray.append(documentID)
if let sharedBy = document.get("SharedBy") as? String {
self?.UserNameArray.append(sharedBy)
}
if let sharedComment = document.get("SharedComment") as? String {
self?.UserCommentArray.append(sharedComment)
}
if let sharedUrl = document.get("imageUrl") as? String {
self?.UserImageArray.append(sharedUrl)
}
if let sharedLocation = document.get("Location") as? GeoPoint {
self?.UserLocationArray.append(sharedLocation)
}
}
self?.tableView.reloadData()
}
}}
}
Related
I have a tableview loading data from firebase database. When i open my app the data does not populate. it crashes and throws the error index out of range. I am fairly new to xcode and would appreciate the help. i have been given recommendations on how to fix my code with regards to having multiple arrays but everyone that advices forget i'm new and does realize that i am pulling imageurls into my tableview too and dont know how to adopt their recommendations into my code. if i could get help getting passed this error that would be awesome.
import UIKit
import FirebaseAuth
import FirebaseDatabase
class EventsViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var eventsRef: DatabaseReference?
var eventsDatabaseHandle:DatabaseHandle?
var eventsTitles = [String]()
var eventTimestamps = [String]()
var eventsLocations = [String]()
var eventsImages = [UIImage]()
#IBOutlet weak var addEventsButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
adminAuth()
eventsRef = Database.database().reference()
tableView.reloadData()
tableView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
tableView.delegate = self
tableView.dataSource = self
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { (snaphot) in
let eventPost = snaphot.value as! [String: Any]
self.eventTimestamps.append(eventPost["eventdate"] as! String)
self.eventsTitles.append(eventPost["eventtitle"] as! String)
self.eventsLocations.append(eventPost["eventlocation"] as! String)
let task = URLSession.shared.dataTask(with: URL(string: eventPost["ImageUrl"] as! String)!) {(data, response, error) in
if let image: UIImage = UIImage(data: data!) {
self.eventsImages.append(image)
}
}
task.resume()
self.tableView.reloadData()
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventsTitles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events") as! EventsTableViewCell
// let image = eventsImages[indexPath.row]
cell.flyerImages.image? = eventsImages[indexPath.row] **<- index out of range**
cell.eventTitle.text! = eventsTitles[indexPath.row]
cell.eventDate.text! = eventTimestamps[indexPath.row]
cell.eventLocation.text! = eventsLocations[indexPath.row]
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
return cell
}
func adminAuth() {
if (Auth.auth().currentUser!.displayName != "Neil Leon") {
self.addEventsButton.tintColor = UIColor.clear
self.addEventsButton.isEnabled = false
}
else{
self.addEventsButton.isEnabled = true
}
}
}``````
At the moment reloadData is called the image array is empty and causes the crash
Use a custom struct and reload the table view after the image data has been received
struct Event {
let title, timestamp, location : String
var image : UIImage?
}
var events = [Event]()
...
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { (snaphot) in
let eventPost = snaphot.value as! [String: Any]
let event = Event(title: eventPost["eventtitle"] as! String,
timestamp: eventPost["eventdate"] as! String,
location: eventPost["eventlocation"] as! String,
image: nil)
let task = URLSession.shared.dataTask(with: URL(string: eventPost["ImageUrl"] as! String)!) { data, _, error in
if let error = error {
print(error)
} else {
event.image = UIImage(data: data!)
DispatchQueue.main.async {
self.events.append(event)
self.tableView.reloadData()
}
}
}
task.resume()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events", for: indexPath) as! EventsTableViewCell
let event = events[indexPath.row]
cell.flyerImages.image = event.image
cell.eventTitle.text = event.title
cell.eventDate.text = event.timestamp
cell.eventLocation.text = event.location
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
return cell
}
I have a tableview loading data from firebase database. When I open my app the data does not populate. when I create a new post I can see the tableview cells modifying like changed were made but the post doesn't populate. I can't figure it out.
import UIKit
import FirebaseAuth
import FirebaseDatabase
class EventsViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var eventsRef: DatabaseReference?
var eventsDatabaseHandle:DatabaseHandle?
var eventsTitles = [String]()
var eventTimestamps = [String]()
var eventsLocations = [String]()
var eventsImages = [UIImage]()
#IBOutlet weak var addEventsButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
adminAuth()
eventsRef = Database.database().reference()
tableView.reloadData()
tableView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
tableView.delegate = self
tableView.dataSource = self
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { (snaphot) in
let eventPost = snaphot.value as! [String: Any]
self.eventTimestamps.append(eventPost["eventdate"] as! String)
self.eventsTitles.append(eventPost["eventtitle"] as! String)
self.eventsLocations.append(eventPost["eventlocation"] as! String)
let task = URLSession.shared.dataTask(with: URL(string: eventPost["ImageUrl"] as! String)!) {(data, response, error) in
if let image: UIImage = UIImage(data: data!) {
self.eventsImages.append(image)
}
}
self.tableView.reloadData()
task.resume()
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventsImages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events") as! EventsTableViewCell
let image = eventsImages[indexPath.row]
cell.flyerImages.image! = image
cell.eventTitle.text! = eventsTitles[indexPath.row]
cell.eventDate.text! = eventTimestamps[indexPath.row]
cell.eventLocation.text! = eventsLocations[indexPath.row]
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
tableView.reloadData()
return cell
}
func adminAuth() {
if (Auth.auth().currentUser!.displayName != "Neil Leon") {
self.addEventsButton.tintColor = UIColor.clear
self.addEventsButton.isEnabled = false
}
else{
self.addEventsButton.isEnabled = true
}
}
}
image of empty tableview
]
So the code below is not tested as I don't have firebase setup currently.
However, observing childAdded... the documentation says it will pass all of the current records in the database at first and will then just post new additions. So all you need to do is loop through them, setup your tableView data source and reload the table.
Rather than use multiple arrays for values I've created an array of ChurchEvent objects instead.
struct ChurchEvents {
let title: String
let location: String?
let date: Date?
let imageUrlString: String?
init(dict: [String: Any]) {
self.title = dict["title"] as String
self.location = dict["location"] as? String
// etc
}
}
var events = [ChurchEvents]()
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { snapshot in
let list = snapshot.value as? [[String : AnyObject]]
let newEvents = list.map { ChurchEvent(dict: $0) }
events.append(newEvents)
tableView.reloadData()
}
Other improvements you could make:
class EventsTableViewCell: UICollectionViewCell {
func configure(with event: ChurchEvent {
eventDate.text = event.date
eventTitle.text = event.title
eventLocation.text = event.location
// etc
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events") as! EventsTableViewCell
let event = events[indexPath.row]
cell.configure(with: event)
return cell
}
I am trying to learn firebase.I have two folders on firebase
1)VDBackgroundFrames/
2)VDFrames/
In both folders ,we have 4 images - VDBG2.png,VDBG3.png,VDBG4.png,VDBG5.png.
I am able to access one image at a time from firebase using the following code:-
func firebaseSetUp(){
let store = Storage.storage()
let storeRef = store.reference()
let userProfilesRef = storeRef.child("VDBackgroundFrames/VDBG11.jpg")
userProfilesRef.downloadURL { (url,error) in
if error != nil {
print("error?.localizedDescription",error?.localizedDescription)
return
}else{
print("url",url!)
}
}
}
//==========updated code ====//
func firebaseSetUp(){
let store = Storage.storage()
let storeRef = store.reference()
let userProfilesRef = storeRef.child("VDBackgroundFrames/")
userProfilesRef.observe(.childAdded, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
//Logic to extract urls...
}, changeHandler: (StorageReference, NSKeyValueObservedChange<Value>) -> Void)
}
Output that I am obtaining is as follows:-
URL
https://firebasestorage.googleapis.com/v0/b/celebrations-8edf8.appspot.com/o/VDBackgroundFrames%2FVDBG11.jpg?alt=media&token=ae0910d1-2139-4443-b19a-02edde2f9b17
I actually want to access all the 4 images together from folder VDBackgroundFrames & VDFrames respectively.Kindly suggest the possible way to do it.Any suggestion or guidance would be apprecialble.
Thanks in advance.
Just access the root folder instead of the child directly, that way you'll obtain all the nodes/images in that folder something like this:
func firebaseSetUp(){
let store = Storage.storage()
let storeRef = store.reference()
let userProfilesRef = storeRef.child("VDBackgroundFrames/")
userProfilesRef.observe(.childAdded, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
//Logic to extract urls...
}
}
DownloadURL takes single string at a time. In case you want to show all the files inside a folder to a tableview like me, here is the full code:
import UIKit import Firebase
My very First View Controller-
class FolderList: UIViewController {
var folderList: [StorageReference]?
lazy var storage = Storage.storage()
#IBOutlet weak var tableView : UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.storage.reference().child("TestFolder").listAll(completion: {
(result,error) in
print("result is \(result)")
self.folderList = result.items
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
} }
extension FolderList : UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return folderList?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "FolderListCell", for:
indexPath) as? FolderListCell else {return UITableViewCell()}
cell.itemName.text = folderList?[indexPath.row].name
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 64.0
} }
extension FolderList : UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
guard let downloadVC = storyBoard.instantiateViewController(withIdentifier:
"DownloadedItemView") as? DownloadedItemView else {
return
}
downloadVC.storageRef = folderList?[indexPath.row]
self.navigationController?.pushViewController(downloadVC, animated: true)
}
}
And here is you DownloadedItemView, which will open the images you selected from the list in a view:
import UIKit
import WebKit
import Firebase
class DownloadedItemView: UIViewController {
#IBOutlet weak var webView : WKWebView!
var downloadItemURL : String?
var storageRef : StorageReference?
override func viewDidLoad() {
super.viewDidLoad()
storageRef?.downloadURL(completion: {(downloadURL,error) in
print("url is \(downloadURL)")
DispatchQueue.main.async {
guard let url = downloadURL else {return}
let urlrequest = URLRequest(url: url)
self.webView.load(urlrequest)
}
})
}
}
Your each cell:
class FolderListCell: UITableViewCell {
#IBOutlet weak var itemName : UILabel!
}
I've got a couple a problem with this code. Here's my code and I don't understand why there is an error line 61 with cell.userID = self.user[indexPath.row].userID it says : Cannot assign value of type String? to type String?.Type. It's probably because in line 36 : if let uid = value["profilepicture.userID"] as? String. userID is in Firebase a child of profile picture but I don't know how to write that inside of value[]. Thanks for your answers.
// TableViewCell.swift
import UIKit
class FriendsTableViewCell: UITableViewCell {
#IBOutlet weak var userImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
var userID = String?.self
}
// ViewController.swift
import UIKit
import Firebase
class FriendsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableview: UITableView!
var user = [User]()
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func retrieveUsers() {
let ref = Database.database().reference()
ref.child("users").queryOrderedByKey().observeSingleEvent(of: .value, with: { DataSnapshot in
let users = DataSnapshot.value as! [String: AnyObject]
self.user.removeAll()
for (_, value) in users{
//let uid = Auth.auth().currentUser!.uid
if let uid = value["profilepicture.userID"] as? String{
if uid != Auth.auth().currentUser!.uid {
let userToShow = User()
if let fullName = value["username"] as? String , let imagePath = value["profilepicture.photoURL"] as? String {
userToShow.username = fullName
userToShow.imagePath = imagePath
userToShow.userID = uid
self.user.append(userToShow)
}
}
}
}
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableview.dequeueReusableCell(withIdentifier: "FriendsTableViewCell", for: indexPath) as! FriendsTableViewCell
cell.nameLabel.text = self.user[indexPath.row].username
cell.userID = self.user[indexPath.row].userID
cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return user.count ?? 0
}
}
extension UIImageView{
func downloadImage(from imgURL: String!) {
let url = URLRequest(url: URL(string: imgURL)!)
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil{
print(error)
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
task.resume()
}
}
Cannot assign value of type String? to type String?.Type.
Change
var userID = String?.self
To
var userID : String?
I'm trying to display JSON value using Struct into table view in Swift 3.0 .
I managed to display JSON value using struct into UIPickerView and it works fine. But somehow when I use the same way in table view it does not work.
Can someone help me to update my code below in order to make it work in UITableView ? Appreciate if someone can help. Thanks.
list.php output
[
{"id":"101", "name":"name1"},
{"id":"102", "name":"name2"}
]
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet var tableview: UITableView!
#IBOutlet var deptLbl: UILabel!
#IBOutlet var selectBtn: UIButton!
#IBOutlet var displayIDLbl: UILabel!
#IBOutlet var displayNameLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
deptLbl.text = "ITD"
self.tableview.isHidden = true
getList()
}
var persons = [Person]()
struct Person {
var id: String
var name: String
init?(dict: [String:Any]) {
guard let id = dict["id"] as? String, let name = dict["name"] as? String else {
return nil
}
self.id = id
self.name = name
}
}
func getList() {
var request = URLRequest(url: URL(string: "http://localhost/list.php")!)
request.httpMethod = "POST"
let postString = "dept=\(deptLbl.text!)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
return
}
if let array = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [[String:Any]] {
self.persons = array.flatMap(Person.init)
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
}
task.resume()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self .persons.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "listcell", for: indexPath)
let data = persons[indexPath.row]
cell.textLabel?.text = data.id
cell.textLabel?.text = data.name
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.displayIDLbl.text = self.persons[indexPath.row].id
self.displayNameLbl.text = self.persons[indexPath.row].name
self.tableview.isHidden = true
}
#IBAction func pressedBtn(_ sender: Any) {
self.tableview.isHidden = !self.tableview.isHidden
}
}