Forum Archive

Use objc_util and NSURLConnection to make a GET request

tlinnet

Hi.

Since pythonista does not handle ipv6, I would like to make a method for objc_util, that can be a replacement for python request.

A objc_util replacement for a call like this:

import requests

root='http://validate.jsontest.com'
url=root+''
params = {'json':str({"key":"value"})}
r = requests.get(url, params=params)
print r.json()
tlinnet

I am here at the moment

from objc_util import *

class Web(object):
    def __init__(self, root=None, method=None, headers=None):
        #NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
        self.request = ObjCClass('NSMutableURLRequest').alloc().initWithURL_(nsurl(root))
        #[request setHTTPMethod:@"POST"];
        self.request.setHTTPMethod_(method)

        # Make headers
        for key in headers:
            #[request setValue:@"es" forHTTPHeaderField:@"Accept-Language"];
            self.request.setValue_forHTTPHeaderField_(key, headers[key])

        # Make request
        #NSURLConnection * theConnection = [[NSURLConnection alloc] initWithRequest:imageRequest delegate:self];
        #self.conn = ObjCClass('NSURLConnection').alloc().initWithRequest_delegate_(self.request, self)
        self.conn = ObjCClass('NSURLConnection').alloc().initWithRequest_delegate_startImmediately_(self.request, self, True)

        #[connection autorelease];
        self.conn.autorelease()

But I dont know how to unpack or return the request?
So I get the response from the server?

lukaskollmer

NSURLConnection was deprecated with iOS 9. However, you can use its replacement, NSURLSession.

This is a very basic example of how to use NSURLSession with parameters and how to get the response.
You can pass your parameters as a dictionary as second argument to the validate method.
I think @omz advised against using objc_util.retain_global, but it's necessary in this example to prevent the block from getting released before its getting called by the URL session.

In a proper implementation, you probably want to create a Python class so that you can store the completion handler.

#!/usr/bin/env python3
import objc_util
import urllib.parse
from ctypes import c_void_p

NSURLRequest = objc_util.ObjCClass("NSURLRequest")
NSURLSession = objc_util.ObjCClass("NSURLSession")
NSURLSessionConfiguration = objc_util.ObjCClass("NSURLSessionConfiguration")


def validate(url, params, responseHandler):
    if params:
        params_encoded = urllib.parse.urlencode(params)
    else:
        params_encoded = ""
    url = objc_util.nsurl("{}?{}".format(url, params_encoded))
    request = NSURLRequest.request(URL=url)
    configuration = NSURLSessionConfiguration.defaultSessionConfiguration()

    session = NSURLSession.session(Configuration=configuration)

    completionHandler = objc_util.ObjCBlock(responseHandler, restype=None, argtypes=[c_void_p, c_void_p, c_void_p, c_void_p])
    objc_util.retain_global(completionHandler)

    dataTask = session.dataTask(Request=request, completionHandler=completionHandler)
    dataTask.resume()

def responseHandlerBlock(_cmd, data, response, error):
    if error is not None:
        error = objc_util.ObjCInstance(error)
        print(error)
        return
    response = objc_util.ObjCInstance(response)
    data = objc_util.ObjCInstance(data)
    print(str(objc_util.nsdata_to_bytes(data)))

url = "http://validate.jsontest.com"
params = {"json" : {"first" : "lukas", "last" : "kollmer"}}

validate(url, None, responseHandlerBlock)
tlinnet

WOW!!!!

Thanks! :)
This works as expected. :)

I get a json back, I can work with.
I will try to implement this, and see if it passes the Apple review.

Thanks!

import objc_util
from urlparse import urlparse
from urllib import urlencode
from ctypes import c_void_p

NSURLRequest = objc_util.ObjCClass("NSURLRequest")
NSURLSession = objc_util.ObjCClass("NSURLSession")
NSURLSessionConfiguration = objc_util.ObjCClass("NSURLSessionConfiguration")

def validate(url, params, responseHandler):
    if params:
        params_encoded = urlencode(params)
    else:
        params_encoded = ""
    url = objc_util.nsurl("{}?{}".format(url, params_encoded))
    request = NSURLRequest.request(URL=url)
    configuration = NSURLSessionConfiguration.defaultSessionConfiguration()

    session = NSURLSession.session(Configuration=configuration)

    completionHandler = objc_util.ObjCBlock(responseHandler, restype=None, argtypes=[c_void_p, c_void_p, c_void_p, c_void_p])
    objc_util.retain_global(completionHandler)

    dataTask = session.dataTask(Request=request, completionHandler=completionHandler)
    dataTask.resume()

def responseHandlerBlock(_cmd, data, response, error):
    if error is not None:
        error = objc_util.ObjCInstance(error)
        print(error)
        return
    response = objc_util.ObjCInstance(response)
    data = objc_util.ObjCInstance(data)
    print(str(objc_util.nsdata_to_bytes(data)))

url = "http://validate.jsontest.com"
params = {"json" : {"first" : "lukas", "last" : "kollmer"}}

#validate(url, None, responseHandlerBlock)
validate(url, params, responseHandlerBlock)
tlinnet

Now getting a return.

Don't know how to exactly to implement a "wait", but here is a try

```
import objc_util
from urlparse import urlparse
from urllib import urlencode
from ctypes import c_void_p
import time

class Web(object):
def init(self, url=None, params=None):
self.data = None

    if params:
        params_encoded = urlencode(params)
    else:
        params_encoded = ""
    url = objc_util.nsurl("{}?{}".format(url, params_encoded))
    request = objc_util.ObjCClass("NSURLRequest").request(URL=url)
    configuration = objc_util.ObjCClass("NSURLSessionConfiguration").defaultSessionConfiguration()

    session = objc_util.ObjCClass("NSURLSession").session(Configuration=configuration)

    completionHandler = objc_util.ObjCBlock(self.responseHandlerBlock, restype=None, argtypes=[c_void_p, c_void_p, c_void_p, c_void_p])
    objc_util.retain_global(completionHandler)

    dataTask = session.dataTask(Request=request, completionHandler=completionHandler)
    dataTask.resume()

def responseHandlerBlock(self, _cmd, data, response, error):
    if error is not None:
        error = objc_util.ObjCInstance(error)
        print(error)
        return
    response = objc_util.ObjCInstance(response)
    data = objc_util.ObjCInstance(data)
    self.data = (str(objc_util.nsdata_to_bytes(data)))

def return_data(self):
    return self.data

url = "http://validate.jsontest.com"
params = {"json" : {"first" : "lukas", "last" : "kollmer"}}

validate(url, None, responseHandlerBlock)

validate(url, params, responseHandlerBlock)

call = Web(url, params)

wait = True
while wait:
data = call.return_data()
if data != None:
print data
wait = False
print "Done"```

tlinnet

I got it to work with, headers, ios basic authentication and parameters, and saving the response.

Thanks!

https://gist.github.com/tlinnet/31b35136e206ea2b4829a13799c89316

tlinnet

And now the last modification..

Making exceptions work as well.

https://gist.github.com/tlinnet/aff5decf31d07d0c0cccfad7961353f7

JonB

If your app is doing a lot of this, be careful that there may be a memory leak. you may need to use someobject ._cached_methods.clear() for every ObjCInstance that you create inside the callback, which breaks a reference cycle (well, at least untangles it a bit so gc can handle it)