NSURLErrorDomain Code=-2000 "can’t load from network" - ios

My app started accidentally with an error that I never had before and I can't find any solution around the net. I think it has nothing to do with my code but if it helps, here it is:
class InterfaceController: WKInterfaceController {
#IBOutlet var tableView: WKInterfaceTable!
final let url = URL(string: "http://...")
private var tasks = [Task]()
override func awake(withContext context: Any?) {
super.awake(withContext: context)
downloadJson()
}
func downloadJson() {
guard let downloadURL = url else { return }
URLSession.shared.dataTask(with: downloadURL) { data, urlResponse, error in
guard let data = data, error == nil, urlResponse != nil else {
print("something is wrong")
return
}
do
{
let decoder = JSONDecoder()
let downloadedTasks = try decoder.decode(Tasks.self, from: data)
self.tasks = downloadedTasks.tasks
print(self.tasks)
} catch {
print("somehting went wrong after downloading")
}
}.resume()
}
}
The error message I'm getting in the console is:
2018-11-07 21:34:15.538369+0100 BJwatch WatchKit Extension[1884:84116] Task <82BE34C9-CCAB-4076-8012-CC9FF61AE556>.<1> load failed with error Error Domain=NSURLErrorDomain Code=-2000 "can’t load from network" UserInfo={NSLocalizedDescription=can’t load from network, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <82BE34C9-CCAB-4076-8012-CC9FF61AE556>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <82BE34C9-CCAB-4076-8012-CC9FF61AE556>.<1>"
), NSErrorFailingURLStringKey=http://..., _kCFNetworkErrorConditionalRequestKey=<CFMutableURLRequest 0x7c09bc70 [0x34f528c]> {url = http://..., cs = 0x0}, _kCFNetworkErrorCachedResponseKey=<CFCachedURLResponse 0x7afc2840 [0x34f528c]>, NSUnderlyingError=0x7c1eb930 {Error Domain=kCFErrorDomainCFNetwork Code=-2000 "(null)" UserInfo={_kCFNetworkErrorCachedResponseKey=<CFCachedURLResponse 0x7afc2840 [0x34f528c]>, _kCFNetworkErrorConditionalRequestKey=<CFMutableURLRequest 0x7c09bc70 [0x34f528c]> {url = http://..., cs = 0x0}}}, NSErrorFailingURLKey=http://...} [-2000]
[BJwatch_WatchKit_Extension.Task, BJwatch_WatchKit_Extension.Task, BJwatch_WatchKit_Extension.Task, BJwatch_WatchKit_Extension.Task]
The URL is not "http://..." in the real app. It is a URL that gives a JSON array and it is working.

NSURLErrorCannotLoadFromNetwork
This error is sent when the task needs to load from the network, but is blocked from doing so by the “load only from cache” directive.
The default policy is NSURLRequest.CachePolicy.useProtocolCachePolicy
useProtocolCachePolicy: Use the caching logic defined in the protocol implementation, if any, for a particular URL load request.
Important: If you are making HTTP or HTTPS byte-range requests, always use the NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData policy instead.
var request = URLRequest(url: URL(string:"http://...")!)
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData
URLSession.shared.dataTask(with: request) {...

Correct answer to fix this is to change URLSessionConfiguration. If anyone still want to use .useProtocolCachePolicy policy, use background configuration.
let configuration = URLSessionConfiguration.background(withIdentifier: "xxx.xxx.xxxxx")
let session = URLSession(configuration: configuration)
Below is what I get from Apple's support.
Watch apps tend to be suspended very quickly so we recommend that developers use a background url session to ensure their api calls are still performed should an event such as backgrounding or suspension occur.

Related

can not load random image from API [duplicate]

This question already has answers here:
Loading/Downloading image from URL on Swift
(39 answers)
Closed 6 months ago.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageOfDog: UIImageView!
struct dataForLoading: Codable {
let message: String
}
override func viewDidLoad() {
super.viewDidLoad()
// load url
let url = "https://dog.ceo/api/breeds/image/random"
guard let loadUrl = URL(string: url) else { return }
// use loaded url in urlSession
URLSession.shared.dataTask(with: loadUrl) {(data, response, error) in
if error != nil{
print("if error printed")
print(error!.localizedDescription)
}
// decode
guard let data = data else { return }
do {
let jsonData = try JSONDecoder().decode(dataForLoading.self, from: data)
DispatchQueue.main.async {
self.imageOfDog.image = UIImage(named: jsonData.message)
}
}
catch let jsonError {
print(jsonError)
}
}.resume()
}
}
i am currentlt using. https://dog.ceo/api/breeds/image/random. this api
for loading random image
i am new to loading Api i am trying to load API through URLSession
when i run project i get below error
Random dog image[5960:196973] [framework] CUIThemeStore: No theme registered with id=0
i think i am not able to decode it properly how can i load image through API
At First Api Generates an url from image like these. {"message":"https://images.dog.ceo/breeds/elkhound-norwegian/n02091467_5985.jpg","status":"success"}
so my idea is to get first API and in Api whaterver url is coming pass it to imageview
The error occurs cause of UIImage(named: jsonData.message) . You can call this only if the image is exist in Assets Folder. You have to use UIImage(data: data)
Example of usage
if let imageURL = URL(string: jsonData.message){
if let data = try? Data(contentsOf: imageURL){
self.imageOfDog.image = UIImage(data: data)
}
}

Azure Media Service Fairplay DRM AVPlayer swift implementation

I am trying to play a Fairplay DRM protected (encrypted through Azure Media Services) HLS video stream on iOS Device.
I have used the code and process described in the following links:
https://icapps.com/blog/how-integrate-basic-hls-stream-fairplay
https://gist.github.com/fousa/5709fb7c84e5b53dbdae508c9cb4fadc
Following is the code I have written for this.
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAssetResourceLoaderDelegate {
#IBOutlet weak var videoView: UIView!
var player: AVPlayer!
override func viewDidLoad() {
super.viewDidLoad()
let streamURL = "someexampleurl.com/stream.m3u8"
if let url = URL(string: streamURL) {
//2. Create AVPlayer object
let asset = AVURLAsset(url: url)
let queue = DispatchQueue(label: "Some queue")
asset.resourceLoader.setDelegate(self, queue: queue)
let playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: playerItem)
//3. Create AVPlayerLayer object
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.videoView.bounds //bounds of the view in which AVPlayer should be displayed
playerLayer.videoGravity = .resizeAspect
//4. Add playerLayer to view's layer
self.videoView.layer.addSublayer(playerLayer)
//5. Play Video
player.play()
}
// Do any additional setup after loading the view.
}
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
// We first check if a url is set in the manifest.
guard let url = loadingRequest.request.url else {
print("🔑", #function, "Unable to read the url/host data.")
loadingRequest.finishLoading(with: NSError(domain: "com.error", code: -1, userInfo:
nil))
return false
}
print("🔑", #function, url)
// When the url is correctly found we try to load the certificate date. Watch out! For this
// example the certificate resides inside the bundle. But it should be preferably fetched from
// the server.
guard
let certificateURL = Bundle.main.url(forResource: "certfps", withExtension: "cer"),
let certificateData = try? Data(contentsOf: certificateURL) else {
print("🔑", #function, "Unable to read the certificate data.")
loadingRequest.finishLoading(with: NSError(domain: "com.error", code: -2, userInfo: nil))
return false
}
// Request the Server Playback Context.
let contentId = "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
guard
let contentIdData = contentId.data(using: String.Encoding.utf8),
let spcData = try? loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData, options: nil),
let dataRequest = loadingRequest.dataRequest else {
loadingRequest.finishLoading(with: NSError(domain: "com.error", code: -3, userInfo: nil))
print("🔑", #function, "Unable to read the SPC data.")
return false
}
// Request the Content Key Context from the Key Server Module.
let ckcURL = URL(string: "https://xxxxx.keydelivery.northeurope.media.azure.net/FairPlay/?kid=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!
var request = URLRequest(url: ckcURL)
request.httpMethod = "POST"
let assetIDString = "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
let postString = "spc=\(spcData.base64EncodedString())&assetId=\(assetIDString)"
request.setValue(String(postString.count), forHTTPHeaderField: "Content-Length")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = postString.data(using: .ascii, allowLossyConversion: true)
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: request) { data, response, error in
if let data = data {
// The CKC is correctly returned and is now send to the `AVPlayer` instance so we
// can continue to play the stream.
if var responseString = String(data: data, encoding: .utf8) {
responseString = responseString.replacingOccurrences(of: "<ckc>", with: "").replacingOccurrences(of: "</ckc>", with: "")
var ckcData = Data(base64Encoded: responseString)!
dataRequest.respond(with: ckcData)
loadingRequest.finishLoading()
} else {
// print("Error encountered while fetching FairPlay license for URL: \(self.drmUrl), \(error?.localizedDescription ?? "Unknown error")")
}
task.resume()
return true
}
}
Everything above works but in the CKC response I get
{
"Error": {
"Message": "Failed content key policy evaluation.",
"Code": "AuthorizationPolicyEvaluationFailure"
}
}
Can anyone please here let me know what I am missing here, this is my first time trying this out
so I could be making a very obvious mistake so please bear with that.
Any help regarding this would be really great (I have been hitting my head on this for multiple days now.)
Thanks.
One thing that will probably help with troubleshooting is to enable the license delivery logging. You can do this in the Azure portal by going to your Media Services account, in the Monitoring section go to Diagnostic settings. Click 'Add diagnostic setting'. Give the setting a name and then, at least initially, tell it to archive to a storage account. Log the 'KeyDeliveryRequests'. Once you save this reproduce the issue. Then go to your Storage account and look for the log result. The Storage container ‘insights-logs-keydeliveryrequests’ will contain the logs.
you can add request header parameter like "authorization" (probably a base 64 token called JWT), "mimetype" in making CKC request, it would work.
Finally, I figured the thing I was missing was not passing the JWT in the "Authorization" header for the CKC request.
Passing the JWT did the trick. :)
Note: JWT stands for the JSON web token generated during the media encryption in azure media services.

URLSession results in NIL data

I'm trying to learn Swift, and I have a little project with Google's places API.
I have a method for fetching places details, which uses URLSession in swift to send the request:
func fetchRestaurantDetails(placeId: String) -> Void {
let jsonURLString = "https://maps.googleapis.com/maps/api/place/details/json?placeid=\(placeId)&key=[MY API KEY]"
guard let url = URL(string: jsonURLString) else { return}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
_ = session.dataTask(with: urlRequest) { (data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
let place = try JSONDecoder().decode(Result.self, from: responseData) // New in Swift 4, used to serialize json.
self.rest = place.result
} catch {
print("error trying to convert data to JSON")
return
}
}.resume()
}
I use this method to create a instance of type Restaurants, which I will later add to a list:
func createRestaurant(placeId: String) -> Restaurants {
self.fetchRestaurantDetails(placeId: placeId)
let rest = Restaurants(name: self.rest.name,
formatted_address: self.rest.formatted_address,
website: self.rest.website,
location: ((self.rest.geometry.location.lat,self.rest.geometry.location.lng)),
opening_hours: self.rest.opening_hours.weekday_text,
photo: restImg)
return rest!
}
But whenever I reach back into the "let rest = Restaurants(...)" all the values are nil. When I try to debug it, it just jumps over my "_ = session" sections right down to resume(), then back to session again and ends back at resume(). No data produced.
I'm quite puzzled since I successfully executed this piece of code before, and now I'm wondering if I missed something.
Thx :-)
Put two breakpoints. One at
let place = try JSONDecoder().decode(Result.self, from: responseData) // New in Swift 4, used to serialize json.
self.rest = place.result
and the second one at
let rest = Restaurants(name: self.rest.name,
formatted_address: self.rest.formatted_address,
website: self.rest.website,
location: ((self.rest.geometry.location.lat,self.rest.geometry.location.lng)),
opening_hours: self.rest.opening_hours.weekday_text,
photo: restImg)
You will realise that the second one is getting called first.
You are fetching data, which is done asynchronously, and before its available you are trying to use it. You need to make sure that the data is available before you use it. One way here would be to use completion handler. You can learn about completion handlers here.
fetchRestaurantDetails is an asynchronous method due to the fact that you call session.dataTask in it, which is asynchronous.
You are trying to use the results of the function before it actually returned. You have several ways to solve this issue:
Use a completion handler to return the value from fetchRestaurantDetails
Use DispatchGroups to detect when the URLRequest finished
Use a 3rd party framework like PromiseKit to handle the asynchronous functions like normal functions with return values.

Image URL to UIImage Not Working

let url = URL(string: (pinsFIREBASE[marker.snippet!]?.imageURL)!)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
self.postImage.image = UIImage(data: data)
}
}
task.resume()
I have the following code that takes a url from firebase, in the form http://i.imgur.com/nkomPpP.jpg, and is supposed to turn that url into a UIImage that can be placed on a view. However, while extracting the text from the firebase object works, parsing the image URL doesn't seem to be working as I get an empty view. What am I doing wrong?
I know why, your code works. The problem is your image link. Your imageURL's HTTP type. iOS don't like HTTP type request because it's not safe.
Plan A: Try a HTTPS type image link, it works.
Plan B: Add "App Transport Security Settings" in project info ,and set "Allow
Arbitrary Loads" yes in "App Transport Security Settings" dictionary.
I suggested use Plan A, that's Apple want iOSDev to do.
You need to remove the () from after DispatchQueue.main.async(). Try this:
let url = URL(string: (pinsFIREBASE[marker.snippet!]?.imageURL)!)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
self.postImage.image = UIImage(data: data)
}
}
task.resume()

iOS Swift fastest way to download many small files

I'm writing an iPad app that needs, to download many, but fairly small, .json and .jpg files from an server.
So fare I am doing it like this:
///Function to allow for recursive calls to syncronize inspections sequentially.
func getInspection(ip: String, view: sensorSyncronizationDelegate, idarr:[IdData], appDelegate: AppDelegate){
let inspectionID = idarr[0]
var newArr = idarr
//A task is created for each inspection that needs to be downloaded, and the json is parsed and added to the database.
if self.session != nil {
let inspectionURL = NSURL(string: "http://\(ip)/inspections/\(inspectionID.id!).json")
let inspectionTask = self.session!.dataTaskWithURL(inspectionURL!) { (data, response, error) in
//If data is nil, end the task.
if data == nil {
view.setInspectionSyncCompleted()
view.completion("Error: Timeout please ensure Instrument is on, and attempt syncronization again")
print(error)
return
}
//if newArr is NOT empty make a recursiv call to getInspection()
newArr.removeAtIndex(0)
if !newArr.isEmpty{
self.getInspection(ip, view: view, idarr: newArr, appDelegate: appDelegate)
}else{
self.syncMisc(ip, view: view)
}
(I'm always using dataTaskWithURL)
And this is how the session is setup:
var session : NSURLSession?
///Function to set up various http configurations, and call the various syncronization functions.
func syncWithSensor(view: sensorSyncronizationDelegate, ip: String!){
//Session Configuration
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
config.allowsCellularAccess = true
config.timeoutIntervalForRequest = 30
config.timeoutIntervalForResource = 60
config.URLCache = nil
//Authentication config
let userpasswordString = "MAMA:PassWord"
let userpasswordData = userpasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64encodedCreds = userpasswordData!.base64EncodedStringWithOptions([])
let authString = "Basic \(base64encodedCreds)"
config.HTTPAdditionalHeaders = ["Authorization" : authString, "Connection" : "Upgrade"]
session = NSURLSession(configuration: config)
//Check if for some reason ip is invalid
if ip == nil{
view.setSeriesSyncCompleted()
view.setTemplateSyncCompleted()
view.setInspectionSyncCompleted()
view.completion("Error: Failed to connect to ***, please reset connection")
}
//Call the inspection sync function.
syncInspections(ip, view: view)
}
//Function to respond to authentication challenges.
func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void) {
let credential = NSURLCredential(user: "MAMA", password: "PassWord", persistence: .ForSession)
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential)
}
And yes it work like just fine. I can download 280+ files (.json and .jpg) in 22sec, which is decent, but a very long time for a user, to look at a download counter.
And the plan is, to have more then that.. So I really need a way to do this faster.
I can provide more of the code i'm using, if needed.
Thanks in advance :)
Try optimizing with json and images batching (server side optimization). It's always better to download one big file than a lot of small ones for a period of time. If you always need all of them it's a big win for battery life as it was pointed in documentation.

Resources