

iOS+URLSessionでHTTPServerにリクエストして、  
応答データが事前に設定したサイズ以内なら受信を継続、  
超えた場合はそこで処理を終了させるプログラムを作りたいと考えています。

方法として、受信ヘッダが取れたところでContents-lengthを参照し、  
この値で受信処理継続の要否を判断する方法を考えました。

ヘッダ受信のタイミングは、URLSessionDataDelegateのurlSessionを使おうと考えたのですが、  
そもそもURLSessionDataDelegateのurlSessionが呼び出されないところで躓いています。  
TaskDelegateでリダイレクトなどのタイミングは取れているのでDelegeteの使い方は大丈夫…、だと思っています。

・使い方が悪いところ  
・違う方法で目的は実現できる  
などありましたらご教示いただけますようお願いします。

[code]

    class httpAccess2:NSObject{
        private var queue = OperationQueue()
        private var session:URLSession?
        /// 初期化処理
        override init() {
            super.init()
        }
    
        func perform()->Bool{
    
            // URLクラスを作る（OPT_URL/OPT_USERPWDの設定）
            guard let url = URLComponents(string: "http://192.168.10.6:8000/test002.html")?.url else {return false}
    
            // URLRequestクラスを作る
            let urlRequest = URLRequest(url:url)
    
            //デフォルトの設定値を取得
            let config = URLSessionConfiguration.default
            //セッションを作成
            self.session = URLSession(configuration:config,delegate: self,delegateQueue: queue)
    
            //タスクと完了後のコールバック(クロージャ)を定義
            let task = session!.dataTask(with: urlRequest){(data,response,error) in
                    print(response!)
            }
            task.resume()
    
            return true
        }
    }
    
    extension httpAccess2:URLSessionDataDelegate{
        func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
            print("[URLSessionDataDelegate]didReceive response");
            print(response)
            print("¥n")
        }
    }
    extension httpAccess2:URLSessionTaskDelegate{
    
        func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
            print("[URLSessionTaskDelegate]didFinishCollecting metrics");
            print(metrics)
            print("¥n")
        }
    }
    
[/code]


CC BY-SA 4.0
2020-06-05T01:30:57.503
67305
2022-11-28T06:06:50.810
2020-06-05T01:42:23.033
question
swift ios nsurlsession
URLSessionで受信するデータのサイズを事前に判断したい。


`URLSessionDataDelegate`のメソッドを呼ばれるようにしたいのであれば、完了ハンドラ付きの`dataTask(with:completionHandler:)`を使用してはいけません。

###
[`dataTask(with:completionHandler:)`](https://developer.apple.com/documentation/foundation/urlsession/1407613-datatask)

> By using the completion handler, the task bypasses calls to delegate methods
> for response and data delivery

さらに、完了ハンドラ無しで受信データを取り出すためには、`urlSession(_:dataTask:didReceive:)`と`urlSession(_:task:didCompleteWithError:)`を正しく実装してやらないといけません。

さらにさらに、`urlSession(_:dataTask:didReceive:completionHandler:)`の実装では、処理の最後に必ず`completionHandler`を呼んでやらないといけません。

と言うわけで`URLSessionDataDelegate`を実装する部分はこんな風になるでしょう。

[code]

    extension HttpAccess2: URLSessionDataDelegate {
    
        func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
            print("[URLSessionDataDelegate]didReceive response");
            print(response)
            print()
            let unknownLength: Int64 = -1 //<- ObjC用の定数マクロ`NSURLResponseUnknownLength`が使えないので自前で定義
            if response.expectedContentLength != unknownLength
            && response.expectedContentLength > MAX_CONTENT_LENGTH {
                completionHandler(.cancel) //<-受信を中断するなら`.cancel`
            } else {
                completionHandler(.allow) //<-受信を続けるなら`.allow`
            }
        }
    
        func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
            print(#function, data.count)
            //途中小分けにしてデータが送られてくるので自分で積み上げる必要がある
        }
    
        func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
            print(#function)
            if let error = error {
                print(error)
                return
            }
            //自分で積み上げたデータを完了後に利用する
        }
    }
    
[/code]

(Swiftでは、「型名は大文字で始める」と言う非常によく守られているルールがあるので、クラス名を修正しました。どうしてもクラス名を変更できないのであれば、読み替えてください。)

dataTaskを作成する部分は完了ハンドラを使わずにこんな風になります。

[code]

            //タスクを定義
            let task = session!.dataTask(with: urlRequest)
            task.resume()
    
[/code]

なお、HTTPの特性として、レスポンスの`Content-
Length`フィールドは必ずしも設定されているとは限りませんので、その場合の対応もしておく必要があります。


CC BY-SA 4.0
2020-06-05T02:51:14.880
67306
2020-06-05T07:37:57.337
2020-06-05T07:37:57.337
13972
13972
67305
answer