I currently have a an array of strings that contain about three thousand urls to download images. I have set up the following function to the pass the array of urls into:
func downloadAllImages(urlArray:[String])->Void{
var urlArray = urlArray
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
var TotalProducts = urlArray.count
for i in 0 ..< urlArray.count {
Alamofire.download(
urlArray[i],
method: .get,
to: destination).downloadProgress(closure: { (progress) in
self.IndividualImage.text="\(progress.fractionCompleted*100)%"
}).response(completionHandler: { (DefaultDownloadResponse) in
TotalProducts-=1
self.DownloadLabel.text="Images left to download: \(TotalProducts)"
print("\(DefaultDownloadResponse.error)")
print("\(DefaultDownloadResponse.response)")
})
}
}
This works fine except it tends to cut out after downloading between 800-900 images and just stops. It outputs no errors. As I'm looping through every url and creating a task I imagine there are only so many concurrent tasks I can have at once. Any ideas?
UPDATE
I have gone through the output responses and found many are timing out:
Optional(Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x60800145b840 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=www.MYURL.com/image1.jpg, NSErrorFailingURLKey=www.MYURL.com/image1.jpg, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.})
So this is obvious as the for loop is sending everything to a task at once virtually and then it can only process so many at once. So the answer is to send them in batches. But how would I send a batch of 50 and then wait till every task has completed and then send another wave?
Related
We are writing an iOS app that uploads data to Google Firebase storage. We use the code below to upload text data to the location we specify. It works fine, except when we are not connected to the internet.
For the case of not being connected to the internet, we are having trouble trapping and handling the error. As far as we can tell, the delegate is never called (MyFirebaseProtocol_UploadTextComplete doesn't fire until much later, perhaps after a timeout) , and it just tries to upload repeatedly. If we are connected to the Mac and watching the debug window, it continuously generates errors in a loop, indefinitely. These certainly look like they are intended to be trappable (class names like NSErrorFailingURLStringKey, and helpful error messages and codes), but so far we are unable to trap them as they occur.
We are relatively new at swift, but is there some syntax, something like exception handling, that will allow us to trap and handle these errors, rather than just loop indefinitely?
Thanks!
func UploadTextFile(filepath : String, text: String, delegate : MyFirebaseProtocol)
{
let storage = Storage.storage()
let storageRef = storage.reference()
let textfileref = storageRef.child(filepath)
let data: Data? = text.data(using: .utf8)
textfileref.putData(data!, metadata: nil)
{
(metadata, error) in
delegate.MyFirebaseProtocol_UploadTextComplete(error: error)
}
}
Error messages in Xcode debug window:
2022-01-20 15:15:38.553528-0500 GarmentTest[431:60410] Connection 1: received failure notification
2022-01-20 15:15:38.553552-0500 GarmentTest[431:60410] Connection 1: failed to connect 1:50, reason -1
2022-01-20 15:15:38.553564-0500 GarmentTest[431:60410] Connection 1: encountered error(1:50)
2022-01-20 15:15:38.555426-0500 GarmentTest[431:60410] Connection 2: received failure notification
2022-01-20 15:15:38.555442-0500 GarmentTest[431:60410] Connection 2: failed to connect 1:50, reason -1
2022-01-20 15:15:38.555452-0500 GarmentTest[431:60410] Connection 2: encountered error(1:50)
2022-01-20 15:15:38.557057-0500 GarmentTest[431:60410] Task .<1> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2022-01-20 15:15:38.557104-0500 GarmentTest[431:60410] Task <1D0E3033-5A15-4435-AF3D-99A06EAB4C3C>.<2> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2022-01-20 15:15:38.557485-0500 GarmentTest[431:60410] Connection 3: received failure notification
2022-01-20 15:15:38.557544-0500 GarmentTest[431:60410] Connection 3: failed to connect 1:50, reason -1
2022-01-20 15:15:38.557664-0500 GarmentTest[431:60410] Connection 3: encountered error(1:50)
2022-01-20 15:15:38.558844-0500 GarmentTest[431:60268] Task .<1> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x280374a80 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask .<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask .<1>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://firebasestorage.googleapis.com/v0/b/garmenttest-8897e.appspot.com/o/Snapshots%2FAn%2FAn_20220120_151528_Relaxed_Center_Image_1B.png?uploadType=resumable&name=Snapshots%2FAn%2FAn_20220120_151528_Relaxed_Center_Image_1B.png, NSErrorFailingURLKey=https://firebasestorage.googleapis.com/v0/b/garmenttest-8897e.appspot.com/o/Snapshots%2FAn%2FAn_20220120_151528_Relaxed_Center_Image_1B.png?uploadType=resumable&name=Snapshots%2FAn%2FAn_20220120_151528_Relaxed_Center_Image_1B.png, _kCFStreamErrorDomainKey=1}
I am using the following code to get address from lat and long:
NSLog(#">>>>%f,%f",location.latitude,location.longitude);
[[GMSGeocoder geocoder] reverseGeocodeCoordinate:location completionHandler:^(GMSReverseGeocodeResponse *resp, NSError *error)
{
NSLog(#"response%#",resp);
}];
When I try to do geocode with a correct lat and long, for a particular location, I get null as response and error is this. This is not happening with my android device.
I can see response as nil and error is:
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSErrorFailingURLStringKey=https://clients4.google.com/glm/mmap, _kCFStreamErrorCodeKey=-2102, NSErrorFailingURLKey=https://clients4.google.com/glm/mmap, NSLocalizedDescription=The request timed out., _kCFStreamErrorDomainKey=4, NSUnderlyingError=0x1659ae80 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "The request timed out." UserInfo={_kCFStreamErrorCodeKey=-2102, NSErrorFailingURLStringKey=https://clients4.google.com/glm/mmap, NSErrorFailingURLKey=https://clients4.google.com/glm/mmap, NSLocalizedDescription=The request timed out., _kCFStreamErrorDomainKey=4}}}
This issue arises because of your API key is expired, if you regenerate the API key in google console it will work and get your response
API key example: AIzaSyDleduRwellp3OwLMAOzLt4MZWsJMWqMvM
Also check, if you're using simulator, that your simulator location setting is set to none. You can check via selecting your simulator and go to:
Debug > Location > none
Hope it helps.
Several people get this error for different reasons. None of the answers I've found have solved my problem.
I use Timberjack to log my Alamofire requests.
All my GET requests work fine and I receive data in JSON.
My POSTs on the other hand works around 1 out 10 times every time if the POST includes a JSON body.
The server does not specify any Keep-Alive header.
Deployment target is iOS 9.0
This is my shared Manager with Timberjack:
class HTTPManager: Alamofire.Manager{
static let sharedManager: HTTPManager = {
let configuration = Timberjack.defaultSessionConfiguration()
let manager = HTTPManager(configuration: configuration)
return manager
}()
}
Defining the request:
let parameters: [String: AnyObject] = ["status":status]
request = HTTPManager.sharedManager.request(.POST, "\(baseURL)\(uri)", parameters: parameters, encoding: .JSON).validate()
Sending the request:
request!.responseJSON(queue: queue, options: .AllowFragments, completionHandler: { (response) in
//Handling the response
})
Most of the time the server receives an empty JSON body. But sometimes it does work and the body is received and the server responds with an OK. When it doesn't work I receive the error:
Error: The network connection was lost.
FAILURE: Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."
UserInfo={NSUnderlyingError=0x12fa47cb0 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "The network connection was lost."
If someone can explain what's happening here I would be forever grateful :)
EDIT 1
We let the server respond every call with "Connection": "close" which did nothing to help the problem. The app always sends "Connection": "keep-alive" by default and it cannot be changed. Could this be the problem? That the app thinks the connection is open even though it's closed by the server? But even though you wait a few minutes it seems as though the POST call only works at random.
EDIT 2
Even when I wait 30 seconds between GET(s) and/or POST(s). GET always works. POST works at random (rarely). I get the -1005 error on most POSTs. Even though I get the network connection was lost error the server still receive my request but the JSON body is missing.
EDIT 3 - Ugly solution
In my response I check for the error code -1005, when I receive this error I just recreate the request and try again. This results in sending around 2-4 POST requests to the server where one POST works and the rest have empty JSON bodies.
Restart simulator or kill your app by throwing out from tasks.
Or check more solutions to this error code:
NSURLErrorDomain Code -1005 The network connection was lost
I have a video that is displayed through the one api in my application. I want to upload and save it on my device and I am using this code:
- (IBAction)saveMovie:(id)sender {
NSString *path = #"http://development.s3.amazonaws.com/uploads/f1a97ec0-e47f-470e-96a6-de98d2235dd0/Biceps.mp4";
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)){
UISaveVideoAtPathToSavedPhotosAlbum(path, nil, nil, nil);
[self alert];
} else {
NSLog(#"incompatible video");
}
}
The link "path" is a own link to upload the file but also have the view link.
When I call the method that receives an answer
Video http://development.s3.amazonaws.com/uploads/f1a97ec0-e47f-470e-96a6-de98d2235dd0/Biceps.mp4 cannot be saved to the saved photos album: Error Domain=NSURLErrorDomain Code=-1100 "The requested URL was not found on this server." UserInfo=0x1660f300 {NSErrorFailingURLStringKey=file:///http:/xerofit-development.s3.amazonaws.com/uploads/f1a97ec0-e47f-470e-96a6-de98d2235dd0/Biceps.mp4, NSErrorFailingURLKey=file:///http:/xerofit-development.s3.amazonaws.com/uploads/f1a97ec0-e47f-470e-96a6-de98d2235dd0/Biceps.mp4, NSLocalizedDescription=The requested URL was not found on this server., NSUnderlyingError=0x16542030 "The operation couldn’t be completed. No such file or directory", NSURL=file:///http:/xerofit-development.s3.amazonaws.com/uploads/f1a97ec0-e47f-470e-96a6-de98d2235dd0/Biceps.mp4}
what's wrong?
I tried to do it from NSTemporaryDirectory and it worked. It seems that if one accepts this way.
In my case I put this url
http://qa-elb-765904057.us-west-2.elb.amazonaws.com/plugin/execute?pluginName=xxxx&pluginMethod=xxxx&clientId=b0007920-88b4-11e5-a62a-fxxxxx&token=xxx94d60-91xx-11e5-a62a-f362d1d2xxx0&file_key=Mbxxx5v6RxxxQOR.mp4
and the error was this
Video /plugin/execute cannot be saved to the saved photos album: Error Domain=NSURLErrorDomain Code=-1100 "The requested URL was not found on this server." UserInfo=0x176644e0 {NSErrorFailingURLStringKey=file:///plugin/execute, NSErrorFailingURLKey=file:///plugin/execute, NSLocalizedDescription=The requested URL was not found on this server., NSUnderlyingError=0x17661850 "The operation couldn’t be completed. No such file or directory", NSURL=file:///plugin/execute}
It seems that the entire url that was sending only detected this
/plugin/execute
I'm trying to download an image (jpeg) from a given URL using this code:
let manager = AFHTTPRequestOperationManager()
manager.responseSerializer = AFImageResponseSerializer()
manager.responseSerializer.acceptableContentTypes = NSSet(array: ["application/octet-stream"]) as Set<NSObject>
manager.GET(imageURL, parameters: nil,
success: { (operation: AFHTTPRequestOperation!, responseObject: AnyObject!) -> Void in
println("IMAGE SUCCESS")
}, failure: { (operation: AFHTTPRequestOperation!, error: NSError!) in
println("IMAGE FAIL")
})
With my internet at the moment, the success block is being called when testing on the simulator. However, the failure block is being called when testing on an iPhone. On another network, the success block is always called on both iPhone and simulator.
I can't figure out what's causing the issue. I have tried setting the Content-Type to "image/jpeg" but still got the same results. Did I miss something?
The error is:
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x1675f381 {NSErrorFailingURLStringKey=<...>, NSErrorFailingURLKey=<...>, NSLocalizedDescription=The request timed out., NSUnderlyingError=0x166c7891 "The request timed out."}
Are you requesting many images (or doing many network requests)? If so, you might want to constrain the AFHTTPRequestOperationManager:
manager.operationQueue.maxConcurrentOperationCount = 5
If you don't do this and then proceed to initiate many requests, the "timeout" starts when the network requests are enqueued even though NSURLConnection (which AFHTTPRequestOperationManager uses behind the scenes) can only run 4 or 5 at a time and the latter requests may not even start until earlier ones finish. You want to start the timeout logic for network requests on the basis of when the request starts, not when the request was enqueued. By constraining the maxConcurrentOperationCount, you're ensuring that network requests aren't initiated before they can reasonably be performed.
Note, that presumes that you use the same AFHTTPRequestOperationManager for all of your requests, not instantiating new operation managers for new requests. But this will ensure that requests aren't started until the backlog of other requests has been reduced to an acceptable level.
--
As an aside, rather than replacing acceptableContentTypes, I'd merely suggest augmenting it:
var acceptableContentTypes = manager.responseSerializer.acceptableContentTypes ?? Set<NSObject>()
acceptableContentTypes.insert("application/octet-stream")
manager.responseSerializer.acceptableContentTypes = acceptableContentTypes
Frankly, I'd rather see you fix the Content-Type of the images you're retrieving from the server so that it didn't send application/octet-stream responses, but if you can't do that, the above is probably a more prudent way to adjust the content types. This ensures that you accept both your custom application/octet-stream type, as well as the standard image types.