This is my class:
import UIKit
import SwiftyJSON
class ListOfEvents: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableView:UITableView?
var items = NSMutableArray()
override func viewDidLoad() {
let frame:CGRect = CGRect(x: 0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100)
self.tableView = UITableView(frame: frame)
self.tableView?.dataSource = self
self.tableView?.delegate = self
self.view.addSubview(self.tableView!)
print("aaa")
getAllRequests()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
tableView.registerClass(SingleEventCell.self, forCellReuseIdentifier: "cell")
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! SingleEventCell
let user:JSON = JSON(self.items[indexPath.row])
print(user["username"].description)
// cell.username.text = user["username"].description
// cell.descr.text = "ggg"
print(cell)
return cell
}
func getAllRequests() {
print("getAllRequests")
RestApiManager.sharedInstance.getRequests { json in
let results = json//["username"]
for (index: String, subJson: JSON) in results {
let user: AnyObject = JSON.object
self.items.addObject(user)
dispatch_async(dispatch_get_main_queue(),{
self.tableView?.reloadData()
})
}
}
}
}
I try to fill my custom UITableViewCell
s with data fetched from webservice. My problem is that:
print(user["username"].description) //that prints perfectly
but if I uncomment one of the lines below:
// cell.username.text = user["username"].description
// cell.descr.text = "ggg"
I'm getting the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I suspect that I try to refer to the cells before they are initialized/visible for the algorithm, but if so - how can I prevent it? I'm using viewDidLoad()
method but that doesn't change anything - the error is still the same.
How can I fix it?
====EDIT
@vadian suggested that it's a different table view in storyboard and in the code.I went with that advice and created an outlet for the tableview from storyboard and in all places in code changed the reference from tableview
to newly created outlet:
import UIKit import SwiftyJSON
class ListOfEvents: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tview: UITableView!
var tableView:UITableView?
var items = NSMutableArray()
override func viewDidLoad() {
let frame:CGRect = CGRect(x: 0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100)
tview = UITableView(frame: frame)
tview.dataSource = self
tview.delegate = self
self.view.addSubview(tview!)
print("aaa")
getAllRequests()
}
func tableView(tview: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tview: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
tview.registerClass(SingleEventCell.self, forCellReuseIdentifier: "cell")
let cell = tview.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! SingleEventCell
let user:JSON = JSON(self.items[indexPath.row])
print(user["username"].description)
// cell.username.text = user["username"].description
cell.descr.text = "ggg"
print(cell)
return cell
}
func getAllRequests() {
print("getAllRequests")
RestApiManager.sharedInstance.getRequests { json in
let results = json
for (index: String, subJson: JSON) in results {
let user: AnyObject = JSON.object
self.items.addObject(user)
dispatch_async(dispatch_get_main_queue(),{
self.tview.reloadData()
})
}
}
}
}
But now when I run it I'm getting error;
fatal error: unexpectedly found nil while unwrapping an Optional value
here:
tview.dataSource = self
Is there a way to fix it?
One obvious problem is this line:
tview = UITableView(frame: frame)
That is not how you create a table view in code. You must call its designated initializer , which is initWithFrame:style:
. For example:
tview = UITableView(frame: frame, style: .Plain)
Note, however, that your code has various other problems that could also be an issue here. For example, this line:
let frame:CGRect = CGRect(x: 0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100)
That line depends on self.view
having a frame, and a frame of sufficient size (ie more than 100 pixels in height). But you don't know this, because in viewDidLoad
, self.view
merely exists — it has not yet achieved its final frame because it has not yet been put into the interface and laid out. So that line is very risky at that point.
I'm guessing that you probably don't have your username
and/or descr
outlets set up correctly inside your SingleEventCell
.
Take a look here to know how to connect your outlets correctly.
Your problem is, that your JSON object is not yet fetched at the time tableView.cellForRowAtIndexPath
wants to load it, therefor giving you an unexpectedly found nil error.
What You can do is this:
Create a new Delegate protocol:
protocol NetworkingDelegate {
func didFinishLoadingData()
}
Create a class Networking
where you put your API request in:
class Networking {
var networkingDelegate: NetworkingDelegate?
// (I am using Alamofire here so the function itself will differ a little.)
func getAllRequests() {
Alamofire.request(.GET, "YOUR_API_ENDPOINT")
.responseJSON { response in
if response.result.isSuccess {
let jsonObject = JSON(response.result.value!)
User.populateUserDataSourceFromJSON(jsonObject)
self.dispatchAndRefresh()
}
}
}
}
func dispatchAndRefresh() {
dispatch_async(dispatch_get_main_queue()) {
if let delegate = self.networkingDelegate {
delegate.didFinishLoadingData()
}
}
}
In your Modelclass have a function called populateUserDataSourceFromJSON
class User {
let name = String()
let age = Int()
//...
init() {
}
class func populateUserDataSourceFromJSON(json: JSON) {
userDataSource = [User]()
for (_, user): (String, JSON) in json {
let currentUser = User()
currentUser.name = user["name"].stringValue
currentUser.age = user["age"].intValue
//...
userDataSource.append(currentUser)
}
}
In your ViewController add the Delegate we created:
class ListOfEvents: UIViewController, UITableViewDataSource, UITableViewDelegate, NetworkingDelegate {
//...
create a reference of your Networking class:
let networking = Networking()
and set it's delegate to self:
override func viewDidLoad() {
super.viewDidLoad()
networking.networkingDelegate = self
//...
Only thing left to do is to make your ViewController conform to the Delegate by implementing didFinishloadingData()
func didFinishLoadingData() {
tableView.reloadData()
}
I hope this helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.