GVKun编程网logo

在 Swift 中,如何从 UrlSession 获取错误类型(swift urlencode)

1

本篇文章给大家谈谈在Swift中,如何从UrlSession获取错误类型,以及swifturlencode的知识点,同时本文还将给你拓展iOSSwiftURLSessionPOST请求因慢速API调用

本篇文章给大家谈谈在 Swift 中,如何从 UrlSession 获取错误类型,以及swift urlencode的知识点,同时本文还将给你拓展iOS Swift URLSession POST 请求因慢速 API 调用而重复、iOS Swift 应用程序随机 EXC_BAD_ACCESS 崩溃:swift_bridgeObjectRetain swift_retain swift::RefCounts、iOS URLSession 简单的文件下载、iOS 中的 URLSession等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

在 Swift 中,如何从 UrlSession 获取错误类型(swift urlencode)

在 Swift 中,如何从 UrlSession 获取错误类型(swift urlencode)

如何解决在 Swift 中,如何从 UrlSession 获取错误类型

我的网络逻辑中有以下代码:

let task = urlSession.dataTask(with: request) { [weak self] (data,response,error) in
  
  if let error = error {        
    if error.localizedDescription.contains("The request timed out") {
      // request timeout stuff ...
    } else {
      // other errors
    }
  }
}

字符串匹配 localizedDescription 不是好的代码实践。如何获取错误类型,就像在 catch 子句中一样?

解决方法

您可以将其转换为 URLError,然后查看 code

if let error = error as? URLError {
    switch error.code {
    case .timedOut: ...
    case .cannotFindHost: ...
    default: ...
    }
}

或者,如果您只关心一种情况,您可以使用 if case

if let error = error as? URLError,case .timedOut = error.code {
    ...
}

if let error = error as? URLError,error.code == .timedOut {
    ...
}

iOS Swift URLSession POST 请求因慢速 API 调用而重复

iOS Swift URLSession POST 请求因慢速 API 调用而重复

如何解决iOS Swift URLSession POST 请求因慢速 API 调用而重复

我有一个下载任务,它首先调用一个 REST API,服务器需要为其生成一个相当大的文件,该文件需要几分钟才能生成,因为它是 cpu 和磁盘 IO 密集型的。客户端等待服务器用它生成的文件的 URL 给出一个 JSON 响应。文件下载在得到第一个结果后开始。

对于生成特别大文件的调用,这会导致服务器响应非常慢,我看到我的代码未启动的重复请求。

最初在服务器端工作的人告诉我重复的请求。然后我设置了一种检查网络流量的方法。这是通过设置连接到有线网络的 Mac 并启用网络共享并使用 Proxyman 检查从 iPhone 到 API 服务器的流量来完成的。我在网络层看到同一 API 请求的多个实例,但我的代码从未收到通知。

代码如下

  1. @objc class OfflineMapDownloadManager : NSObject,URLSessionDelegate,URLSessionDownloadDelegate {
  2. @objc func download(){
  3. let config = URLSessionConfiguration.background(withIdentifier: "OfflineMapDownloadSession")
  4. config.timeoutIntervalForRequest = 500
  5. config.shouldUseExtendedBackgroundIdleMode = true
  6. config.sessionSendsLaunchEvents = true
  7. urlSession = URLSession(configuration: config,delegate: self,delegateQueue: nil)
  8. getMapUrlsFromServer(bounds)
  9. }
  10. func getMapUrlsFromServer(){
  11. var urlString = "http://www.fake.com/DomakeMap.PHP"
  12. if let url = URL(string: urlString) {
  13. let request = NSMutableuRLRequest(url: url)
  14. //...Real code sets up a JSON body in to params...
  15. request.httpBody = params.data(using: .utf8 )
  16. request.setValue("application/json",forHTTPHeaderField: "Content-Type")
  17. request.httpMethod = "POST"
  18. request.timeoutInterval = 500
  19. urlSession?.configuration.timeoutIntervalForRequest = 500
  20. urlSession?.configuration.timeoutIntervalForResource = 500
  21. request.httpShouldUsePipelining = true
  22. let backgroundTask = urlSession?.downloadTask(with: request as URLRequest)
  23. backgroundTask?.countOfBytesClientExpectsToSend = Int64(params.lengthOfBytes(using: .utf8))
  24. backgroundTask?.countOfBytesClientExpectsToReceive = 1000
  25. backgroundTask?.taskDescription = "Map Url Download"
  26. backgroundTask?.resume()
  27. }
  28. }
  29. func urlSession(_ session: URLSession,downloadTask: URLSessionDownloadTask,didFinishDownloadingTo location: URL) {
  30. if (downloadTask.taskDescription == "CTM1 Url Download") {
  31. do {
  32. let data = try Data(contentsOf: location,options: .mappedIfSafe)
  33. let jsonResult = try JSONSerialization.jsonObject(with: data,options: .mutableLeaves)
  34. if let jsonResult = jsonResult as? Dictionary<String,AnyObject> {
  35. if let ctm1Url = jsonResult["CTM1Url"] as? String {
  36. if let filesize = jsonResult["filesize"] as? Int {
  37. currentDownload?.ctm1Url = URL(string: ctm1Url)
  38. currentDownload?.ctm1FileSize = Int32(filesize)
  39. if (Int32(filesize) == 0) {
  40. postDownloadFailed()
  41. } else {
  42. startCtm1FileDownload(ctm1Url,filesize)
  43. }
  44. }
  45. }
  46. }
  47. } catch {
  48. postDownloadFailed()
  49. }
  50. }
  51. }

此下载类还有更多内容,因为它会在第一个 api 调用完成后下载实际文件。由于问题发生在该代码执行之前,我没有将其包含在示例代码中。

Proxyman 的日志显示 API 调用在 (minutes:seconds) 46:06,47:13,48:21,49:30,50:44,52:06,53:45 结束

看起来请求以刚超过 1 分钟的间隔重复。

有一个 API 字段,我可以在其中输入任何值,服务器会回显给我。我在那里放了一个用 CACurrentMediaTime() 生成的时间戳,并登录 Proxyman 显示它确实是相同的 API 调用,所以我的代码不可能被多次调用。似乎 iOS 网络层正在重新发出 http 请求,因为服务器需要很长时间才能响应。这最终会导致服务器出现问题并且 API 失败。

任何帮助将不胜感激。

解决方法

我认为问题在于此 API 调用使用 URLSessionConfiguration.background(withIdentifier:)。

使用此方法初始化一个配置对象,适合在应用程序在后台运行时传输数据文件。 使用此对象配置的会话将传输的控制权移交给系统,该系统在单独的进程中处理传输。在 iOS 中,即使应用程序本身已关闭,此配置也可以继续传输暂停或终止。

所以问题在于,由于这种错误的 API 使用,系统不必要地重试了您的请求。

这是我推荐的 -

  1. 使用默认会话配置(不是后台)。
  2. 执行此 API 调用以启动此长作业,不要让客户端等待此作业,一旦此作业启动,服务器端就会将 job_id 返回给客户端。
  3. 客户端现在可以使用该 job_id 值每 X 秒轮询一次服务器以了解作业的状态,甚至可以根据需要在客户端显示进度。
  4. 当作业完成,客户端下次轮询时,它会获取这个大文件的下载 URL。
  5. 下载文件(根据需要使用默认/后台会话配置)。
,

这听起来很像 TCP 重传。如果客户端发送一个 TCP 段,并且服务器在短时间内没有确认收到,则客户端假定该段没有到达目的地,并再次发送该段。这是一种比 URLSession 低得多的机制。

此 API 使用的 HTTP 服务器应用程序(例如 Apache、IIS、LigHTTPd、nginx 等)可能配置为使用响应数据进行确认,以节省打包和帧开销。如果是这样,并且如果响应数据花费的时间比客户端的 TCP 重传超时时间长,您将获得此行为。

你有连接的数据包捕获吗?如果没有,请尝试使用 tcpdump 收集一个并在 Wireshark 中查看它。如果我是对的,您会看到多个请求,并且它们都具有相同的序列号。

至于如果这是问题如何解决,我不确定。服务器应该在收到请求后立即确认。

iOS Swift 应用程序随机 EXC_BAD_ACCESS 崩溃:swift_bridgeObjectRetain swift_retain swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>

iOS Swift 应用程序随机 EXC_BAD_ACCESS 崩溃:swift_bridgeObjectRetain swift_retain swift::RefCounts

如何解决iOS Swift 应用程序随机 EXC_BAD_ACCESS 崩溃:swift_bridgeObjectRetain swift_retain swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>

我不断收到来自随机用户的随机崩溃报告。不幸的是,我无法定期重现这一点。用户说崩溃是在 discussionViewController 中随机发生的。所有崩溃报告都有类似的内容:

0   libswiftCore.dylib              0x00000001a53face4 swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::incrementSlow(swift::RefCountBitsT<(swift::RefCountInlinedness)1>,unsigned int) + 60 (atomic:1003)
1   libswiftCore.dylib              0x00000001a53c59e0 swift_retain + 124 (RefCount.h:813)
2   libswiftCore.dylib              0x00000001a5401d60 swift_bridgeObjectRetain + 56 (SwiftObject.mm:585)
3   APPNAME                             0x0000000102b59734 closure #1 in discussionViewController.fetchPostData() + 7916

这是完整的崩溃日志和崩溃的线程:

Hardware Model:      iphone11,6
Process:             APPNAME [11770]
Path:                /private/var/containers/Bundle/Application/.../APPNAME.app/APPNAME
Identifier:          ----
Version:             62 (62)
AppStoretools:       12E262
AppVariant:          1:iphone11,6:13
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           ---- [1824]


Date/Time:           2021-06-17 12:07:01.4346 +1000
Launch Time:         2021-06-17 12:06:56.4993 +1000
OS Version:          iPhone OS 14.6 (18F72)
Release Type:        User
Baseband Version:    3.04.01
Report Version:      104

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x8000000000000010 -> 0x0000000000000010 (possible pointer authentication failure)
VM Region Info: 0x10 is not in any region.  Bytes before following region: 4339515376
      REGION TYPE                 START - END      [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      UNUSED SPACE AT START
--->  
      __TEXT                   102a7c000-102a94000 [   96K] r-x/r-x SM=COW  ...APPNAME.app/APPNAME

Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL,Code 0xb
Terminating Process: exc handler [11770]
Triggered by Thread:  3


Thread 3 name:
Thread 3 Crashed:
0   libswiftCore.dylib              0x00000001a53face4 swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::incrementSlow(swift::RefCountBitsT<(swift::RefCountInlinedness)1>,unsigned int) + 60 (atomic:1003)
1   libswiftCore.dylib              0x00000001a53c59e0 swift_retain + 124 (RefCount.h:813)
2   libswiftCore.dylib              0x00000001a5401d60 swift_bridgeObjectRetain + 56 (SwiftObject.mm:585)
3   APPNAME                             0x0000000102b59734 closure #1 in discussionViewController.fetchPostData() + 7916
4   APPNAME                             0x0000000102ad09d4 thunk for @escaping @callee_guaranteed (@guaranteed Data?,@guaranteed NSURLResponse?,@guaranteed Error?) -> () + 132 (<compiler-generated>:0)
5   CFNetwork                       0x00000001a1b0a3dc __40-[__NSURLSessionLocal taskForClassInfo:]_block_invoke + 540 (LocalSession.mm:687)
6   CFNetwork                       0x00000001a1b1c768 __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 244 (LocalSessionTask.mm:584)
7   libdispatch.dylib               0x00000001a10d1a84 _dispatch_call_block_and_release + 32 (init.c:1466)
8   libdispatch.dylib               0x00000001a10d381c _dispatch_client_callout + 20 (object.m:559)
9   libdispatch.dylib               0x00000001a10db004 _dispatch_lane_serial_drain + 620 (inline_internal.h:2557)
10  libdispatch.dylib               0x00000001a10dbc34 _dispatch_lane_invoke + 456 (queue.c:3862)
11  libdispatch.dylib               0x00000001a10e64bc _dispatch_workloop_worker_thread + 764 (queue.c:6589)
12  libsystem_pthread.dylib         0x00000001ed04a7a4 0x1ed047000 + 14244
13  libsystem_pthread.dylib         0x00000001ed05174c 0x1ed047000 + 42828

我已验证 discussionViewController.fetchPostData() 不会强制解开任何可选选项,没有 try! 并且在任何地方都使用 [weak self]self?。该函数非常大,所以我很难缩小崩溃发生的范围。

iOS URLSession 简单的文件下载

iOS URLSession 简单的文件下载

import Foundation

class DownloadFileManager: NSObject, URLSessionDownloadDelegate {
    
    private var downloadedFilePath = ""
    
    private var progressHandler: ((_ progress: Float) -> ())?
    private var completionHandler: ((_ path: String) -> ())?
    
    static let shared = DownloadFileManager()
    
    private lazy var urlSession : URLSession  = {
        let session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue.main)
        return session
    }()
    
    func downloadFile(_ url: String!, progressHandler: ((_ progress: Float) -> ())?, completionHandler: ((_ path: String) -> ())?) {
        
        let downloadTask = urlSession.downloadTask(with: URL(string: url)!)
        downloadTask.resume()
        
        self.progressHandler = { (progress) in
            if let block = progressHandler {
                block(progress)
            }
        }
        
        self.completionHandler = { (path) in
            if let block = completionHandler {
                block(self.downloadedFilePath)
            }
        }
    }

    func cancel() {
        
        urlSession.getAllTasks { (downloadTasks) in
            for downloadTask in downloadTasks {
                downloadTask.cancel()
            }
        }
        
//        urlSession.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) in
//
//        }
//
//        urlSession.invalidateAndCancel()
    }
}

extension DownloadFileManager {
    
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        
        let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
        if let block = progressHandler {
            block(progress)
        }
    }
    
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let block = completionHandler {
            block(downloadedFilePath)
        }
    }
    
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        
        if let suggestedFilename = downloadTask.response?.suggestedFilename {
            downloadedFilePath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first! + "/" + suggestedFilename
            
            do {
                try FileManager.default.moveItem(at: location, to: URL(fileURLWithPath: downloadedFilePath))
            } catch { }
        }
    }
}

 

附:AFNetworking 下载

private lazy var manager : AFURLSessionManager  = {
    let manager = AFURLSessionManager.init(sessionConfiguration: .default)
    return manager
}()

func download(_ name: String, _ url: String!, progressHandler: ((_ progress: Float) -> ())?, completionHandler: ((_ path: String) -> ())?) {
    
    let task = manager.downloadTask(with: URLRequest(url: URL(string: url)!), progress: { (progress) in
        
        let p = Float(progress.completedUnitCount) / Float(progress.totalUnitCount)
        if let block = progressHandler {
            block(p)
        }
        
    }, destination: { (url, response) -> URL in
        
        let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first! + "/" + (response.suggestedFilename ?? "")
        return URL(fileURLWithPath: path)
    }) { (response, url, error) in
        
    }
    task.resume()
}

func cancel() {
    
    for task in manager.downloadTasks {
        task.cancel()
    }
}

 

 

iOS 中的 URLSession

iOS 中的 URLSession

 URLSession   

 NSURLSession 是 iOS7 中新的网络接口,与 NSURLConnection 是并列的.

    当程序在前台时,NSURLSession 和 NSURLConnection 大部分可以互相替代.NSURLSession 支持后台网络操作,除非用户强行关闭.

NSURLSession 提供的功能:

    1> 通过 URL 将数据下载到内存;   

    2> 通过 URL 将数据下载到文件系统;

    3> 将数据上传到指定的 URL;

    4> 在后台完成上述功能.

    5> 支持下载,断点续传,后台上传 / 下载,后台上传 / 下载任务跟进

对于小型数据,如用户登录,下载小图像,JSON&XML 仍然使用 NSURLConnection 的异步或同步方法即可.

NSURLSession 的使用:

    使用 NSURLSessionConfiguration 来配置 NSURLSession 对象 

    用 NSURLSession 对象来启动一个 NSURLSessionTask 对象 

    也可以使用系统全局的 sharedSession 单例来满足大多数的需求 

注意:

    相比较 NSURLConnection 的返回处理,NSURLSession 提供了灵活的数据返回方式,可以使用简单的 block 方式来处理返回数据,也可以使用更强大的 delegate.

NSURLSessionConfiguration

    用于定义和配置 NSURLSession 对象;

    每一个 NSURLSession 对象都可以设置不同的 NSURLSessionConfiguration,从而满足应用内不同类型的网络请求.

NSURLSessionConfiguration 的三种类型: 

    1> defaultSessionConfiguration: 默认的 session 配置,类似 NSURLConnection 的标准配置,使用硬盘来存储缓存数据.     

    2> ephemeralSessionConfiguration: 临时的 session 配置,与默认配置相比,这个配置不会将缓存、cookie 等存在本地,只会存在内存里,所以当程序退出时,所有的数据都会消失

    3> backgroundSessionConfiguration: 后台 session 配置,与默认配置类似,不同的是会在后台开启另一个线程来处理网络数据.

NSURLSessionTask

    NSURLSession 使用 NSURLSessionTask 来具体执行网络请求的任务.

    NSURLSessionTask 支持网络请求的取消、暂停和恢复,比如下载文件暂停之后再恢复就能够自动从上次的进度继续下载 .

    NSURLSessionTask 还能获取数据的读取进度 .

    NSURLSessionTask 的三种类型:

    1> NSURLSessionDataTask 处理一般的 NSData 数据对象,比如通过 GET 或 POST 方式从服务器获取 JSON 或 XML 返回等等,但不支持后台获取.

    2> NSURLSessionUploadTask 用于上传文件,支持后台上传 .

    3> NSURLSessionDownloadTask 用于下载文件,支持后台下载 .

通过 HTTP PUT 方法实现文件上传的步骤

    实例化 NSMutableURLRequest 并指定 HTTPMethod 为 PUT .

    设置请求的授权 :

    1> 授权字符串格式:用户名:口令 .

    2> 授权模式:Basic base64 编码的授权字符串 .

    3> 为 HTTPHeaderField 的 Authorization 赋值 .

文件上传的请求部分

// 1. URLRequest
NSURL *url = [NSURL URLWithString:@"http://localhost/uploads/xxx.png"];
NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0f];
requestM.HTTPMethod = @"PUT";
// 设置用户授权
// 1> 授权字符串(用户名+密码)
NSString *authStr = @"admin:123456";
// 2> BASE 64编码
NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64Str = [authData base64EncodedStringWithOptions:0];
NSString *auth = [NSString stringWithFormat:@"BASIC %@", base64Str];
[requestM setValue:auth forHTTPHeaderField:@"Authorization"];



我们今天的关于在 Swift 中,如何从 UrlSession 获取错误类型swift urlencode的分享已经告一段落,感谢您的关注,如果您想了解更多关于iOS Swift URLSession POST 请求因慢速 API 调用而重复、iOS Swift 应用程序随机 EXC_BAD_ACCESS 崩溃:swift_bridgeObjectRetain swift_retain swift::RefCounts、iOS URLSession 简单的文件下载、iOS 中的 URLSession的相关信息,请在本站查询。

本文标签: