通用函数:表达式类型在没有更多上下文的情况下不明确
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
}
我在 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
}