質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
FTP

FTP(File Transfer Protocol)は、ネットワークでのファイル転送を行うための通信プロトコルの1つである。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

2回答

2855閲覧

【Xcode/Swift/iPad/FTP】URLSessionで切断によるタイムアウト発生後、復帰しない

退会済みユーザー

退会済みユーザー

総合スコア0

FTP

FTP(File Transfer Protocol)は、ネットワークでのファイル転送を行うための通信プロトコルの1つである。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/10/28 22:04

編集2021/11/04 00:33

前提・実現したいこと

・URLSessionを使いFTPでファイルをダウンロードする。
・物理的な接続断等に対応するため、5秒のタイムアウトを設ける。

発生している問題・エラーメッセージ

タイムアウト発生後、接続を回復し、再度ダウンロード実行してもタイムアウトエラーとなる。
アプリケーションを再起動するまで治らない。
・HTTPアクセスは可能(Alamofire使用)なので、通信はできている。
・タイムアウトを発生させなければ、何度でもダウンロード可能。

Task <******>.<*> finished with error [-1001] Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."

該当のソースコード

Swift

1 2 // FTP処理呼び出し(関数名などは省略、呼び出し部のみ) 3 // Lists:ダウンロードファイル名一覧 4 DispatchQueue.global(qos: .background).async { [weak self] in 5 for i in 0..<self!.Lists.count 6 { 7 if !self!.isFileExist(self!.Lists[i]) 8 { 9 self?.ftpRequest(self!.Lists[i]) 10 if self!.ftpStatus == .error 11 { 12 break; 13 } 14 } 15 } 16 17 DispatchQueue.main.async { 18 if self?.ftpStatus == .error 19 { 20 // ファイルのダウンロードに失敗 21 } 22 else 23 { 24 // something else... 25 } 26 } 27 } 28 29  // FTPダウンロード処理 30 // URLは適当です 31 func ftpRequest(_ file: String) 32 { 33 let url = URL(string: "ftp://hoge:hoge@192.168.1.100/" + file)! 34 35 // 5秒のタイムアウトとする 36 let request = URLRequest(url: url,timeoutInterval: 5) 37 let session = URLSession(configuration: .default,delegate: nil,delegateQueue: OperationQueue.main) 38 39 let task = session.dataTask(with: request) { (data,response,error) in 40 session.finishTasksAndInvalidate() 41 42 //guardにはelseが必要 43 //guard let error = error 44 if let error = error 45 { 46 self.ftpStatus = .error 47 self.semaphore.signal() 48 return 49 } 50 51 guard let data = data else 52 { 53 self.ftpStatus = .error 54 self.semaphore.signal() 55 return 56 } 57 58 // (省略) data を保存する処理など 59 self.ftpStatus = .pass 60 self.semaphore.signal() 61 } 62 task.resume() 63 self.semaphore.wait() 64 }

試したこと

・問題発生中のターゲット(iPad)に、他のPCからFTP Clientを使いダウンロードすると、ダウンロードできる。
・その後、iPadからダウンロードを試しても、やっぱりだめ。
・アプリケーションを再起動すると、ダウンロード可能になる。

このことから、アプリ側で
・何かのセッション/タスクが残っている
と考えられますが、session.finishTasksAndInvalidate()も呼んでいるし、原因が分からずです。

FTPはもう使わない方がいいというのは理解してますが、
相手(サーバー側)が変えれないので、仕方なく使用しています。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

通信エラー発生有無の判定が逆になっています。

diff

1 let task = session.dataTask(with: request) { (data,response,error) in 2 session.finishTasksAndInvalidate() 3- guard let error = error 4- { 5+ if let error = error { 6 self.ftpStatus = .error 7 self.semaphore.signal() 8 return 9 }

投稿2021/11/03 09:54

__k_san__

総合スコア177

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/11/03 10:22

@__k_san__ ご回答ありがとうございます。 errorがnilでなければエラーが発生しているので、エラーで抜けると言う意図ですが、ifとguardで、判定変わるのでしょうか?? また、判定が反対だったとすると、通信成功しても常にエラーになるのではないでしょうか? すいません、ifとguardの違いと、今回の問題の関連性が分からないので、教えて頂ければ、幸いです。
__k_san__

2021/11/03 11:16 編集

ifとguardでは判定が変わります。 guard let error = error else { ... } では、errorの値がnilの場合にカッコの中の処理を実行します。 それに対し、 if let error = error { ... } では逆に、errorの値がnil以外の場合にカッコの中の処理を実行します。 そのため、指摘箇所の判定ではguard文ではなくif文を使用すべきなのだと思います。 この部分の判定が意図したものとは逆になることにより、実際には通信成功していてもエラーとして扱われ、実際に通信失敗している場合には、dataの値がnilであるために後続の guard let data = data else { ... } でカッコの中に入ってしまうのではないかと予想しています。 今回の問題とどこまで関係しているかは把握しきれていないのですが、もしかしたらと思い回答させていただきました。見当違いでしたら申し訳ありません。
退会済みユーザー

退会済みユーザー

2021/11/04 00:11 編集

@__k_san__さん ご回答ありがとうございます。 が、混乱してきました・・・・。 ifとguardで判定が変わる、というのが理解できないです。 guard let error = error else { ... } if let error = error else { ... } 上記の場合でerrorがnilだと、どちらも{...}が処理されるのではないでしょうか? ifとguardで判定が変わるのではなく、elseの有無で変わるのではないでしょうか? そもそも、guard は else 無しで書かないものなのでしょうか・・・。 ネットの情報だと、ifよりguardを使うべき等書かれてありましたので、もっと勉強してみます。 元の問題に戻ると、 >・タイムアウトを発生させなければ、何度でもダウンロード可能。 通常時は、self.ftpStatus = .passで返ってきます。 その状態から一度タイムアウトを発生させた後が、ダメなんです。。。 >今回の問題とどこまで関係しているかは把握しきれていないのですが、もしかしたらと思い回答させて >いただきました。見当違いでしたら申し訳ありません。 いえいえ、ご回答いただけるだけでも、本当に助かりますし、勉強になります。
退会済みユーザー

退会済みユーザー

2021/11/04 00:30

@__k_san__さん >そもそも、guard は else 無しで書かないものなのでしょうか・・・。 guard ~ はelse必須のようですね。勉強不足すいません。 実は、提示しているコードは、もともとifで書いていたのですが、 「ifよりguardを使うべき等書かれてありました」を受けて、 動作確認もせず、投稿時に書き換えてしまったものです。 申し訳ありません。 今、Playgroundで確認したら、guardの後にelseがない!とエラーを出してくれました。。。 ちなみに、ifに変えても問題は発生しています。 コードは修正しておきます。
guest

0

未処理のタスクを全て終了するメソッド「finishTasksAndInvalidate」に対して
未処理のタスクを全てキャンセルするメソッド「invalidateAndCancel」というのもあるようです。
終了とキャンセルの何が違うのか分かりませんが試してみてはいかがでしょうか?

投稿2021/10/29 04:50

takk_014

総合スコア53

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/10/29 04:53

@takk_014 さん ご回答ありがとうございます。 はい、InvalidateAndCancelも試してみましたが、ダメでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問