*** Завершение работы приложения из-за необработанного исключения «NSInternalInconsistencyException», причина: «Неустранимо: предоставленные идентификаторы не являются уникальными». *** - PullRequest
0 голосов
/ 28 февраля 2020

Привет всем! Я анализирую следующие значения JSON и UITableView, используя UITableViewDiffableDataSource для хорошей анимации поиска.

JSON: https://www.pathofexile.com/api/trade/data/items

А вот репо: https://github.com/laurentdelorme/PathOfData

Из JSON, Я могу загрузить все 13 различных категорий, как показано в файле модели. Затем я могу скопировать sh данные из этих категорий в другой tableView (тот, который использует UITableViewDiffableDataSource) и отобразить все мило.

Однако есть ОДНА категория, в которой мое приложение делает sh, когда я пытаюсь передать sh его содержимое в DetailViewController, который является категорией «Карты» в начальном ViewController.

Вот моя модель:

struct ItemCategories: Codable {
    var result: [ItemCategory]
}

struct ItemCategory: Codable {
    var label: String
    var entries: [Item]
}

struct Item: Codable, Hashable {
    var name: String?
    var type: String?
    var text: String?
}

Вот мой ViewController:

import UIKit

class ViewController: UITableViewController {

    let urlString = "https://www.pathofexile.com/api/trade/data/items"
    var categories = [ItemCategory]()


    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Path of Data"
        navigationController?.navigationBar.prefersLargeTitles = true
        parseJSON()

        for family: String in UIFont.familyNames
        {
            print(family)
            for names: String in UIFont.fontNames(forFamilyName: family)
            {
                print("== \(names)")
            }
        }
    }

    func parseJSON() {
        guard let url = URL(string: urlString) else { return }
        guard let data = try? Data(contentsOf: url) else { return }

        let decoder = JSONDecoder()

        guard let jsonItemCategories = try? decoder.decode(ItemCategories.self, from: data) else { return }

        categories = jsonItemCategories.result
        tableView.reloadData()
    }


    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return categories.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        var categoryName = categories[indexPath.row].label
        if categoryName == "" { categoryName = "Unknown" }
        cell.textLabel?.text = categoryName

        let font = UIFont(name: "Fontin-SmallCaps", size: 30)
        cell.textLabel?.font = font
        cell.textLabel?.textColor = .systemOrange

        let numberOfItemsInCategory = String(categories[indexPath.row].entries.count)
        cell.detailTextLabel?.text = numberOfItemsInCategory + " items"
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        if let vc = storyboard?.instantiateViewController(identifier: "Detail") as? DetailViewController {

            let listLabel: String? = categories[indexPath.row].label
            vc.title = listLabel

            let itemList = categories[indexPath.row].entries
            vc.items = itemList

            print(itemList)

            vc.category = categories[indexPath.row].label

            navigationController?.pushViewController(vc, animated: true)
        }
    }
}

Вот DetailViewController:

import UIKit
import SafariServices

class DetailViewController: UITableViewController {

    enum Section {
        case main
    }

    var category: String!
    var items: [Item] = []
    var transformedItems: [Item] = []
    var filteredItems: [Item] = []

    var isSearching: Bool = false

    var dataSource: UITableViewDiffableDataSource<Section,Item>!

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController?.navigationBar.prefersLargeTitles = true
        navigationController?.navigationBar.tintColor = .systemOrange
        replacenNilNameFor(items: items)
        configureDataSource()
        updateData(on: items)
        congifureSearchController()
    }

    func replacenNilNameFor(items: [Item]) {
        for item in items {
            if item.name == nil {
                guard item.type != nil else { return }
                let newItem = Item(name: item.type, type: nil, text: nil)
                transformedItems.append(newItem)
            } else {
                transformedItems.append(item)
            }
        }
        self.items = transformedItems
    }

    func configureDataSource() {
        dataSource = UITableViewDiffableDataSource<Section, Item>(tableView: self.tableView, cellProvider: { tableView, indexPath, item -> UITableViewCell? in
            let cell = tableView.dequeueReusableCell(withIdentifier: "Detail", for: indexPath)
            cell.textLabel?.text = item.name
            cell.detailTextLabel?.text = item.type

            let font = UIFont(name: "Fontin-SmallCaps", size: 25)
            cell.textLabel?.font = font
            cell.textLabel?.textColor = self.setLabelColor(for: self.category)

            return cell
        })
    }

    func setLabelColor(for category: String) -> UIColor {
        switch category {
        case "Prophecies":
            return UIColor(red: 0.6471, green: 0.1569, blue: 0.7569, alpha: 1.0)
        default:
            return UIColor(red: 0.6392, green: 0.549, blue: 0.4275, alpha: 1.0)
        }
    }

    func updateData(on items: [Item]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        snapshot.appendSections([.main])
        snapshot.appendItems(items)
        dataSource.apply(snapshot, animatingDifferences: true)
    }

    func congifureSearchController() {
        let searchController = UISearchController()
        searchController.searchResultsUpdater = self
        searchController.searchBar.placeholder = "Search for an item"
        searchController.searchBar.delegate = self
        navigationItem.searchController = searchController
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let endpoint = "https://pathofexile.gamepedia.com/"

        let activeArray = isSearching ? filteredItems : items
        let item = activeArray[indexPath.row]

        let url = URL(string: endpoint + formatNameFor(item: item))
        let sf = SFSafariViewController(url: url!)
        present(sf, animated: true)
    }

    func formatNameFor(item: Item) -> String {
        let name = item.name!
        let firstChange = name.replacingOccurrences(of: " ", with: "_")
        let secondChange = firstChange.replacingOccurrences(of: "'", with: "%27")
        return secondChange
    }
}




extension DetailViewController: UISearchResultsUpdating, UISearchBarDelegate {

    func updateSearchResults(for searchController: UISearchController) {
        guard let filter = searchController.searchBar.text, !filter.isEmpty else { return }
        isSearching = true
        filteredItems = items.filter { ($0.name?.lowercased().contains(filter.lowercased()))! }
        updateData(on: filteredItems)
    }

    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        isSearching = false
        updateData(on: items)
    }
}

И вот сообщение об ошибке, которое я получаю когда я пытаюсь войти в категорию «Карты»:

2020-02-28 14:40:20.470098+0100 PathOfData[2789:224548] *** Assertion failure in -[_UIDiffableDataSourceUpdate initWithIdentifiers:sectionIdentifiers:action:desinationIdentifier:relativePosition:destinationIsSection:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3901.4.2/_UIDiffableDataSource.m:1417
2020-02-28 14:40:20.474313+0100 PathOfData[2789:224548] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Fatal: supplied identifiers are not unique.'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001069f327e __exceptionPreprocess + 350
    1   libobjc.A.dylib                     0x0000000105077b20 objc_exception_throw + 48
    2   CoreFoundation                      0x00000001069f2ff8 +[NSException raise:format:arguments:] + 88
    3   Foundation                          0x0000000104a9fb51 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
    4   UIKitCore                           0x0000000119c4dcdf -[_UIDiffableDataSourceUpdate initWithIdentifiers:sectionIdentifiers:action:desinationIdentifier:relativePosition:destinationIsSection:] + 725
    5   UIKitCore                           0x0000000119c4e04e -[_UIDiffableDataSourceUpdate initWithItemIdentifiers:appendingToDestinationSectionIdentifier:] + 90
    6   UIKitCore                           0x0000000119c43408 -[__UIDiffableDataSource appendItemsWithIdentifiers:intoSectionWithIdentifier:] + 165
    7   libswiftUIKit.dylib                 0x0000000105e9f061 $s5UIKit28NSDiffableDataSourceSnapshotV11appendItems_9toSectionySayq_G_xSgtF + 241
    8   PathOfData                          0x0000000104723b41 $s10PathOfData20DetailViewControllerC06updateC02onySayAA4ItemVG_tF + 369
    9   PathOfData                          0x000000010472231f $s10PathOfData20DetailViewControllerC11viewDidLoadyyF + 767
    10  PathOfData                          0x00000001047223db $s10PathOfData20DetailViewControllerC11viewDidLoadyyFTo + 43
    11  UIKitCore                           0x0000000119e22f01 -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 83
    12  UIKitCore                           0x0000000119e27e5a -[UIViewController loadViewIfRequired] + 1084
    13  UIKitCore                           0x0000000119e28277 -[UIViewController view] + 27
    14  UIKitCore                           0x0000000119d773dd -[UINavigationController _startCustomTransition:] + 1039
    15  UIKitCore                           0x0000000119d8d30c -[UINavigationController _startDeferredTransitionIfNeeded:] + 698
    16  UIKitCore                           0x0000000119d8e721 -[UINavigationController __viewWillLayoutSubviews] + 150
    17  UIKitCore                           0x0000000119d6f553 -[UILayoutContainerView layoutSubviews] + 217
    18  UIKitCore                           0x000000011a98c4bd -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2478
    19  QuartzCore                          0x000000010bbe7db1 -[CALayer layoutSublayers] + 255
    20  QuartzCore                          0x000000010bbedfa3 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 517
    21  QuartzCore                          0x000000010bbf98da _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 80
    22  QuartzCore                          0x000000010bb40848 _ZN2CA7Context18commit_transactionEPNS_11TransactionEd + 324
    23  QuartzCore                          0x000000010bb75b51 _ZN2CA11Transaction6commitEv + 643
    24  UIKitCore                           0x000000011a4d03f4 _afterCACommitHandler + 160
    25  CoreFoundation                      0x0000000106955867 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    26  CoreFoundation                      0x00000001069502fe __CFRunLoopDoObservers + 430
    27  CoreFoundation                      0x000000010695097a __CFRunLoopRun + 1514
    28  CoreFoundation                      0x0000000106950066 CFRunLoopRunSpecific + 438
    29  GraphicsServices                    0x0000000109100bb0 GSEventRunModal + 65
    30  UIKitCore                           0x000000011a4a6d4d UIApplicationMain + 1621
    31  PathOfData                          0x000000010471fe6b main + 75
    32  libdyld.dylib                       0x00000001078c5c25 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)

Я не могу понять, что здесь происходит, поэтому, если у кого-то есть идея, это было бы здорово ?

Спасибо тебе очень нравится!

Ответы [ 2 ]

1 голос
/ 28 февраля 2020

Ошибка явно связана с UIDiffableDataSource.

Для источника данных, доступного для преобразования, требуются уникальные значения ha sh идентификаторов элементов. Очевидно, что есть два элемента с одинаковыми name, type и text.

. Чтобы обеспечить уникальное значение ha sh, добавьте свойство uuid и используйте только это свойство для га sh значение (реализовать методы протокола). Для правильного декодирования Item необходимо указать CodingKeys, чтобы предотвратить декодирование свойства uuid.

struct Item: Codable {
    let uuid = UUID()

    private enum CodingKeys : String, CodingKey { case name, type, text }

    var name: String?
    var type: String?
    var text: String?
}

extension Item : Hashable {
    static func ==(lhs: Item, rhs: Item) -> Bool {
        return lhs.uuid == rhs.uuid
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(uuid)
    }
}

В iOS 13+ вы можете принять Identifiable, чтобы избавиться от расширения Hashable

struct Item: Codable, Identifiable {
    let id = UUID()

    private enum CodingKeys : String, CodingKey { case name, type, text }

    var name: String?
    var type: String?
    var text: String?
}

И вам настоятельно не рекомендуется загружать данные синхронно с Data(contentsOf:, Не делай этого . Используйте асинхронный URLSession

0 голосов
/ 28 февраля 2020

В вашей раскадровке есть повторяющиеся значения идентификаторов для UIViewController. Поиск текста Detail в ваших раскадровках и установка различных идентификаторов для различных контроллеров UIView.

Например Detail1 и Detail2

...