Swift同步网络请求

时间:2017-12-13 10:31:40

标签: ios swift uitableview core-data

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

创建元词从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
    }
}

}

enter image description here

1 个答案:

答案 0 :(得分:0)

问题:

  • 核心数据中的数据未处于准备好在应用中显示的状态。
  • NSFetchedResultsController恰好选择此数据并在应用中显示。

的方法:

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