2017-04-24 39 views
0

我的應用程序需要從應用程序的幾個不同位置下載我的網站上的文件,因此編寫函數以完成下載一次,將其放入其自己的類並從每個ViewController調用該函數似乎是有意義的。到目前爲止,很好,一切正常。正在下載,正確地下載了文件print從多個視圖控制器調用文件下載功能;如何將結果返回給那些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功能是不可否認的難看的(好吧,非常非常醜陋的)的方式來送東西回調用視圖控制器:

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,但在與視圖本身中的任何內容進行交互時出錯。

我對此的處理越多,我就越不自信,我在這裏的正確軌道上,而我對Swift的有限經驗還不足以看到需要發生什麼,以便下載功能可以做到所有的工作「在這裏」,然後向調用的VC返回成功/失敗消息,以便VC可以使用它。

This question似乎在問類似的東西;那裏的一個答案並沒有解決我如何在提交的VC中獲取代碼的根本問題,這個問題再次發生在VC之外發生的結果(經理批准他的情況下,我的下載) 。

不要求我的代碼被重寫(除非它是一個快速修復),但需要指向正確的方向。非常感謝!

+1

如果我正確地閱讀這篇文章,您希望 - 當天回來 - 被稱爲「鬆散耦合」代碼。在Swift之前的日子裏,另一個名字可能是KVC編碼。基本上,你想嘗試下載*異步*,並將結果發回給調用它的VC。至少有兩種方法可以想到。 (1)可能最正確的方法是將觀察者添加到默認NSNotificationCenter(http://stackoverflow.com/questions/24049020/nsnotificationcenter-addobserver-in-swift)。 (2)另一種方式 - 不太優雅,但它可能完成工作 - 是使用UIButton的sendAction方法。 – dfd

回答

1

你想要的東西可以很容易地用閉包完成。

的第一步是到另一個參數添加到您的load方法和刪除您callingViewController PARAM:

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

這將允許你打電話completion,而不是returnToCaller當你的方法完成,像這樣:

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

這是有效的!非常奇怪的是,在請求完成後運行的代碼中,爲了成功返回,我將標籤的背景更改爲綠色,然後「打印」文件內容(幾頁純文本),並在打印發生時立即,標籤更改顏色(至少在模擬器中)前大約需要5秒鐘。我應該做的任何事情,以便視圖立即作出反應?非常感謝您的幫助! – ConfusionTowers

+0

檢查以確保您的響應正在主線程上返回。如果不是,則需要將其發送到主線程。 – AdamPro13

相關問題