Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
643 views
in Technique[技术] by (71.8m points)

swift - Xcode Beta 6 "Type of expression is ambiguous without more context" navigationlink

Since updating to Xcode Beta 6 earlier today my application will no longer build, it was working fine in Beta 5 and earlier.

This is the code from the file with the error message, although I'm aware at the moment this doesn't necessarily mean this is where the error actually lies.

import SwiftUI

struct JobView_Table : View {

    @ObservedObject var jobList: JobDetailViewModel = JobDetailViewModel()

    var body: some View {

        NavigationView {

            List {
                ForEach($jobList.jobDetails) { job in
                    NavigationLink(destination: JobDetailHost(jobDetails: job)) { // ERROR: "Type of expression is ambiguous without more context"
                        JobView_List(jobDetails: job)
                    }
                }
            }

            .navigationBarTitle(Text("My Jobs"))
            .onAppear(perform: fetchData)
            .onAppear(perform: {
                print("Hello!")
            })

        }
    }

    private func fetchData() {
        return(jobList.updateDetails())
    }
}

The struct containing the data conforms correctly to the following protocols.

struct JobDetails: Codable, Identifiable, Equatable, Hashable { 
    ...

    ...
}

This is the class which provides the data to JobView_Table.

import Foundation
import UIKit
import Combine

class JobDetailViewModel: ObservableObject, Identifiable {

    @Published var jobDetails: [JobDetails] = []

    func updateDetails() {
        self.jobDetails = DataManager().fetchJobList()
    }

}

And finally the target view which is linked to via the NavigationLink.

struct JobDetailHost: View {

    @Environment(.editMode) var mode
    @Binding var jobDetails: JobDetails


    var body: some View {

        VStack {
            JobDetailView(jobDetails: jobDetails)
        }
        .navigationBarItems(trailing: EditButton())
    }

}

I notice that some others seem to be having similar problems, i.e. in the two questions listed below, but exploring the answers in these questions is not helping me at this moment. SwiftUI Xcode 11 beta 5 / 6: Type of expression is ambiguous without more context

SwiftUI: Why does ForEach($strings) (text: Binding) not build?

EDIT:

I have tried implementing the suggestion from Fabian, this has got rid of the error, however no content is being populated in the list.

This is the adjusted List code, which compiles successfully but when running the application the list is not populated.

List {
    ForEach(jobList.jobDetails.indexed(), id: .1.id) { (index, job) in
            NavigationLink(destination: JobDetailHost(jobDetails: self.$jobList.jobDetails[index])) {
                        Text(job.jobName)
                    }
     }
}

The following does not use the ForEach and discards the NavigationLink, and still doesn't work.

List(jobList.jobDetails.indexed(), id: .1.id) { (index, job) in
                Text(job.jobName)
            }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'll quote from the macOS Catalina 10.15 Beta 6 Release Notes:

The Binding structure’s conditional conformance to the Collection protocol is removed. (51624798)

If you have code such as the following:

struct LandmarkList: View {
    @Binding var landmark: [Landmark]

    var body: some View {
        List(landmarks) { landmark in
            Toggle(landmark.value.name, isOn: landmark[.isFavorite])
        }
    }
}

Define the following collection type:

struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection {
    typealias Index = Base.Index
    typealias Element = (index: Index, element: Base.Element)

    let base: Base

    var startIndex: Index { base.startIndex }

    var endIndex: Index { base.endIndex }

    func index(after i: Index) -> Index {
        base.index(after: i)
    }

    func index(before i: Index) -> Index {
        base.index(before: i)
    }

    func index(_ i: Index, offsetBy distance: Int) -> Index {
        base.index(i, offsetBy: distance)
    }

    subscript(position: Index) -> Element {
        (index: position, element: base[position])
    }
}

extension RandomAccessCollection {
    func indexed() -> IndexedCollection<Self> {
        IndexedCollection(base: self)
    }
}

Then, update your code to:

struct LandmarkList: View {
    @Binding var landmarks: [Landmark]

    var body: some View { // Does often give error on id: .1.id
        List(landmarks.indexed(), id: .1.id) { (index, landmark) in
            Toggle(landmark.name, isOn: self.$landmarks[index].isFavorite)
        }
    }
}

Your code also takes a Binding<[JobDetails]> with $jobList.jobDetails, but Binding<[JobDetails]> does not conform to the Collection protocol anymore.

Note to the solution above however, that I got cases where .1.id was not recognized because the compiler didn't understand .1 is referring to the second element in the tuple IndexedCollection defines, but it's possible that I used it wrong. It's possible to rewrite it however which makes it work.

Example using IndexedCollection

struct AnotherIndexedView_NeedsEnv: View {
    @EnvironmentObject var modalManager: ModalManager

    var body: some View {
        ZStack {
            SwiftUI.ForEach(modalManager.modals.indexed()) { m in
                ModalView(currentModal: self.$modalManager.modals[m.index]).environmentObject(self.modalManager)
            }
        }.onAppear(perform: {self.modalManager.fetchContent()})
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...