Swift - How to properly use delegate - ios

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)")
}
}

Related

Why delegate object is nil?

I am facing little weird issue with delegate pattern and I couldn't make it work like I expect. I guess I am missing something here but I couldn't figure it out myself.
Here's how my classes are defined,
class NetworkManager {
weak var delegate: DownloaderProtocol?
func downloadFile(downloadUrl: URL ) {
downloadTask(with: downloadUrl).resume()
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
delegate?.downloadCompleted(task: task, error: error)
}
}
class DownloadManager: DownloaderProtocol {
var networkManager: NetworkManager
init(networkManager: NetworkManager) {
self.networkManager = networkManager
}
func downloadMatches(urls: [String]) {
self.networkManager.delegate = self
for(....) {
self.networkManager.downloadFile(url: url)
}
}
func downloadCompleted(task: URLSessionTask, error: Error) {
// Implementation
}
}
class Matches {
var networkManager: NetworkManager
init() {
self.networkManager = NetworkManager()
}
func getMatchSchedules(urls: [String] , completionHandler: #escaping (Result<Data, Error>) -> Void) {
return DownloadManager.downloadMatches(urls: [String])
}
}
What is the issue ?
When the urlSession - didCompleteWithError method is called, I can see that delegate object is nil.
What I have tried ?
If I remove the weak reference of the delegate i.e weak var delegate: DownloaderProtocol? to var delegate: DownloaderProtocol?, then the delegate object is not nil. It works fine.
I even tried to set the delegates in Matches class, but the delegate is still shown as nil
Any help or point out is welcome.
well I recommend this approach since is not all you code that posted in the question but just the relevant one.
class NetworkManager {
weak var delegate: DownloaderProtocol?
func downloadFile(downloadUrl: URL ) {
downloadTask(with: downloadUrl).resume()
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
delegate?.downloadCompleted(task: task, error: error)
}
}
class DownloadManager: DownloaderProtocol {
var networkManager: NetworkManager
init(networkManager: NetworkManager) {
self.networkManager = networkManager
self.networkManager.delegate = self
}
func downloadMatches(urls: [String]) {
for url in urls {
guard let url = URL(string: url) else {return}
self.networkManager.downloadFile(downloadUrl: url)
}
}
func downloadCompleted(task: URLSessionTask, error: Error) {
// Implementation
}
}
class Matches {
var downloadManager: DownloadManager//NetworkManager
init() {
//self.networkManager = NetworkManager()
//self.networkManager.delegate = DownloadManager()
let networkManager = NetworkManager()
self.downloadManager = DownloadManager(networkManager: networkManager)
}
func getMatchSchedules(urls: [String] , completionHandler: #escaping (Result<Data, Error>) -> Void) {
return downloadManager.downloadMatches(urls: ["asadsdad"])
}
}
you need a download manager in matches that have a network manager when you create matches you create the network manger and set to the init of the download manager so you have a delegate set.
You can use callback in place of delegate.
It's much easier approach than delegate.
typealias APIServiceSuccessCallback = ((Any?) -> ())
ViewController A:
//call from A -> B result fire if completionHandler fire in VC B
addCompanyTargetsController.responseCreateBookingObj = { [unowned self] (returnObject) in
if let object = returnObject as? [String:Any] {
if object["success"] as? Bool == true {
self.loadTargetList()//success block
}
}
}
ViewController B:
var responseCreateBookingObj : APIServiceSuccessCallback? //declaration
func completionHandler() { //from where you fire call this method
guard let callBack = self.responseCreateBookingObj else{
return
}
callBack( true as AnyObject)
}

SKProductsRequest won't call my callback in Swift 2

I follow different tutorials to retrieve products informations in a iOS app in Swift 2. But I can't make it works :-/
I make the following code as simple a possible, I just call the requestProductsSimple method, and I'm expecting productRequest() to be call. I have 0 error and 0 warning before running.
But output only show:
request started
ended
And I got a EXEC_BAD_ACCESS after few seconds. Here is the code. I wish someone can help.
Thanks :)
import UIKit
import StoreKit
class ViewController: UIViewController {
#IBOutlet var mainView : UIView?
override func loadView() {
super.loadView()
}
override func viewDidLoad() {
super.viewDidLoad()
let shop = IAPHelper()
shop.requestProductsSimple()
print("ended")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
class IAPHelper: NSObject, SKProductsRequestDelegate {
override init() {
}
func requestProductsSimple() {
let productIdentifiers: Set<String> = ["my_product_id"]
let productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
productsRequest.delegate = self
productsRequest.start()
print("request started")
}
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
print("received")
}
func request(request: SKRequest, didFailWithError error: NSError) {
print("request failed")
}
}
Since your shop is a local variable, it will be released once viewDidLoad returns. This is before the asynchronous operation has completed and you then get a segmentation fault when it tries to call the now released completion method.
Move your shop to a property so that a strong reference is held after viewDidLoad returns

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

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.

Resources