通用函数:表达式类型在没有更多上下文的情况下不明确

Generic Function: Type of expression is ambiguous without more context

我在 class 中有一个静态函数,它采用必须符合 decodable 的通用类型,但是当我调用这个函数时,我收到以下错误:“表达式类型不明确,没有更多上下文” . Tour class(这是我传递给函数的类型)符合 Decodable 并继承自 CoreDataModel class.

这发生在 DashboardNetworkAdapter 的新 Xcode 12.0 Beta 上,当我调用 CoreDataModel.create 时(我为此 class 共享的代码片段的第 7 行)。

编辑最小可重现示例:

仪表板网络适配器:

class DashboardNetworkAdapter {
    
    // MARK: - GET
    
    public func syncTours(page: Int, completion: @escaping(Bool, Error, Bool) -> Void) {
        
        if let path = Bundle.main.path(forResource: "Tours", ofType: "json") {
            do {
                let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
                CoreDataModel.create(Array<Tour>.self, from: data) { success, error in
                    completion(success, error, false)
                }
            } catch let error {
                completion(false, error, false)
            }
        }
        
    }
    
}

CoreDataModel:

public class CoreDataModel: NSManagedObject {
    
    // MARK: - Variables
    
    @NSManaged public var id: Int64
    @NSManaged public var createdAt: Date?
    @NSManaged public var updatedAt: Date?


    
    // MARK: - CRUD
    
    static func create<T>(_ type: T.Type, from data: Data, completion: @escaping (Bool, Error?) -> Void) where T : Decodable {
        DataCoordinator.performBackgroundTask { context in
            do {
                let _ = try DataDecoder(context: context).decode(type, from: data, completion: {
                    completion(true, nil)
                })
            } catch let error {
                completion(false, error)
            }
        }
    }
}

游览:

@objc(Tour)
class Tour: CoreDataModel, Decodable {
    
    // MARK: - Variables
    
    @NSManaged public var name: String?
    @NSManaged public var image: URL?
    @NSManaged public var owned: Bool
    @NSManaged public var price: Double
    
    
    
    // MARK: - Coding Keys
    
    enum CodingKeys: String, CodingKey {
        case id = "id"
        case name = "name"
        case image = "image"
        case owned = "owned"
        case price = "price"
        case updatedAt = "updated_at"
        case createdAt = "created_at"
    }
    
    
    
    // MARK: - Initializer
    
    required convenience init(from decoder: Decoder) throws {
        guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError("Cannot find decoding context")}
        self.init(context: context)
        
        let topLevel = try decoder.container(keyedBy: PaginatedResponseCodingKeys.self)
        let values = try topLevel.nestedContainer(keyedBy: CodingKeys.self, forKey: .data)
        let id = try values.decode(Int64.self, forKey: .id)
        let name = try values.decode(String.self, forKey: .name)
        let image = try values.decode(String.self, forKey: .image)
        let owned = try values.decode(Int.self, forKey: .owned)
        let price = try values.decode(Double.self, forKey: .price)
        let updatedAt = try values.decode(Date.self, forKey: .updatedAt)
        let createdAt = try values.decode(Date.self, forKey: .createdAt)
        
        self.id = id
        self.name = name
        self.image = URL(string: image)
        self.owned = owned == 1
        self.price = price
        self.updatedAt = updatedAt
        self.createdAt = createdAt
    }
    
    
    init(context: NSManagedObjectContext) {
        guard let entity = NSEntityDescription.entity(forEntityName: "Tour", in: context) else { fatalError() }
        super.init(entity: entity, insertInto: context)
    }
    
}

数据解码器:

class DataDecoder: JSONDecoder {
    
    // MARK: - Variables
    
    var context: NSManagedObjectContext!
    private var persistent: Bool = true
    
    
    
    
    // MARK: - Initializers
    
    public init(persistent: Bool = true, context: NSManagedObjectContext) {
        super.init()
        
        self.dateDecodingStrategy = .custom({ (decoder) -> Date in
            let container = try decoder.singleValueContainer()
            let string = try container.decode(String.self)

            let dateFormatter = DateFormatter()
            locale = .autoupdatingCurrent
            dateFormat = "yyyy-MM-dd HH:mm:ss"

            return dateFormatter.date(from: string)!
        })
        
        self.context = context
        userInfo[.context] = context
    }
    
    
    
    
    // MARK: - Utilities
    
        public func decode<T>(_ type: T.Type, from data: Data, completion: (() -> Void)? = nil) throws -> T where T : Decodable {
            let result = try super.decode(type, from: data)
            
            if(persistent) {
                saveContext(completion: completion)
            }
            return result
        }
        
        
        private func saveContext(completion: (() -> Void)? = nil)  {
            guard let context = context else { fatalError("Cannot Find Decoding Context") }
            context.performAndWait {
                try? context.save()
                completion?()
                context.reset()
            }
        }
        
    }

CodingUserInfoKey 扩展:

extension CodingUserInfoKey {
    
    static let context = CodingUserInfoKey(rawValue: "context")!
    
}

completion 闭包对其 Error 参数的期望与它从 CoreDataModel.create 完成闭包的推断类型中获得的类型之间存在类型不匹配,即 Error?:

public func syncTours(page: Int, completion: @escaping(Bool, Error, Bool) -> Void) {
   ...

        CoreDataModel.create(Array<Tour>.self, from: data) { success, error in
            // error is of type Error?, but completion expects Error
            completion(success, error, false)
        }
   ...
}

一般来说,每当您遇到类型推断问题或错误时,将每个类型都明确化,您就会准确地看到不匹配的确切位置。例如,您可以在下面明确说明内部闭包签名:

CoreDataModel.create([Tour].self, from: data) { (success: Bool, err: Error?) -> Void in

}