Swift 同步网络请求

Posted

技术标签:

【中文标题】Swift 同步网络请求【英文标题】:Swift synchronise network request 【发布时间】:2017-12-13 10:31:39 【问题描述】:

我正在获取用户的输入并为其创建 CoreData 实体,然后放入 tableView,但在我需要通过发出几个网络请求来为实体指定一些属性之前。还有一个问题。第一个实体始终为零(并为表格视图创建一个空单元格)但如果我保存上下文并再次打开应用程序 - 在这里,实体就可以了。如果我创建第二个(等等)实体,它工作正常。它似乎也像表格总是为开始创建一个空单元格然后删除它并创建正确的单元格...... 我不明白为什么完成处理程序不能正常工作并且网络请求是异步的。请帮我理解。

创建方法从textFieldShouldReturn开始

 import UIKit
 import CoreData

 class ViewController: UIViewController, UITextFieldDelegate 

    var managedObjectContext: NSManagedObjectContext?
    lazy var fetchedResultsController: NSFetchedResultsController<Word> = 

        let fetchRequest: NSFetchRequest<Word> = Word.fetchRequest()
        let createdSort = NSSortDescriptor(key: "created", ascending: false)
        let idSort = NSSortDescriptor(key: "id", ascending: false)

        fetchRequest.sortDescriptors = [createdSort, idSort]

        var fetchedResultsController = NSFetchedResultsController(
            fetchRequest: fetchRequest,
            managedObjectContext: AppDelegate.viewContext,
            sectionNameKeyPath: nil,
            cacheName: nil)

        fetchedResultsController.delegate = self

        return fetchedResultsController
    ()

    private var hasWords: Bool 
        guard let fetchedObjects = fetchedResultsController.fetchedObjects else  return false 
        return fetchedObjects.count > 0
    

    let creator = WordManager.shared

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var messageLabel: UILabel!

    override func viewDidLoad() 
        super.viewDidLoad()

        managedObjectContext = AppDelegate.viewContext
        fetchWords()

        updateView()
    

    func updateView() 
        tableView.isHidden = !hasWords
        messageLabel.isHidden = hasWords
    
    ............

    func textFieldShouldReturn(_ textField: UITextField) -> Bool 
        guard let managedObjectContext = managedObjectContext else  return false 
        guard let title = textField.text, !title.isEmpty, !title.hasPrefix(" ") else 
            showAllert(title: "Warning!", message: "Title may not to be nil!")
            return false
        

        let word = Word(context: managedObjectContext)
        guard let fetchedWordsCount = fetchedResultsController.fetchedObjects?.count else  return false 

        creator.createWord(title: title, counter: fetchedWordsCount, word: word)
        self.updateView()

        textField.text = nil

        return true
    


    func fetchWords() 
        do 
            try fetchedResultsController.performFetch()
         catch 
            fatalError("Cant fetch words. Error \(error.localizedDescription)")
        
    

创建实体

import CoreData
import UIKit

class WordManager 
    static var shared = WordManager()
    private init() 

    private var api = DictionaryAPI()
    private var api2 = MeaningAPI()

    func createWord(title: String, counter: Int, word: Word) 

        self.api.fetch(word: title, completion:  translation in
            self.api2.fetchTranscription(word: title, completion:  transcription in

                word.id = Int16(counter) + 1
                word.title = title
                word.translation = translation
                word.transcription = "[\(transcription)]"
                word.created = NSDate()
            )
        )
    

tableView 没什么有趣的

extension ViewController: UITableViewDataSource 

func numberOfSections(in tableView: UITableView) -> Int 
    guard let sections = fetchedResultsController.sections else  return 0 
    return sections.count


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    guard let section = fetchedResultsController.sections?[section] else  return 0 
    return section.numberOfObjects


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: "mainCell", for: indexPath) as! MainCell

    let word = fetchedResultsController.object(at: indexPath)
    cell.configure(with: word)

    return cell

配置单元格方法

    func configure(with word: Word) 

        titleField.text = word.title
        translationField.text = word.translation
        transcriptionLabel.text = word.transcription
        totalCounter.text = "\(word.showCounter)"
        correctCounter.text = "\(word.correctCounter)"
        wrongCounter.text = "\(word.wrongCounter)"

获取结果委托

extension ViewController: NSFetchedResultsControllerDelegate 

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) 
    tableView.beginUpdates()


func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) 
    tableView.endUpdates()
    updateView()


func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) 

    switch type 
    case .delete:
        guard let indexPath = indexPath else  return 
        tableView.deleteRows(at: [indexPath], with: .fade)
    case .insert:
        guard let indexPath = newIndexPath else  return 
        tableView.insertRows(at: [indexPath], with: .fade)
    case .move:
        guard let indexPath = indexPath else  return 
        tableView.deleteRows(at: [indexPath], with: .fade)
        guard let newIndexPath = newIndexPath else  return 
        tableView.insertRows(at: [newIndexPath], with: .fade)
    default:
        break
    

【问题讨论】:

【参考方案1】:

问题:

您在核心数据中的数据未处于可以在应用中显示的状态。 NSFetchedResultsController 恰好选择了这些数据并将其显示在应用程序中。

方法:

当用户创建记录时,用户希望立即在应用程序中看到更改,而不必等待应用程序在服务器中更新以查看更改。因此,即使没有网络连接,用户仍然可以创建记录。 从视图上下文创建子上下文 在将记录更新到子上下文时,使用后台线程上传到服务器。 仅当数据处于一致状态时才保存子上下文。 NSFetchedResultsController 应该使用视图上下文,因此不会知道子上下文记录。 当您保存子上下文时,NSFetchedResultsController 会选择这些记录。

【讨论】:

问题是我不需要 CoreData 中的不良记录。

以上是关于Swift 同步网络请求的主要内容,如果未能解决你的问题,请参考以下文章

Swift 3 中的顺序网络请求

如何调试网络请求 Swift

Swift:链接多个网络请求,Alamofire

使用 Swift 和 Combine 链接 + 压缩多个网络请求

使用 Swift 4 的简单网络请求

为啥我的 Swift 网络请求不起作用?