2017-04-24 6 views
0

私のアプリは、アプリのいくつかの異なる場所から私のウェブサイトからファイルをダウンロードする必要があるので、ダウンロードを一度達成し、独自のクラスに入れ、各ViewControllerからその関数を呼び出す関数を書くのが理にかなっているようです。これまでのところ、とてもうまくいっています。ダウンロードが行われており、ダウンロードしたファイルは正しくprintになります。複数のView Controllerからファイルダウンロード機能を呼び出します。結果をVCに返す方法は?

ダウンロード機能が呼び出されたViewControllerに「成功」​​または「失敗」メッセージを送信して、VCがそれに応じて反応するようになると、問題が発生します。表示を更新し、ダウンロードダイアログを閉じます、 なんでも。それが起こるようにするには、私は立ち往生しています。私が持っているもの

(私のサーバーから別のファイルを要求する以外に今のところ同一である、)ViewControllerTwoとViewControllerThreeのそれぞれは、このようにダウンロード機能を呼び出します。

Downloader.load(url: urlForFileA!, to: localDestinationFileA, callingViewControllerNumber: 2) 

ためのコード(現在同期しているが、最終的に非同期になります)ダウンローダ機能は、(独自のDownloaderクラスで)次のようになります。

class func load(url: URL, to localUrl: URL, callingViewControllerNumber: Int) { 
    let sessionConfig = URLSessionConfiguration.default 
    let session = URLSession(configuration: sessionConfig) 
    let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData) 

    let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in 
     if let tempLocalUrl = tempLocalUrl, error == nil { 
      // Got a file, might be a 404 page... 
      if let statusCode = (response as? HTTPURLResponse)?.statusCode { 
       print("Success downloading: \(statusCode)") 
       if statusCode == 404 { 
        // ERROR -- FILE NOT FOUND ON SERVER 
        returnToCaller(sourceIdent: callingViewControllerNumber, successStatus: .fileNotFound, errorMessage: "File not Found, 404 error") 
       } 
      } 
      do { 
       try FileManager.default.copyItem(at: tempLocalUrl, to: localUrl) 
       // SUCCESS! 
       returnToCaller(sourceIdent: callingViewControllerNumber, successStatus: .success, errorMessage: "") 
      } catch (let writeError) { 
       returnToCaller(sourceIdent: callingViewControllerNumber, successStatus: .movingError, errorMessage: "\(writeError)") 
      } 
     } else { 
      returnToCaller(sourceIdent: callingViewControllerNumber, successStatus: .downloadFailed, errorMessage: "Grave Unexplained Failure") 
     } 
    } 
    task.resume() 
} 

この部分は動作します。

returnToCaller機能を呼び出すのViewControllerに戻って何かを送信するために(大丈夫、非常に、非常に醜い)確かに醜い方法です:

class func returnToCaller(sourceIdent : Int, successStatus : downloadSuccessStatusEnum, errorMessage : String) { 
    switch sourceIdent { 
    case 2: 
     ViewControllerTwo().returnFromDownload(successStatus: successStatus, errorMessage: errorMessage) 
    case 3: 
     ViewControllerThree().returnFromDownload(successStatus: successStatus, errorMessage: errorMessage) 
    default: 
     fatalError("Unknown sourceIdent of \(sourceIdent) passed to func returnToCaller") 
    } 
} 

問題がでたときにそのreturnFromDownload機能ということですオリジナルのViewControllerが呼び出され、ロードされているVCの何も認識しません。ラベルの背景色を変更して、ラベルがnilであるランタイムエラーを取得します。ラベルは存在しますが、ViewControllerコードへのこの呼び出しは、実行中のインスタンス化されたVC自体から独立して行われます。 (おそらく間違ったボキャブラリーがあります。謝罪します。)コードは実行され、printでも可能ですが、View自体の何かと対話するときにエラーが出る可能性があります。

私はこのことをよりうまく使うほど、私はここで正しい方向に向いていると自信がなくなり、Swiftでの私の限られた経験は、何が起こって、ダウンロード機能がすべての作業を「ここで」実行し、呼び出し元のVCに成功/失敗のメッセージを返して、VCがそれを処理できるようにします。

This question同様の質問をしているようです。そこにある1つの答えは、VCの内部で起こったことの結果を使って、提示されたVC内でコードを取得する方法の根本的な問題(自分の場合はマネージャの承認、私のダウンロード) 。

私のコードを書き直すように求めるのではなく(クイックフィックスでない限り)、正しい方向を指すようにする必要があります。どうもありがとう!

+1

私がこれを正しく読んでいれば、その日に戻って欲しいのは、「疎結合」コードと呼ばれていました。プレスイフトの頃には、別の名前がKVCコーディングである可能性があります。基本的には、何かを非同期的にダウンロードしようとしています*、それを呼び出したVCに結果を返します。少なくとも2つの方法が考えられます。 (1)恐らく最も正しい方法は、デフォルトのNSNotificationCenter(http://stackoverflow.com/questions/24049020/nsnotificationcenter-addobserver-in-swift)にオブザーバーを追加することです。 (2)別の方法 - それほどエレガントではありませんが、仕事を終わらせるかもしれません - UIButtonのsendActionメソッドを使うことです。 – dfd

答えて

1

あなたが望むものは、クロージャで簡単に達成できます。

class func load(url: URL, to localUrl: URL, completion: (downloadSuccessStatusEnum, String) -> Void)

これはあなたの方法はとても似完了時にあなたがreturnToCallerの代わりにcompletionを呼び出すことができるようになります:

最初のステップは、あなたのloadメソッドに別のパラメータを追加し、callingViewControllerのparamを削除することです

DispatchQueue.main.async { 

    completion(.success, "Insert your error message here") 
} 

最後に、このメソッドを呼び出すには、VCで次のようなメソッドを呼び出すだけです。

Downloader.load(url: nameOfYourURL, to: destinationName) { statusEnum, errorString in 
    // Insert your code you want after the request completes here 
} 
+0

非常に良い!コンパイラは、クロージャがエスケープする必要があり、[この回答]に基づいて提案するようだ(http://stackoverflow.com/questions/42214840/swift-3-closure-use-of-non-escaping-parameter-may-エスケープを許可する)、それは理にかなっているようです。同意しますか? – ConfusionTowers

+0

それは動作します!非常に奇妙なのは、要求が完了した後に実行されるコードで、成功したリターンのためにラベルの背景を緑色に変更した後、ファイルの内容(平方テキストの数ページ)を 'print'し、直ちに、ラベルが色を変える(シミュレータでは少なくとも)までに約5秒以上かかる。ビューがすぐに反応するように私がすべきことは何ですか?ご協力いただき誠にありがとうございます! – ConfusionTowers

+0

応答がメインスレッドで返されていることを確認します。そうでない場合は、メインスレッドに送信します。 – AdamPro13

関連する問題