XCode NSMutableURLRequest not hitting the web server - ios

I have several series of images stored in folders on a web server. The folder hierarchy looks something like this…
WebServer’s Web Folder
_20160123161915_4671579
Final
t1.jpg
t2.jpg
i1.jpg
i2.jpg
Recall
t1.jpg
t2.jpg
i1.jpg
i2.jpg
Initial
t1.jpg
t2.jpg
i1.jpg
i2.jpg
The user selects one of the series, initial, Recall, or Final from a list on the iPad and the series is retrieved from the server and displayed. I am using the following code to retrieve the images. In this example I am retrieving the the jpgs that start with a t…
for (int i = 0; i < imageCount; i++) {
fileName = [NSString stringWithFormat:#"t%i.jpg", i+1];
imagePath = [NSString stringWithFormat:#"_%#/%#/%#", [opHelper ID], [seriesArray objectAtIndex:thumbsSeriesIndex], fileName];
urlString = [[NSString stringWithFormat:#"http://%#/%#", [opHelper hostIP], imagePath] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
url = [NSURL URLWithString:urlString];
imageURL = [NSMutableURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *error;
imageData = [NSURLConnection sendSynchronousRequest:imageURL returningResponse:&response error:&error];
//consume the imageData
}
This works except for once in a while I get back blank images for one of the series. In my example, I would get good images with Initial and Recall, but Final comes back with blanks. A 500kb file is requested and imageData gets 800 bytes. I can watch the server hit count increase for each image request. If a series fails the server does not get hit at all. ie. If in the above example file hierarchy I select Recall the server count increases by 4, same with Initial, but if I select Final the server count does not increment.
Here are the actual NSURLs being sent…
url NSURL * #"http://192.168.1.100/_20160123161915_4671579/Final/t1.jpg" 0x80a7f640
url NSURL * #"http://192.168.1.100/_20160123161915_4671579/Recall%201/t1.jpg" 0x804fbcf0
url NSURL * #"http://192.168.1.100/_20160123161915_4671579/Initial/t1.jpg" 0x8088da30
The url for Final in this case was acting up and not hitting the server, but I don’t see anything that jumps out at me in the URLs that might be suspect. Does anyone see anything in my code that might be contributing to this problem? I know it is not a file issue as even if I rename one of the working folders so that the requested file cannot be found, the server gets hit and the iPad receives blank images without any errors.
Thanks for any help.
John

Ok. It was a cache issue. I fixed it by modifying the cache policy...
imageURL = [NSMutableURLRequest requestWithURL:url];
imageURL.cachePolicy = NSURLRequestReloadIgnoringCacheData;
John

Related

cloudkit error no authToken received for asset

Why do I get this error when I run the following code? :
"Internal Error" (1/1000); "No authToken received for asset"
I think it has something to do with the setObject code in the last line.
let documentsDirectoryPath:NSString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
var imageURL: URL!
let imageData = UIImageJPEGRepresentation(self.newImage, 1.0)
let path:String = documentsDirectoryPath.appendingPathComponent(self.newImage.description)
try? UIImageJPEGRepresentation(self.newImage, 1.0)!.write(to: URL(fileURLWithPath: path), options: [.atomicWrite])
imageURL = URL(fileURLWithPath: path)
try? imageData?.write(to: imageURL, options: [.atomicWrite])
let imageAsset:CKAsset? = CKAsset(fileURL: URL(fileURLWithPath: path))
curImages = record["Images"] as! [CKAsset]
curImages.append(imageAsset!)
print("saving image")
record.setObject(curImages as CKRecordValue?, forKey: "Images")
I've encountered this, too. It appears to be a bug in cloudkit, and--from what I can tell--it happens when you try to re-use any part of the "asset creation chain."
In other words, you have some initial data, you create an image from that data, you write it to a file, you load that file into a CKAsset, then you load the CKAsset into the CKRecrod. In my experiments, if you re-use any of those components... or if they just happen to be the same (that is, you create an image, then you happen to create a new-but-identical image later) you'll see this error.
For example, the following code reliably recreates the "no auth token" error when saving a record. All it does is create an array of assets and places it into the record:
for (int i = 0; i <= maxPlayers; i++)
{
int tempVal = 0xf;
NSData *tempData = [[NSData alloc] initWithBytes:&tempVal length:sizeof(tempVal)];
NSString *tempDataFilepath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"temp%d.dat",i]];
[tempData writeToFile:tempDataFilepath atomically:YES];
NSURL *tempDataURL = [NSURL fileURLWithPath:tempDataFilepath];
someArray[i] = [[CKAsset alloc] initWithFileURL:tempDataURL ];
}
someRecord[SOME_FIELD_NAME] = someArray;
Simply changing the third line to:
int tempVal = i; //force the temp value to be different every time
Completely solves the error.
Furthermore, this error occurs even when I tried to use a value in a different CKAsset **that was already used in a prior CKAsset For example, using int tempVal = 0xf in the first asset, then using int secondTempVal = 0xf in another CKAsset also produces the "no auth token" error.
In my case, I was able to force the asset value to always be a unique value, and completely solved the problem. In your case, I suggest the following possible work arounds:
Check if you're using identical images for your assets. If you are, try slightly modifying the images for each new CKAsset.
If you must re-use identical images, try saving the record after you set each asset. I do not know if that will solve the issue, and it certainly increases your network traffic. But it's worth an experiment to see if it helps.
In this question Saving CKAsset to CKRecord in CloudKit produces error: "No authToken received for asset" the OP was able to create separate copies of the image file that ultimately solved the problem.
Open a bug with Apple. I didn't bother doing this, as I've grown jaded watching similar bug reports sit open for years without attention. But who knows, you might have better luck.
This is not an answer to the specific problem (which as been solved by the accepted answer), but it solves another problem that creates the same error message, so it might be useful for somebody else:
I have an app that uses CoreData+Cloudkit with a .public database, i.e. my description for the NSPersistentCloudKitContainer uses
description.cloudKitContainerOptions!.databaseScope = .public
Whenever I change my CloudKit schema, I have to re-initialize it using
do {
try self.initializeCloudKitSchema()
} catch {
print("Could not initialize schema, error \(error)")
}
This creates the error
"Internal Error" (1/1000); "No authToken received for asset"
although I do not use any asset in my model.
I now realized that it has something to do with the .public database:
As soon as I out-comment the instruction that sets the database scope to .public, the re-initialization works without problems.
Now the CloudKit schema is independent of the database type (.private or .public). Thus, a re-initialization of the schema requires the following:
Set the database to .private (the default)
Execute the init code
Set the database to .public
Disable the init code
PS: I know I should write now a bug report, but I stopped doing so: Nearly none of my bug reports (about 15) have ever been answered or processed, so it is not worth the effort.

Sending few values at the same time over bluetooth

I am working on a Bluetooth based application and I am having problems when I try to send data from the iPhone to the other device.
I have no problem when I have to send just one value, using something like this:
- (void)sendData:(NSInteger)mel {
NSData *myData = [NSData dataWithBytes:&mel length:sizeof(mel)];
[self.myDevice writeValue:myData forCharacteristic:self.myCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
But, for some characteristics I need send 2 or more values at the same time (for example in this case, variable mel and another one) but I haven’t been able yet to do it.
Does somebody know how to do this? Thanks in advance.
UPDATE 1
What I tried to send two values is
unsigned char bytes[] = {mel, interval};
NSMutableData *myData = [NSMutableData new];
[myData appendBytes:&bytes length:sizeof(bytes)];
[self.myDevice writeValue:myData forCharacteristic:self.myCharacteristic type:CBCharacteristicWriteWithoutResponse];
But this works like if the second value didn't exist
You can't use sizeof(bytes) to get the number of bytes in the array. It's simply going to return 4 since that is the size of a char *.
One options would be to use sizeof(mel) + sizeof(interval) instead of sizeof(bytes).

converting video to NSData in share extension in ios

I have created a share extension for my ios app. When I click on the share option in the photo app my share extension is shown and I click on it and my controller is shown. Everything is working fine up to this. I am uploading video to youtube using the youtube api. I am using this method to create the parameter
GTLUploadParameters *uploadParameters = [GTLUploadParameters uploadParametersWithData:fileData MIMEType:#"video/*"];
Now if the video is small then it is easily converted to NSData using this code
NSData *fileData = [NSData dataWithContentsOfURL:[NSURL URLWithString:videoURL]];
and everything is working and video is uploaded.
But if the video is large then it simply crash and exit from the share extension(I put breakpoint and found this problem. If I remove the fileData conversion then its not crashing.). So what I did was instead of converting it to NSData I used this youtube api method
GTLUploadParameters *uploadParameters = [GTLUploadParameters uploadParametersWithFileURL:[NSURL URLWithString:videoURL] MIMEType:#"video/*"];
Now app is not crashing but I am getting network error. The error is
Error Domain=NSURLErrorDomain Code=-995 "(null)"
little searching found that it is because of NSURLSession and told to use something like this
sessionConfiguration.sharedContainerIdentifier = #“com.me.myapp.containerIdentifier”;
I am using youtube api. I am not sure where to use it OR is there any other way to use youtube api in share extension with large video file.
NOTE: I am using youtube api in my app and its working fine with NSData.
Hope question is clear. I am stuck on it for a day now. Please help.
Thanks in advance.
EDIT 1:
I used this code
NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:videoURL] options:0 error:&error];
filedata is nil. The error I am getting is
Error Domain=NSCocoaErrorDomain Code=260 "The file “IMG_2187.MOV”
couldn’t be opened because there is no such file."
UserInfo={NSFilePath=/file:/var/mobile/Media/DCIM/102APPLE/IMG_2187.MOV,
Consider this line:
var videoDataURL = info[UIImagePickerControllerMediaURL] as! NSURL!
This does a forced unwrapping of info[UIImagePickerControllerMediaURL] (which is bad, because if it was nil, the app would crash) and that casts it as an implicitly unwrapped optional NSURL!. That doesn't make sense. Just do a conditional unwrapping (and unwrap to a NSURL, not a NSURL!):
if let videoDataURL = info[UIImagePickerControllerMediaURL] as? NSURL { ... }
The next line calls filePathURL:
var videoFileURL = videoDataURL.filePathURL
If you wanted a file URL, you already have one, so no conversion is needed, but instead just use videoDataURL. If you really wanted a path, you'd use path method:
let videoPath = videoDataURL.path
Frankly, Apple is trying to shift us away from using string paths, so just use the original videoDataURL and avoid the use of both path and filePathURL.
You are using dataWithContentsOfMappedFile:
var video = NSData.dataWithContentsOfMappedFile("\(videoDataURL)")
If you really wanted to use dataWithContentsOfMappedFile, the proper Swift syntax is:
let video = NSData(contentsOfMappedFile: videoPath!)
But dataWithContentsOfMappedFile deprecated, so you should instead use:
let video = try NSData(contentsOfFile: videoPath!, options: .DataReadingMappedIfSafe)
Or, bypassing that videoPath altogether, you could:
let video3 = try NSData(contentsOfURL: videoDataURL, options: .DataReadingMappedIfSafe)
Obviously, those try renditions should be done within a do block with a catch block.
By the way, as you'll see in all of my above examples, one should use let where possible.
Quite frankly, I would advise against loading it into a NSData at all. Just copy it with NSFileManager, which is a more efficient use of memory. If the video is long, it could be quite large, and you should avoid loading the whole thing into memory at any given point in time.
So you could:
if let videoDataURL = info[UIImagePickerControllerMediaURL] as? NSURL {
do {
// build your destination URL however you want
//
// let tempFolder = NSURL(fileURLWithPath: NSTemporaryDirectory())
// let destinationURL = tempFolder.URLByAppendingPathComponent("test.mov")
// or
let documents = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
let destinationURL = documents.URLByAppendingPathComponent("test.mov")
// but just copy from the video URL to the destination URL
try NSFileManager.defaultManager().copyItemAtURL(videoDataURL, toURL: destinationURL)
} catch {
print(error)
}
}
If you're uploading this to a web service, you'd then use a NSURLSessionUploadTask, using file or stream options. The construction of this request is a separate question, but hopefully you get the idea: With large assets like photos or, especially, videos, don't instantiate a NSData with the asset if you can possibly avoid it.
Please try this if your file exist in your phone instead of [NSURL URLWithString:videoURL].
NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:videoURL]];

Thread on Images are not loading from bundle path While Loading the Webview?

In my iOS app, I am loading the 'IYGLessonPlayer' html page in web view. Here, IYGLessonPlayer html Page load another html page i.e., index.html.
"index.html page will come as per the screen shot"
I have added a folder structure (created folder references option) in app bundle i.e.,
check the below url
https://www.dropbox.com/s/l9qjamfsg9gt867/Screen%20Shot%202015-03-02%20at%202.11.56%20PM.png?dl=0
My issue is:
I am unable to load the images, css from the path in html page i.e.,
assets/images/background.png
assets/css/bootstrap.css
web view code
iOS code
NSURL *htmlFile =
[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"IYGLessonPlayer" ofType:#"html"]
isDirectory:NO];
NSURLRequest *request = [NSURLRequest requestWithURL:htmlFile];
[_contentWebView loadRequest:request];
Javscript code
for css
$('.submitboxnew').css({
"color": "#ffff00",
"background": "url('../assets/images/background.png')",
"animation": "none"
});
for html loading
bundlePath = [[NSBundle mainBundle]resourcePath];
rootPath = IYG_G7_L03;
temp = IYG_G7_L03_7.2_Pressures;
bundle path comes from native iOS
var path = bundlePath + '/' + rootPath + '/' + temp + '/';
var activity_path = path + 'index.html';
alert("activity path:"+activity_path);
$(".content").load(activity_path);
Please let us know how to load all the content.
Note:
Here, from native code loading IYGLesson Player html page.
and from Javascript code loading another html pages.

AVAssetResourceLoaderDelegate - Only requests first two bytes?

I'm implementing an AVAssetResourceLoaderDelegate, and I'm having a bit of trouble getting to to behave correctly. My goal is to intercept any requests made by the AVPlayer, make the request myself, write the data out to a file, then respond to the AVPlayer with the file data.
The issue I'm seeing: I can intercept the first request, which is only asking for two bytes, and respond to it. After that, I'm not getting any more requests hitting my AVAssetResourceLoaderDelegate.
When I intercept the very first AVAssetResourceLoadingRequest from the AVPlayer it looks like this:
<AVAssetResourceLoadingRequest: 0x17ff9e40,
URL request = <NSMutableURLRequest: 0x17f445a0> { URL: fakeHttp://blah.com/blah/blah.mp3 },
request ID = 1,
content information request = <AVAssetResourceLoadingContentInformationRequest: 0x17ff9f30,
content type = "(null)",
content length = 0,
byte range access supported = NO,
disk caching permitted = NO>,
data request = <AVAssetResourceLoadingDataRequest: 0x17e0d220,
requested offset = 0,
requested length = 2,
current offset = 0>>
As you can see, this is only a request for the first two bytes of data. I'm taking the fakeHttp protocol in the URL, replacing it with just http, and making the request myself.
Then, here's how I'm responding to the request once I have some data:
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
//Make the remote URL request here if needed, omitted
CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)([self.response MIMEType]), NULL);
loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
loadingRequest.contentInformationRequest.contentType = CFBridgingRelease(contentType);
loadingRequest.contentInformationRequest.contentLength = [self.response expectedContentLength];
//Where responseData is the appropriate NSData to respond with
[loadingRequest.dataRequest respondWithData:responseData];
[loadingRequest finishLoading];
return YES;
}
I've stepped through this and verified that everything in the contentInformationRequest is filled in correctly, and that the data I'm sending is NSData with the appropriate length (in this case, two bytes).
No more requests get sent to my delegate, and the player does not play (presumably because it only has two bytes of data, and hasn't requested any more).
Does anyone have experience with this to point me toward an area where I may be doing something wrong? I'm running iOS 7.
Edit: Here's what my completed request looks like, after I call finishedLoading:
<AVAssetResourceLoadingRequest: 0x16785680,
URL request = <NSMutableURLRequest: 0x166f4e90> { URL: fakeHttp://blah.com/blah/blah.mp3 },
request ID = 1,
content information request = <AVAssetResourceLoadingContentInformationRequest: 0x1788ee20,
content type = "public.mp3",
content length = 7695463,
byte range access supported = YES,
disk caching permitted = NO>,
data request = <AVAssetResourceLoadingDataRequest: 0x1788ee60,
requested offset = 0,
requested length = 2,
current offset = 2>>
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
loadingRequest.contentInformationRequest.contentType = #"public.aac-audio";
loadingRequest.contentInformationRequest.contentLength = [self.fileData length];
loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
NSData *requestedData = [self.fileData subdataWithRange:NSMakeRange((NSUInteger)loadingRequest.dataRequest.requestedOffset,
(NSUInteger)loadingRequest.dataRequest.requestedLength)];
[loadingRequest.dataRequest respondWithData:requestedData];
[loadingRequest finishLoading];
return YES;
}
This implementation works for me. It always asks for the first two bytes and then for the whole data. If you don't get another callback it means that there was something wrong with the first response you have made. I guess the problem is that you are using MIME content type instead of UTI.
Circling back to answer my own question in case anyone was curious.
The issue boiled down to threading. Though it's not explicitly documented anywhere, AVAssetResourceLoaderDelegate does some weird stuff with threads.
Essentially, my issue was that I was creating the AVPlayerItem and AVAssetResourceLoaderDelegate on the main thread, but responding to delegate calls on a background thread (since they were the result of network calls). Apparently, AVAssetResourceLoader just completely ignores responses coming in on a different thread than it was expecting.
I solved this by just doing everything, including AVPlayerItem creation, on the same thread.

Resources