Swift : fatal error: unexpectedly found nil while unwrapping an Optional value - ios

I am trying to access REST web service, but its giving this - fatal error: unexpectedly found nil while unwrapping an Optional value.
Below is the code - So i have this RESTParser class -
protocol RESTParserDelegate
{
func getReceiveData(data:NSMutableData,sender:RESTParser)
}
class RESTParser: NSObject, NSURLConnectionDataDelegate
{
var receiveData: NSMutableData!
var requestConnection: NSURLConnection!
var delegate: RESTParserDelegate?
func receiveData(resData:NSMutableData){
receiveData = resData
}
func requestConnection(reqConn:NSURLConnection){
requestConnection = reqConn
}
func httpRequest(myRequest:NSMutableURLRequest){
self.requestConnection = NSURLConnection(request: myRequest, delegate: self)
}
// NSURLConnectionDataDelegate methods
func connection(connection: NSURLConnection, didReceiveData data: NSData){
self.receiveData?.appendData(data)
}
func connectionDidFinishLoading(connection: NSURLConnection){
self.delegate?.getReceiveData(receiveData, sender: self) // error # this line
self.delegate = nil
self.receiveData = nil
self.requestConnection = nil
}
func connection(connection: NSURLConnection, didFailWithError error: NSError){
NSLog("Failed with error - %# ",error.localizedDescription)
}
func parseData(data:NSMutableData){
}
}
Error is # this line - self.delegate?.getReceiveData(receiveData, sender: self) in connectionDidFinishLoading function.
And calling this from my project details view controller - but as soon as getReceiveData method is called this fatal error is coming.
class ProjectDetails_tab: UIViewController, RESTParserDelegate{
override func viewDidLoad() {
super.viewDidLoad();
//var projectData: NSDictionary = [String:String]()
var url:String = "http://domianName.com"
var nsURL = NSURL(string: url)
var createrequest: NSMutableURLRequest = NSMutableURLRequest(URL: nsURL!)
createrequest.HTTPMethod = "GET"
var rest = RESTParser()
rest.delegate=self
rest.httpRequest(createrequest)
}
func getReceiveData(data:NSMutableData,sender:RESTParser){
}
}

Not clear that fatal error with which line of your code. But you can recheck your delegate object or:
// NSURLConnectionDataDelegate methods
func connection(didReceiveResponse: NSURLConnection, didReceiveResponse response: NSURLResponse) {
// Recieved a new request, clear out the data object
self.receiveData = NSMutableData()
}
func connection(connection: NSURLConnection, didReceiveData data: NSData){
self.receiveData?.appendData(data)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
// Request complete, self.data should now hold the resulting info
}

Related

Custom NSURLProtocol with NSURLSession

I'm trying to implement this tutorial which implements a custom NSURLProtocol with NSURLConnection.
https://www.raywenderlich.com/76735/using-nsurlprotocol-swift
It works as expected, but now that NSURLConnection is deprecated in iOS9, I'm trying to convert it to NSURLSession.
Unfortunatly it didn't work.
I'm loading a website in uiwebview, if I use NSURLConnection it loads and everything work as expected, all http requests from the webview is captured, but not when using NSURLSession.
Any help is appreciated.
here is my code
import UIKit
class MyProtocol: NSURLProtocol, NSURLSessionDataDelegate, NSURLSessionTaskDelegate, NSURLSessionDelegate {
//var connection: NSURLConnection!
var mutableData: NSMutableData!
var response: NSURLResponse!
var dataSession: NSURLSessionDataTask!
override class func canInitWithRequest(request: NSURLRequest) -> Bool {
if NSURLProtocol.propertyForKey("MyURLProtocolHandledKey", inRequest: request) != nil {
return false
}
return true
}
override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest {
return request
}
override class func requestIsCacheEquivalent(aRequest: NSURLRequest,
toRequest bRequest: NSURLRequest) -> Bool {
return super.requestIsCacheEquivalent(aRequest, toRequest:bRequest)
}
override func startLoading() {
let newRequest = self.request.mutableCopy() as! NSMutableURLRequest
NSURLProtocol.setProperty(true, forKey: "MyURLProtocolHandledKey", inRequest: newRequest)
self.dataSession = NSURLSession.sharedSession().dataTaskWithRequest(newRequest)
dataSession.resume()
self.mutableData = NSMutableData()
}
override func stopLoading() {
print("Data task stop")
self.dataSession.cancel()
self.mutableData = nil
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
self.response = response
self.mutableData = NSMutableData()
print(mutableData)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.client?.URLProtocol(self, didLoadData: data)
self.mutableData.appendData(data)
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if (error == nil)
{
self.client!.URLProtocolDidFinishLoading(self)
self.saveCachedResponse()
}
else
{
self.client?.URLProtocol(self, didFailWithError: error!)
}
}
func saveCachedResponse () {
let timeStamp = NSDate()
let urlString = self.request.URL?.absoluteString
let dataString = NSString(data: self.mutableData, encoding: NSUTF8StringEncoding) as NSString?
print("TiemStamp:\(timeStamp)\nURL: \(urlString)\n\nDATA:\(dataString)\n\n")
}
}
I've solved it.
Here is the code if anyone needs it.
import Foundation
class MyProtocol1: NSURLProtocol, NSURLSessionDataDelegate, NSURLSessionTaskDelegate
{
private var dataTask:NSURLSessionDataTask?
private var urlResponse:NSURLResponse?
private var receivedData:NSMutableData?
class var CustomKey:String {
return "myCustomKey"
}
// MARK: NSURLProtocol
override class func canInitWithRequest(request: NSURLRequest) -> Bool {
if (NSURLProtocol.propertyForKey(MyProtocol1.CustomKey, inRequest: request) != nil) {
return false
}
return true
}
override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest {
return request
}
override func startLoading() {
let newRequest = self.request.mutableCopy() as! NSMutableURLRequest
NSURLProtocol.setProperty("true", forKey: MyProtocol1.CustomKey, inRequest: newRequest)
let defaultConfigObj = NSURLSessionConfiguration.defaultSessionConfiguration()
let defaultSession = NSURLSession(configuration: defaultConfigObj, delegate: self, delegateQueue: nil)
self.dataTask = defaultSession.dataTaskWithRequest(newRequest)
self.dataTask!.resume()
}
override func stopLoading() {
self.dataTask?.cancel()
self.dataTask = nil
self.receivedData = nil
self.urlResponse = nil
}
// MARK: NSURLSessionDataDelegate
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask,
didReceiveResponse response: NSURLResponse,
completionHandler: (NSURLSessionResponseDisposition) -> Void) {
self.client?.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed)
self.urlResponse = response
self.receivedData = NSMutableData()
completionHandler(.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.client?.URLProtocol(self, didLoadData: data)
self.receivedData?.appendData(data)
}
// MARK: NSURLSessionTaskDelegate
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil && error!.code != NSURLErrorCancelled {
self.client?.URLProtocol(self, didFailWithError: error!)
} else {
saveCachedResponse()
self.client?.URLProtocolDidFinishLoading(self)
}
}
// MARK: Private methods
/**
Do whatever with the data here
*/
func saveCachedResponse () {
let timeStamp = NSDate()
let urlString = self.request.URL?.absoluteString
let dataString = NSString(data: self.receivedData!, encoding: NSUTF8StringEncoding) as NSString?
print("TimeStamp:\(timeStamp)\nURL: \(urlString)\n\nDATA:\(dataString)\n\n")
}
}
Swift 3 version:
// CustomURLProtocol.swift
class CustomURLProtocol: URLProtocol, URLSessionDataDelegate, URLSessionTaskDelegate {
private var dataTask: URLSessionDataTask?
private var urlResponse: URLResponse?
private var receivedData: NSMutableData?
class var CustomHeaderSet: String {
return "CustomHeaderSet"
}
// MARK: NSURLProtocol
override class func canInit(with request: URLRequest) -> Bool {
guard let host = request.url?.host, host == "your domain.com" else {
return false
}
if (URLProtocol.property(forKey: CustomURLProtocol.CustomHeaderSet, in: request as URLRequest) != nil) {
return false
}
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
let mutableRequest = NSMutableURLRequest.init(url: self.request.url!, cachePolicy: NSURLRequest.CachePolicy.useProtocolCachePolicy, timeoutInterval: 240.0)//self.request as! NSMutableURLRequest
//Add User Agent
var userAgentValueString = "myApp"
mutableRequest.setValue(userAgentValueString, forHTTPHeaderField: "User-Agent")
print(mutableRequest.allHTTPHeaderFields ?? "")
URLProtocol.setProperty("true", forKey: CustomURLProtocol.CustomHeaderSet, in: mutableRequest)
let defaultConfigObj = URLSessionConfiguration.default
let defaultSession = URLSession(configuration: defaultConfigObj, delegate: self, delegateQueue: nil)
self.dataTask = defaultSession.dataTask(with: mutableRequest as URLRequest)
self.dataTask!.resume()
}
override func stopLoading() {
self.dataTask?.cancel()
self.dataTask = nil
self.receivedData = nil
self.urlResponse = nil
}
// MARK: NSURLSessionDataDelegate
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
self.urlResponse = response
self.receivedData = NSMutableData()
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.client?.urlProtocol(self, didLoad: data as Data)
self.receivedData?.append(data as Data)
}
// MARK: NSURLSessionTaskDelegate
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil { //&& error.code != NSURLErrorCancelled {
self.client?.urlProtocol(self, didFailWithError: error!)
} else {
//saveCachedResponse()
self.client?.urlProtocolDidFinishLoading(self)
}
}
}
The problem you are having with your code is that you are using the the NSURLSession.sharedSession to contain your data task. By using the shared session, you are not able to change the session delegate so none of your delegate routines are going to be invoked.
You will need to create a custom session with your protocol established as the delegate for the session. Then, when asked to start loading you can create a data task in that session.
From the documentation of URLSession:
Important
The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you don’t invalidate the session, your app leaks memory until it exits.
Also:
Note
Be careful to not create more sessions than you need. For example, if you have several parts of your app that need a similarly configured session, create one session and share it among them.
So I would move the creation of the URLSession from the startLoading method to the URLProtocol subclass initializer:
class MyURLProtocol: URLProtocol, URLSessionDataDelegate,URLSessionTaskDelegate {
override init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
super.init(request: request, cachedResponse: cachedResponse, client: client)
defaultSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
}
private var defaultSession: URLSession?

How to successfully handle sftp:// protocol by subclassing NSURLProtocol?

The built-in URL protocols supported by NSURLConnection can handle the schemes http, https, file, ftp, about, and data. I want to support sftp. I heard that there is a way to achieve this by subclassing NSURLProtocol. But I'm not getting how to do it. I want to download a image from the folder through sftp.
Source: https://www.raywenderlich.com/76735/using-nsurlprotocol-swift
The tutorial says by subclassing we can support custom URL. But when i ran the code the connection always fails. I thought when we try connecting to sftp , delegate method in MyURLProtocol.swift i.e didReceiveAuthenticationChallenge would get called but that doesn't happen. Instead delegate method didFailWithError gets called. I not getting why the connection is failing. Both these methods are from NSURLConnectionDelegate
I have a ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let urlString = "sftp://username#192.168.0.1:22/batman"
// Open a connection for the URL.
var url = NSURL(string: urlString)
request = NSURLRequest(URL: url!)
connection = NSURLConnection(request: request, delegate: self, startImmediately: true)//(request: request, delegate: self)
}
In My AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
NSURLProtocol.registerClass(MyURLProtocol)
return true
}
My MyURLProtocol.swift
import UIKit
import CoreData
var requestCount = 0
class MyURLProtocol: NSURLProtocol, NSURLConnectionDelegate {
var connection: NSURLConnection!
var mutableData: NSMutableData!
var response: NSURLResponse!
override class func canInitWithRequest(request: NSURLRequest) -> Bool {
print("Request #\(requestCount++): URL = \(request.URL!.absoluteString)")
if NSURLProtocol.propertyForKey("MyURLProtocolHandledKey", inRequest: request) != nil {
return false
}
return true
}
override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest {
return request
}
override class func requestIsCacheEquivalent(aRequest: NSURLRequest,
toRequest bRequest: NSURLRequest) -> Bool {
return super.requestIsCacheEquivalent(aRequest, toRequest:bRequest)
}
override func startLoading() {
// 1
let possibleCachedResponse = self.cachedResponseForCurrentRequest()
if let cachedResponse = possibleCachedResponse {
print("Serving response from cache")
// 2
let data = cachedResponse.valueForKey("data") as! NSData
let mimeType = cachedResponse.valueForKey("mimeType") as! String
let encoding = cachedResponse.valueForKey("encoding") as! String
// 3
let response = NSURLResponse(URL: self.request.URL!, MIMEType: mimeType, expectedContentLength: data.length, textEncodingName: encoding)
// 4
self.client!.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed)
self.client!.URLProtocol(self, didLoadData: data)
self.client!.URLProtocolDidFinishLoading(self)
} else {
// 5
print("Serving response from NSURLConnection")
let newRequest = self.request.mutableCopy() as! NSMutableURLRequest
NSURLProtocol.setProperty(true, forKey: "MyURLProtocolHandledKey", inRequest: newRequest)
self.connection = NSURLConnection(request: newRequest, delegate: self)
}
}
override func stopLoading() {
if self.connection != nil {
self.connection.cancel()
}
self.connection = nil
}
func connection(connection: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
self.client!.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed)
self.response = response
self.mutableData = NSMutableData()
}
func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
self.client!.URLProtocol(self, didLoadData: data)
self.mutableData.appendData(data)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
self.client!.URLProtocolDidFinishLoading(self)
self.saveCachedResponse()
}
func connection(connection: NSURLConnection, didFailWithError error: NSError) {
self.client!.URLProtocol(self, didFailWithError: error)
}
func connection(connection: NSURLConnection, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
}
func saveCachedResponse () {
print("Saving cached response")
// 1
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context = delegate.managedObjectContext
// 2
let cachedResponse = NSEntityDescription.insertNewObjectForEntityForName("CachedURLResponse", inManagedObjectContext: context) as NSManagedObject
cachedResponse.setValue(self.mutableData, forKey: "data")
cachedResponse.setValue(self.request.URL!.absoluteString, forKey: "url")
cachedResponse.setValue(NSDate(), forKey: "timestamp")
cachedResponse.setValue(self.response.MIMEType, forKey: "mimeType")
cachedResponse.setValue(self.response.textEncodingName, forKey: "encoding")
// 3
do {
try context.save()
} catch let error as NSError {
print(error)
print("Could not cache the response")
}
}
func cachedResponseForCurrentRequest() -> NSManagedObject? {
// 1
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context = delegate.managedObjectContext
// 2
let fetchRequest = NSFetchRequest()
let entity = NSEntityDescription.entityForName("CachedURLResponse", inManagedObjectContext: context)
fetchRequest.entity = entity
// 3
let predicate = NSPredicate(format:"url == %#", self.request.URL!.absoluteString)
fetchRequest.predicate = predicate
// 4
let possibleResult:Array<NSManagedObject>?
do {
possibleResult = try context.executeFetchRequest(fetchRequest) as? Array<NSManagedObject>
if let result = possibleResult {
if !result.isEmpty {
return result[0]
}
}
} catch let error as NSError {
print(error)
}
return nil
}
}
Adding support for the URL scheme itself doesn't add support for the underlying network protocol. The sftp protocol is unrelated to HTTP, and requires entirely different networking code to make the connection and download data. Right now, your custom protocol class is basically just asking the URL loading system to make a new sftp request whenever your protocol gets an sftp URL (or any other URL). This will always fail because the URL loading system doesn't know how to handle sftp requests.
To add sftp support, you would need to bring in an actual sftp library, and then use that instead of creating a new NSURLConnection in your startLoading method. You also need to check the protocol in canInitWithRequest to make sure it really is an sftp request, IIRC. Otherwise, your custom protocol subclass will end up handling all requests for all possible URL schemes.
With that said, unless there's a really good reason to handle sftp using NSURLConnection or NSURLSession, you're probably better off just handling that by using one of those sftp libraries directly, rather than trying to plumb them into the URL loading system.
For info on sftp libraries, see this question:
SFTP libraries for iPhone?

NSURLConnection Delegate Methods Not Called In Generic Class

I have a test class that tries to reach to google with a NSURLConnection. If I try to make it generic, the NSURLConnectionDataDelegate methods are never called.
class Remote<T: NSObject>: NSObject, NSURLConnectionDelegate, NSURLConnectionDataDelegate {
//class Remote: NSObject, NSURLConnectionDelegate, NSURLConnectionDataDelegate {
var data = NSMutableData()
func connect(query:NSString) {
var url = NSURL(string:"http://www.google.com")!
var request = NSURLRequest(URL: url)
var conn = NSURLConnection(request: request, delegate: self, startImmediately: true)
}
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
LF.log("didReceiveResponse")
}
func connection(connection: NSURLConnection!, didReceiveData conData: NSData!) {
LF.log("didReceiveData")
self.data.appendData(conData)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
LF.log("didFinished")
//println(self.data)
}
deinit {
println("deiniting")
}
}
To test it (comment/uncomment the first/second line to compare):
let remote = Remote<NSObject>()
//let remote = Remote()
remote.connect("")
Any idea please?
Update1: to answer comment 1, it's a REST client that handles network connection and parsing for you. I'd write a blog about this later (since it's still under development), but to give you the idea here's some demo code from my project:
let client = ICRestClient<ICCategoryModel>(api:IC.api.category_list)
client.func_array = {
(results: [ICCategoryModel]?, error: NSError?) -> Void in
block!(results, error)
}
client.execute()
And ICCategoryModel is like:
class ICSubCategoryModel: ICModel {
var name: String?
var category_id: Int = 0
}
The idea is that you pass the API URL in, you get an array (or error) with some reflected objects instead of Dictionary. It's from my LSwift library and supports various authentications methods (buildin-parameters, cookie, header, authentication challenge etc.)
One of the problem is that I can't access the delegate object of NSURLConnection. I came up with a solution by creating another RemoteDelegate class, which is not generic type, and set it as the delegate of "conn". It works for now but it's just a work-around, and I'm still looking for the answer to the question.
My delegate class:
class LRestConnectionDelegate: NSObject {
var func_done: ((NSURLResponse?, NSData!, NSError!) -> Void)?
var credential: NSURLCredential?
var response: NSURLResponse?
var data: NSMutableData = NSMutableData()
func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
if challenge.previousFailureCount > 0 {
challenge.sender.cancelAuthenticationChallenge(challenge)
} else if let credential = credential {
challenge.sender.useCredential(credential, forAuthenticationChallenge:challenge)
} else {
LF.log("REST connection will challenge", connection)
}
}
func connection(connection: NSURLConnection, didReceiveResponse a_response: NSURLResponse) {
//LF.log("CONNECTION response", response)
response = a_response
}
func connection(connection: NSURLConnection, didReceiveData data_received: NSData) {
//LF.log("CONNECTION data", data.length)
data.appendData(data_received)
}
func connectionDidFinishLoading(connection: NSURLConnection) {
//LF.log("CONNECTION finished", connection)
if func_done != nil {
func_done!(response, data, nil)
}
}
func connection(connection: NSURLConnection, didFailWithError error: NSError) {
//LF.log("CONNECTION failed", error)
if let func_done = func_done {
func_done(response, nil, error)
}
}
deinit {
//LF.log("DELEGATE deinit", self)
}
}
And this works in class LRestClient<T: LFModel>:
let delegate = LRestConnectionDelegate()
delegate.credential = credential
delegate.func_done = func_done
connection = NSURLConnection(request:request, delegate:delegate, startImmediately:true)
Because you're using NSURLConnection synchronously, you have to schedule the operation in the main run loop. Add the following code to the end of your connect function:
conn?.scheduleInRunLoop(NSRunLoop.currentRunLoop, forMode: NSDefaultRunLoopMode)
Alternatively, set startImmediately to NO and call conn?.start().

Swift access REST Web Service failed

Following this tutorial tutorial
I'm able to invoke my web service which released base on springMVC, however the connectionDidFinishLoading does not seem to fire, and there isn't any error message either.
Here is my code.
import Foundation
class Requestor {
var data: NSMutableData = NSMutableData();
func connect() {
var urlParth = "http://localhost:8080/webservice/request/public/key";
var url = NSURL(string: urlParth);
var request:NSURLRequest = NSURLRequest(URL: url!);
var connection = NSURLConnection(request: request, delegate: self, startImmediately: false);
connection?.start();
}
//NSURLConnection Connection failed
func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
println("Failed with error:\(error.localizedDescription)")
}
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
println("didReceiveResponse")
}
func connection(connection: NSURLConnection!, didReceiveData conData: NSData!) {
self.data.appendData(conData)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
println(self.data)
println("did");
}
}
here is my service code:
#RestController
#RequestMapping("/webservice")
public class PublicService extends Service {
#RequestMapping("/request/public/key")
public String requestPublicKey() {
String result = String.format("{\"code\":\"%s\",\"publicKey\":\"%s\"}", "200", Base64.encodeBase64String(rasFactory.getPublicKeyInByte()));
return result;
}
}
I debugged for a quite long time by putting a break point in service, seems I can reach service, but after service returns the result, iOS side doesn't have any reaction.
Is there anything wrong with my code?
Thanks.

Pull to Refresh in Swift not Reloading UITableView

I've got JSON filling my UITableView successfully, but the JSON is often updated so I need the ability to refresh. I followed THIS TUTORIAL to implement a pull to refresh control. Visually, it seems like it all works correctly, but when I call tableView.reloadData() the table doesn't reload. However, if I leave the ViewController and return, the table is updated. Why would tableView.reloadData() work in viewDidAppear and viewWillAppear but not in my custom refresh() function?
MainVC.swift file
class MainVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var dataArray: NSArray = NSArray()
#IBOutlet var Controller: UISegmentedControl!
var refreshControl:UIRefreshControl!
func refresh(sender:AnyObject)
{
refreshBegin("Refresh",
refreshEnd: {(x:Int) -> () in
self.tableView .reloadData()
println("Table Reloaded")
self.refreshControl.endRefreshing()
})
}
func refreshBegin(newtext:String, refreshEnd:(Int) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
println("refreshing")
sleep(2)
dispatch_async(dispatch_get_main_queue()) {
refreshEnd(0)
}
}
}
override func viewWillAppear(animated: Bool) {
self.tableView .reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.titleView = UIImageView(image: UIImage(named: "logojpg.jpg"))
startConnectionAt("http://www.domain.com/json.php")
refreshControl = UIRefreshControl()
refreshControl.backgroundColor = UIColor.orangeColor()
refreshControl.tintColor = UIColor.whiteColor()
refreshControl.attributedTitle = NSAttributedString(string: "Pull to Refresh")
refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl)
}
//MARK: JSON Loading
var data: NSMutableData = NSMutableData()
func startConnectionAt(urlPath: String){
var url: NSURL = NSURL(string: urlPath)
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)
connection.start()
}
func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
println("Connection failed.\(error.localizedDescription)")
}
func connection(connection: NSURLConnection, didRecieveResponse response: NSURLResponse) {
println("Recieved response")
}
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
self.data = NSMutableData()
}
func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
self.data.appendData(data)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
var dataAsString: NSString = NSString(data: self.data, encoding: NSUTF8StringEncoding)
var err: NSError
var json: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var results: NSArray = json["needs"] as NSArray
self.dataArray = results
tableView.reloadData()
println("success")
}
//End loading of JSON
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return self.dataArray.count;
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell:CustomCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as CustomCell
var rowData: NSDictionary = dataArray[indexPath.row] as NSDictionary
var firstName=rowData["needFirstname"] as String
var descrip=rowData["needDescription"] as String
var poster=rowData["needPoster"] as String
var city=rowData["needCity"] as String
var state=rowData["needState"] as String
var country=rowData["needCountry"] as String
cell.needFirstName.text = firstName
cell.needDescription.text = descrip
cell.needDescription.numberOfLines = 0
cell.needPoster.text = poster
cell.needCity.text = city
cell.needState.text = state
cell.needCountry.text = country
return cell
}
#IBAction func Change(sender: AnyObject) {
if Controller.selectedSegmentIndex == 0 {
startConnectionAt("http://www.domain.com/localJSON.php")
}
else if Controller.selectedSegmentIndex == 1 {
startConnectionAt("http://www.domain.com/intlJSON.php")
}
self.tableView .reloadData()
}
}
Your last comment is right-on in my view.
During your pull to refresh function, you call tableView.reloadData(), however, reloadData() does not inherently do any repopulating the elements in the data source (in your case, dataArray). It simply reloads all the data that's currently in the table view's data source at the time it is called.
So my recommendation would be to construct your refresh function such that the following happens:
Initiate a request to your web service.
When the response comes back (ie, connectionDidFinishLoading is executed), parse the JSON results and assign that result to the dataArray instance. You seem to be doing this already in connectionDidFinishLoading, so it's just a matter of sending the request to your web service, I'd think.
Call tableView.reloadData() to display any new elements that have been added since the last time the tableView's data was displayed. Again, you're doing this already in connectionDidFinishLoading, so #1 is the primary thing that I think needs to happen.
Referring to https://stackoverflow.com/a/25957339
Not sure but maybe the connection is run on a different thread, if so you need to run the table update on the main UI thread
// using Swift's trailing closure syntax:
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}

Resources