-
Notifications
You must be signed in to change notification settings - Fork 199
/
Model.swift
81 lines (66 loc) · 3.1 KB
/
Model.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
// MARK: - Model
/// All persistent models should conform to the Model protocol.
public protocol Model: Codable {
/// Alias of Model identifier (i.e. primary key)
@available(*, deprecated, message: "Use ModelIdentifier")
typealias Identifier = String
/// A reference to the `ModelSchema` associated with this model.
static var schema: ModelSchema { get }
/// The reference to the root path. It might be `nil` if models do not support
/// property references.
static var rootPath: PropertyContainerPath? { get }
/// The name of the model, as registered in `ModelRegistry`.
static var modelName: String { get }
/// Convenience property to return the Type's `modelName`. Developers are strongly encouraged not to override the
/// instance property, as an implementation that returns a different value for the instance property will cause
/// undefined behavior.
var modelName: String { get }
/// For internal use only when a model schema is provided
/// (i.e. calls from Flutter)
func identifier(schema: ModelSchema) -> ModelIdentifierProtocol
/// Convenience property to access the serialized value of a model identifier
var identifier: String { get }
}
extension Model {
public var identifier: String {
guard let schema = ModelRegistry.modelSchema(from: modelName) else {
preconditionFailure("Schema not found for \(modelName).")
}
return identifier(schema: schema).stringValue
}
public func identifier(schema modelSchema: ModelSchema) -> ModelIdentifierProtocol {
// resolve current instance identifier fields
let fields: ModelIdentifierProtocol.Fields = modelSchema.primaryKey.fields.map {
guard let fieldValue = self[$0.name] else {
preconditionFailure("Identifier field named \($0.name) for model \(modelSchema.name) not found.")
}
switch fieldValue {
case let value as Persistable:
return (name: $0.name, value: value)
case let value as EnumPersistable:
return (name: $0.name, value: value.rawValue)
default:
preconditionFailure(
"Invalid identifier value \(String(describing: fieldValue)) for field \($0.name) in model \(modelSchema.name)")
}
}
guard !modelSchema.fields.isEmpty else {
return DefaultModelIdentifier<Self>.makeDefault(fromModel: self)
}
if fields.count == 1, fields[0].name == ModelIdentifierFormat.Default.name {
return ModelIdentifier<Self, ModelIdentifierFormat.Default>(fields: fields)
} else {
return ModelIdentifier<Self, ModelIdentifierFormat.Custom>(fields: fields)
}
}
/// The `rootPath` is set to `nil` by default. Specific models should override this
/// behavior and provide the proper path reference when available.
public static var rootPath: PropertyContainerPath? { nil }
}