Swift access REST Web Service failed - ios

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.

Related

Swift - How to properly use delegate

I am using Salesforce ios sdk for my mobile application and there is a method to execute SOQL queries. For eg.,
class SfdcController: NSObject, SFRestDelegate {
let request = SFRestAPI.sharedInstance().request(forQuery:"SELECT Name FROM User LIMIT 10");
SFRestAPI.sharedInstance().send(request, delegate: self);
func request(_ request: SFRestRequest, didLoadResponse jsonResponse: Any)
{
print(jsonResponse)
}
func request(_ request: SFRestRequest, didFailLoadWithError error: Error)
{
self.log(.debug, msg: "didFailLoadWithError: \(error)")
}
}
Question
I'll execute multiple SOQL queries with in the same class and there are different methods to handle the response. Now,
Is there a way to get the reference of the request inside the didLoadResponse? So, I can write switch statement to execute different functions.
If cannot reuse the delegate, do I need to create multiple delegate class to handle each and every response?
What is the better way to do it?
Update
request
SFRestRequest 0x1700cb130
endpoint: /services/data
method: GET
path: /v39.0/sobjects/Event/describe
queryParams: []
Other way to delegate is to create a separate class and define it.
SFRestAPI.sharedInstance().send(objectDescribe, delegate: testing());
class testing: NSObject, SFRestDelegate {
func request(_ request: SFRestRequest, didLoadResponse jsonResponse: Any) {
print(jsonResponse)
}
}
But the problem with the above, I have to create a class every time when I want to execute a SOQL query.
Thanks
I have an idea for you. Create a separate delegate class and in that class put an identifier. Then let that separate class delegate back to your view controller.
// Create a protocol for it
class SFHandlerDelegate {
func request(_ request: SFRestRequest, handlerId: Int, didLoadResponse jsonResponse: Any)
func request(_ request: SFRestRequest, handlerId: Int, didFailLoadWithError error: Error)
}
Make a new delegate class
// this becomes the SF delegate
class SFHandler : SFRestDelegate {
var id: Int = 0
delegate: SFHandlerDelegate
init(id: Int, delegate: SFHandlerDelegate) {
self.id = id
self.delegate = delegate
}
func request(_ request: SFRestRequest, didLoadResponse jsonResponse: Any)
{
delegate.request(request, handlerId: id, didLoadResponse: jsonResponse)
}
func request(_ request: SFRestRequest, didFailLoadWithError error: Error)
{
delegate.request(request, handlerId: id, didFailLoadWithError: error)
}
}
Change the controller
class SfdcController: NSObject, SFHandlerDelegate {
let request = SFRestAPI.sharedInstance().request(forQuery:"SELECT Name FROM User LIMIT 10");
let delegate = SFHandlerDelegate(self, 123) // use some handler id
SFRestAPI.sharedInstance().send(request, delegate: delegate);
func request(_ request: SFRestRequest, handlerId: Int, didLoadResponse jsonResponse: Any)
{
switch handlerId {
case 123:
doSomething()
// whatever
}
print(jsonResponse)
}
func request(_ request: SFRestRequest, handlerId: Int, didFailLoadWithError error: Error)
{
switch handlerId {
case 123:
doSomething()
// whatever
}
self.log(.debug, msg: "didFailLoadWithError: \(error)")
}
}

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 leak in Swift

I've found several other articles about memory issues with NSURLConnection however none of them related to projects using ARC. My problem is that I make NSURLConnections every 0.5s (yes I know it would be better with sockets, but I don't have control over the API). When I open the debug inspector of xCode I could clearly see my used memory rising:
When I profile my application with instruments however - no leaks, no persistent storage across mark generations, simply nothing:
I've purposely decoupled all my other logic and created simple application to show my problem.
This is the main(and only) ViewController
import UIKit
class ViewController: UIViewController {
private var timer: NSTimer!
private var requests = [ServerRequest]()
override func viewDidAppear(animated: Bool)
{
self.timer = NSTimer(timeInterval: 0.5, target: self, selector: "refresh", userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(self.timer, forMode: NSRunLoopCommonModes)
}
#objc private func refresh()
{
let urlRequest = NSURLRequest(URL: NSURL(string: "https://api.stackexchange.com/2.2/questions?order=desc&sort=activity&site=stackoverflow")!)
let serverRequest = ServerRequest(request: urlRequest){
[unowned self] result, error, request in
println("yaba daba duu")
self.requests.removeAll(keepCapacity: false)
}
requests.append(serverRequest)
serverRequest.send()
}
}
And here's my async connection delegate:
import Foundation
class ServerRequest: NSObject
{
private var request: NSURLRequest!
typealias completionType = (NSData?, NSError?, ServerRequest) -> ()
private var completion: completionType!
private var receivedData: NSMutableData?
private var connection: NSURLConnection?
init(request: NSURLRequest, completion: (NSData?, NSError?, ServerRequest) -> ())
{
super.init()
self.request = request
self.completion = completion
self.connection = NSURLConnection(request: self.request, delegate: self, startImmediately:false)
//this line will help connection is fired even while tere are touch events
self.connection?.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
NSURLCache.sharedURLCache().removeAllCachedResponses()
}
func send()
{
receivedData = NSMutableData()
self.connection?.start()
}
func abort()
{
self.connection?.cancel()
}
}
extension ServerRequest: NSURLConnectionDataDelegate, NSURLConnectionDelegate
{
func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse)
{
self.receivedData?.length = 0
}
func connection(connection: NSURLConnection, didReceiveData data: NSData)
{
self.receivedData?.appendData(data)
}
func connection(connection: NSURLConnection, didFailWithError error: NSError)
{
self.connection = nil
self.completion(nil, error, self)
}
func connectionDidFinishLoading(connection: NSURLConnection)
{
self.connection = nil
self.completion(receivedData, nil, self)
}
//MARK: https specific(canAuthenticateAgainstProtectionSpace is depraceted first in iOS 8)
func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool
{
return protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
}
func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge)
{
challenge.sender.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust), forAuthenticationChallenge: challenge)
challenge.sender.continueWithoutCredentialForAuthenticationChallenge(challenge)
}
func connection(connection: NSURLConnection, willCacheResponse cachedResponse: NSCachedURLResponse) -> NSCachedURLResponse? {
return nil
}
}
Please note that I need to connect to https server, so I need corresponding delegate methods. Also I've implemented cache methods because of some suggestions in SO that caching responses in NSURLConnection may cause my issues. Finally request is scheduled in "NSRunLoopCommonModes" mode because I need it alive while scrolling(although removing this line doesn't affect the issue anyway).
UPDATE
I've jut tried even without custom class with delegate and the result is pretty much the same. Just copy-paste following:
import UIKit
class ViewController: UIViewController {
private var timer: NSTimer!
override func viewDidAppear(animated: Bool)
{
self.timer = NSTimer(timeInterval: 0.5, target: self, selector: "refresh", userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(self.timer, forMode: NSRunLoopCommonModes)
}
#objc private func refresh()
{
let urlRequest = NSURLRequest(URL: NSURL(string: "https://api.stackexchange.com/2.2/questions?order=desc&sort=activity&site=stackoverflow")!)
NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue())
{
response, data, error in
println("yaba daba duu")
}
}
}
And my memory pressure is again rising:

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

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
}

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().

Resources