I am trying to upload an object to my WebAPI using the NSURLSession to perform this in the background. My Web API is operational - my problem is that the client code gets to the CreateUploadTask() method and then just throws an exception. It is a null exception and there is nothing in the Output window either. So there is literally nothing to indicate what the problem is. The code I use to initiate is here;
private NSUrlSessionConfiguration configuration = NSUrlSessionConfiguration.BackgroundSessionConfiguration ("com.SimpleBackgroundTransfer.BackgroundSession");
private NSUrlSession session;
public void SendBackgroundMessage(AppMessage m)
{
session = NSUrlSession.FromConfiguration(configuration, new UploadDelegate(), new NSOperationQueue());
NSUrl uploadHandleUrl = NSUrl.FromString(Constants.ApiUrl + "api/AppMessage/Send");
NSMutableUrlRequest request = new NSMutableUrlRequest(uploadHandleUrl);
request.HttpMethod = "POST";
request["Content-Type"] = "application/json";
var keys = new object[] { "Authorization" };
var objects = new object[] { _accessToken };
var dictionary = NSDictionary.FromObjectsAndKeys(objects, keys);
request.Headers = dictionary;
string json = JsonConvert.SerializeObject(m);
var body = NSData.FromString(json);
var uploadTask = session.CreateUploadTask(request, body);
uploadTask.Resume();
}
I suspect it is something to do with the way I am serializing to json and creating the NSData object. Any pointers on this would be greatly appriciated!
EDIT: Ok so if i remove the body parameter the upload task creates fine. It is something to do with the way I am composing the json.
This was resolved by saving the json string to disk first. Then using it in the upload task, like this;
string json = JsonConvert.SerializeObject(m);
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, m.AppMessageId.ToString());
File.WriteAllText(filename, json);
var uploadTask = session.CreateUploadTask(request, NSUrl.FromFilename(filename));
You can configure XS debugger to stop on all exceptions. Quote from forums:
Use the "Run -> New Breakpoint" menu, or the "New Breakpoint" button
in the Breakpoint Pad, and set the new breakpoint to break "When an
exception ins thrown".
Once it hit that exception-breakpoint the NullReferenceException, like most .NET exceptions, should give you extra details including the stack trace which should include the line number where it occurred.
Related
I have built an app that includes a WKWebView, and the website that the web view loads supports multiple languages. How can I change the Accept-Language header in a WKWebView, or other HTTP headers for that matter?
I've got it working in a way, but only get requests will have the custom header. As jbelkins answered in the linked so from Gabriel Cartiers comment to your question, you will have to manipulate the request and load it anew.
I've got it working for GET-Requests like this:
(it's in xamarin 0> c#, but i think you will get the idea)
i've created a private field
private bool _headerIsSet
which i check every time a request is made in the deligate method:
[Foundation.Export("webView:decidePolicyForNavigationAction:decisionHandler:")]
public void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler)
{
var request = navigationAction.Request;
// check if the header is set and if not, create a muteable copy of the original request
if (!_headerIsSet && request is NSMuteableUrlRequest muteableRequest);
{
// define your custom header name and value
var keys = new object[] {headerKeyString};
var values = new object[] {headerValueString};
var headerDict = NSDictionary.FromObjectsAndKeys(values, keys);
// set the headers of the new request to the created dict
muteableRequest.Headers = headerDict;
_headerIsSet = true;
// attempt to load the newly created request
webView.LoadRequest(muteableRequest);
// abort the old one
decisionHandler(WKNavigationActionPolicy.Cancel);
// exit this whole method
return;
}
else
{
_headerIsSet = false;
decisionHandler(WKNavigationActionPolicy.Allow);
}
}
As i said, this only works for GET-Requests. Somehow, POST-Requests don't contain the body data of the original request (request.Body and request.BodyStream are null), so the muteableRequest (which is a muteable copy of the original request) won't contain the body data of the original request.
I hope this will help you or others that approach the same problem.
Edit: For your needs, set "Accept-Language" as the key
Simply can set needed language ISO 639-1 code in URL request like below, so that we can get user preferred or locale language response from server side.
Swift 4 & above
var request = URLRequest(url: URL(string: "YourUrlStr"))
request.setValue("en", forHTTPHeaderField: "Accept-Language")
wkWebView.load(request)
Objective-C
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:YourUrlStr]];
[request setValue:#"en" forHTTPHeaderField:#"Accept-Language"];
[wkWebView loadRequest:urlRequest];
WKWebView supports localization out of the box. You will not be required set the 'Accept-Language' header field.
For some reason if you are required to, this is how it can be done.
Create a 'URLRequest' an instance of URL initialized with the desired website
var request = URLRequest(url: url)
Maintain a mapping of locales required and set the 'Accept-Language' header field accordingly
request.setValue("de-de", forHTTPHeaderField: "Accept-Language")
Load the 'URLRequest' using an instance of 'WKWebView'
webview.load(request)
Similarly any header field can be changed
I have an Angular web build inside an iOS app and want to POST requests up to the native layer with some JSON that I can use to build some native functionality. I am using the old UIWebView (because Angular) so am using an NSURLProtocol to intercept the request. This works and I can break at the point that the request comes in. The problem is that I can not see the JSON in the data property at this point because it is not the response. The request is still in the config object but I have no idea how to grab this.
My angular code for creating the post is currently like this:
var newdata = $.param({
json: JSON.stringify({
name: "Lee"
})
});
$http.post(url, newdata)
and in my NSURLProtocol class I am successfully intercepting this POST in this method but the HTTPBody property is nil:
override class func canInitWithRequest(request:NSURLRequest) -> Bool {
if (request.URL!.absoluteString as NSString).containsString("request_media_gallery") {
if(request.HTTPBody != nil){
let data:NSData = request.HTTPBody!
print(data)
}
return true
}
return request.URL?.host == "file"
}
If I debug this in chrome I get a 405 because of CORS but I can see that my request object does not have any data but does have a config object. Here's the console log from Chrome:
By the time a URL request gets down to the protocol layer, IIRC, the URL Loading System sanitizes it in a lot of ways. In particular, if a request has an HTTPBody object associated with it, it basically does this:
req.HTTPBodyStream = [NSInputStream inputStreamWithData:req.HTTPBody];
req.HTTPBody = nil;
As a result, to get the data, you need to read from the HTTPBodyStream, regardless of whether the request was originally created with an NSData object or a body stream.
I've created a script/api which is suppose to add a record to my database when running a specific url. However i'm not sure how to run this url. I do not expect anything back just to run this url? how can i do this?
var identifier = UIDevice.currentDevice().identifierForVendor.UUIDString
var addViewUrl = "http://url/addview.php?type=ios&identifier=\(identifier)&newsid=\(newsObject?.id)"
Based on my comment:
You should get a response and check for errors.
Also there is always the possibility to call a URL asynchronously to avoid blocking the GUI if the request takes a long time.
This can be made using delegate patterns or with completions handlers like in Objective-C.
Example:
var url = NSURL.URLWithString(addViewUrl)// Creating URL
var request = NSURLRequest(URL: url)// Creating Http Request
var queue: NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response:NSURLResponse!, responseData:NSData!, error: NSError!) -> Void in
if error != nil
{
println(error.description)
}
else
{
var responseStr:NSString = NSString(data:responseData, encoding:NSUTF8StringEncoding)
//Everything went fine
}
})
I need to check if a website is reachable before loading it. I am new in iOS developement but this is the method I've implemented to discover the response.
var url = NSURL(string: "http://www.apple.com")
var task = NSURLSession.sharedSession().dataTaskWithURL(url!) {
data, response, error in
println(data)
var httpResponse = response as? NSHTTPURLResponse
println(httpResponse)
}
task.resume()
It works! But the problem is that the response comes from the cache... So the result is that:
If I am checking if a file exists and at that moment I am checking it exists -> for the application it will always exist because it is stored in the cache... So if I remove the file and then I make the request... it will always give me response 200 and not 404.
Infact if I insert this line of code (it deletes the cache!) before making the request then it works like it should work and it always check for real if the website or the file exists!
NSURLCache.sharedURLCache().removeAllCachedResponses()
So... how can I solve this problem in Swift?...thank you very much
You can set a no cache policy by using a new url session instance.
Create a property and set a new NSURLSession instance to it.
var urlSession : NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.requestCachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData;
self.urlSession = NSURLSession(configuration: configuration)
Use this URLSession property to get your data.
var url = NSURL(string: "http://www.apple.com")
var task = self.urlSession.dataTaskWithURL(url!) {
data, response, error in
// Your code
}
task.resume()
I know it's possible to use the Google Places API with MonoTouch, but I'm having trouble getting it to work. I have the properly formated URL with my API key and all, but could someone please provide me with some example code of how to get and use the response from the request url?
(ex: https://maps.googleapis.com/maps/api/place/search/json?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=AddYourOwnKeyHere)
I ended up using the code below, it gives me a JsonArray of JsonObjects that I can iterate through and use. For this example it's for a places textsearch not a standard places search.
HttpWebRequest url = (HttpWebRequest)WebRequest.Create("https://maps.googleapis.com/maps/api/place/textsearch/json? query=Sushi&sensor=false&type=restaurant&key=YOUR_KEY_HERE");
HttpWebResponse temp = (HttpWebResponse)url.GetResponse ();
JsonValue jval = JsonObject.Load (temp.GetResponseStream ());
JsonObject jobj = (JsonObject)jval;
var resultJson = jobj["results"];
foreach (JsonObject jentry in resultJson)
{
//Do stuff with the jentry
}