Redundant duplication of typealiase declarations when conforms to a protocol - swift

protocol PathCollection: Collection where Element == Target.Element, Index == Target.Index {
associatedtype Target: Collection
static var reference: KeyPath<Self, Target> { get }
}
extension PathCollection {
private var target: Target { self[keyPath: Self.reference] }
var startIndex: Index { target.startIndex }
var endIndex: Index { target.endIndex }
subscript(index: Index) -> Element {
get { target[index] }
}
func index(after i: Index) -> Index {
target.index(after: i)
}
}
It's pretty useful protocol which helps us to reduce boilerplate code when creating custom collections.
Suppose our struct wraps a dictionary. And we want it to be a collection just like that dictionary.
We should provide keyPath to the dictionary property and apply to the protocol. And it works!
Example of usage and my question:
protocol Graph: PathCollection where Target == [String: Int] {
var storage: [String: Int] { get set }
}
extension Graph {
static var reference: KeyPath<Self, [String: Int]> { \.storage }
}
struct UndirectedGraph: Graph {
typealias Element = Dictionary<String, Int>.Element // Why should we again declare this typealias!?
typealias Index = Dictionary<String, Int>.Index // Why should we again declare this typealias!?
var storage: [String: Int]
}
It perfectly works. But why should we redeclare Element and Index typealiases!? At the very first line of code of this post we explicitly defines Element and Index:
protocol PathCollection: Collection where Element == Target.Element, Index == Target.Index {
and then:
protocol Graph: PathCollection where Target == [String: Int] {
If I remove that redeclarations I get an compilation error, which I don't understand:
'PathCollection' requires the types 'Slice' and
'Dictionary<String, Int>.Element' (aka '(key: String, value: Int)') be
equivalent

Related

How can i disable adding new item to an array in Swift?

I have an array like this inside a struct:
struct TestType {
private(set) var array: [String] = ["0", "1", "2"]
mutating func updateItem0(_ value: String) {
self.array[0] = value
}
mutating func updateItem1(_ value: String) {
self.array[1] = value
}
mutating func updateItem2(_ value: String) {
self.array[2] = value
}
}
I want be able to disable appending method to this array when I use an instance of struct, and keeping the count of it as it is. I cannot use private(set) because it would not allow me to update items of it.
My idea is using private(set) inside struct and making a mutating function for updating items in case, I wanted to know if there is better way for it?
Lots of options but a simple enhancement would be passing the index:
mutating func update(_ value: String, at index: Int) {
array[index] = value
}
And another is to check if the operation is possible:
enum Error: Swift.Error {
case indexOutOfBound
}
mutating func update(_ value: String, at index: Int) throws {
guard array.indices.contains(index) else { throw Error.indexOutOfBound }
array[index] = value
}
Here is a nice way to handle it. Add subscript to your struct which then allows you to access and change the values like you would an array. Adopting CustomStringConvertible and implementing description allows you to print the internal array while keeping it entirely private:
struct TestType: CustomStringConvertible {
private var array: [String] = ["0", "1", "2"]
var description: String { String(describing: array) }
subscript(_ index: Int) -> String {
get {
return array[index]
}
set {
array[index] = newValue
}
}
}
var foo = TestType()
foo[0] = "hello"
foo[2] = "goodbye"
foo[3] = "oops" // Fatal error: Index out of range
print(foo[0]) // hello
print(foo[1]) // 1
print(foo[2]) // goodbye
print(foo) // ["hello", "1", "goodbye"]

RangeReplaceableCollection conformance doesn't require... actually anything

According to the documentation:
To add RangeReplaceableCollection conformance to your custom
collection, add an empty initializer and the replaceSubrange(_:with:)
method to your custom type.
But in practice it's not required! (except for empty initializer)
// Just stubs for minimal reproducible code
struct Category: Hashable {}
struct Product {}
struct ProductCollection {
typealias DictionaryType = [Category : [Product]]
// Underlying, private storage
private var products = DictionaryType()
// Enable our collection to be initialized with a dictionary
init(products: DictionaryType = DictionaryType()) {
self.products = products
}
}
extension ProductCollection: Collection {
// Required nested types, that tell Swift what our collection contains
typealias Index = DictionaryType.Index
typealias Element = DictionaryType.Element
// The upper and lower bounds of the collection, used in iterations
var startIndex: Index { return products.startIndex }
var endIndex: Index { return products.endIndex }
// Required subscript, based on a dictionary index
subscript(index: Index) -> Iterator.Element {
get { return products[index] }
}
// Method that returns the next index when iterating
func index(after i: Index) -> Index {
return products.index(after: i)
}
}
extension ProductCollection: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (Category, [Product])...) {
self.init(products: .init(uniqueKeysWithValues: elements))
}
}
extension ProductCollection: RangeReplaceableCollection {
init() {
products = DictionaryType()
}
// func replaceSubrange<C: Collection, R: RangeExpression>(_ subrange: R, with newElements: C)
// where Self.Element == C.Element, Self.Index == R.Bound {
// }
}
The code is taken from a great (but not related to the post's topic) John Sundell article.
This code compiles even though replaceSubrange function is not provided.
One more question. Why should I provide an empty initializer explicitly in this situation? I can initialize the struct like ProductCollection() without having that initializer. I can do this for many reasons: 1) products property has initializing value provided 2) main initializer has default value provided 3) there is also a ExpressibleByDictionaryLiteral initializer which can be used to initialize an empty object.
So why I have to provide one more empty initializer explicitly?
But please, the first question about replaceSubrange function is more important :)
That is a bug which has also been discussed in the Swift forum:
SR-6501 RangeReplaceableCollection default implementations cause infinite recursion
Compiler lets me use incomplete RangeReplaceableCollection
Using Swift
The reason is that there is an overload of the replaceSubRange() method (taking a RangeExpression as the first argument) which the compiler erroneously accepts as satisfying the protocol requirement.
But note that even if the code compiles without implementing the required method, it does not work and leads to an infinite loop. Here is a short example:
struct MyCollection : MutableCollection {
private var storage: [Int] = []
init(_ elements: [Int]) { self.storage = elements }
var startIndex : Int { return 0 }
var endIndex : Int { return storage.count }
func index(after i: Int) -> Int { return i + 1 }
subscript(position : Int) -> Int {
get { return storage[position] }
set(newElement) { storage[position] = newElement }
}
}
extension MyCollection: RangeReplaceableCollection {
init() { }
}
var mc = MyCollection([0, 1, 2, 3, 4, 5])
mc.replaceSubrange(0..<3, with: [2, 3, 4])
Running that code leads to an “infinite” loop and eventually crashes with EXC_BAD_ACCESS due to a stack overflow.

Swift Generic Functions, Protocols and associatedType - Cannot invoke function with an argument list of type '(from: T)'

I am trying to generalise a functions for different types of objects that does the same things (retrieves a value from the objects using a keyPath).
class GenericOutputParameter<Type>: Equatable {
// Single Line Parameter for the Deal parameters
var path: WritableKeyPathApplicator<Type>
var label: String
var value: Any? // THIS IS OPTIONAL
var format: Int
var columnID: String
var order: Int
init(path: WritableKeyPathApplicator<Type>, label: String, value: Any?, format: Int, order: Int, columnID: String) {
self.path = path
self.label = label
self.value = value
self.format = format
self.order = order
self.columnID = columnID
}
}
protocol haveOutputs {
associatedtype containerType
var dictionary: [String : (path: WritableKeyPathApplicator<containerType>,label: String, value: Any, format: Int, order: Int)] { get set }
var outputs: [GenericOutputParameter<containerType>] { get set }
}
func fillOutputs<T: haveOutputs>(container: inout T) {
container.outputs.removeAll()
for (columnID, valueTuple) in container.dictionary {
container.outputs.append(GenericOutputParameter(path: valueTuple.path, label: valueTuple.label, value: valueTuple.path.retrieve(from: container), format: valueTuple.format,order: valueTuple.order, columnID: columnID))
}
container.outputs.sort(by: { $0.order < $1.order })
} // END OF FILLOUTPUTS
I am using associatedType in the protocol as each object has its own different dictionary.
The function fillOutputs(container: inout T) retrieves the value from the object for the parameter specified by the key paths and appends it to an array.
I am getting an error in the container.outputs.append line towards the end of the code, as follows: Cannot invoke 'retrieve' with an argument list of type '(from: T)'. This refers to retrieve(from: container). Before attempting to generalise, this function was a method of each object (container) and using retrieve(from: self) worked.
For reference, the retrieve method is part of another generic function:
class WritableKeyPathApplicator<Type> {
private let applicator: (Type, Any) -> Type
private let retriever: (Type) -> Any
init<ValueType>(_ keyPath: WritableKeyPath<Type, ValueType>) {
applicator = {
...
}
retriever = {
...
}
}
func apply(value: Any, to: Type) -> Type {
return applicator(to, value)
}
func retrieve(from: Type) -> Any {
return retriever(from)
}
}
Given I am not an expert on Swift nor fully comprehend protocols, I may have lost myself in a glass of water and I would appreciate any thought/help. Thanks

Get all key paths from a struct in Swift 4

Let's say I have that struct:
struct MyStruct {
let x: Bool
let y: Bool
}
In Swift 4 we can now access it's properties with the myStruct[keyPath: \MyStruct.x] interface.
What I need is a way to access all it's key paths, something like:
extension MyStruct {
static func getAllKeyPaths() -> [WritableKeyPath<MyStruct, Bool>] {
return [
\MyStruct.x,
\MyStruct.y
]
}
}
But, obviously, without me having to manually declare every property in an array.
How can I achieve that?
DISCLAIMER:
Please note that the following code is for educational purpose only and it should not be used in a real application, and might contains a lot of bugs/strange behaviors if KeyPath are used this way.
Answer:
I don't know if your question is still relevant today, but the challenge was fun :)
This is actually possible using the mirroring API.
The KeyPath API currently doesn't allow us to initialize a new KeyPath from a string, but it does support dictionary "parsing".
The idea here is to build a dictionary that will describe the struct using the mirroring API, then iterate over the key to build the KeyPath array.
Swift 4.2 playground:
protocol KeyPathListable {
// require empty init as the implementation use the mirroring API, which require
// to be used on an instance. So we need to be able to create a new instance of the
// type.
init()
var _keyPathReadableFormat: [String: Any] { get }
static var allKeyPaths: [KeyPath<Foo, Any?>] { get }
}
extension KeyPathListable {
var _keyPathReadableFormat: [String: Any] {
let mirror = Mirror(reflecting: self)
var description: [String: Any] = [:]
for case let (label?, value) in mirror.children {
description[label] = value
}
return description
}
static var allKeyPaths: [KeyPath<Self, Any?>] {
var keyPaths: [KeyPath<Self, Any?>] = []
let instance = Self()
for (key, _) in instance._keyPathReadableFormat {
keyPaths.append(\Self._keyPathReadableFormat[key])
}
return keyPaths
}
}
struct Foo: KeyPathListable {
var x: Int
var y: Int
}
extension Foo {
// Custom init inside an extension to keep auto generated `init(x:, y:)`
init() {
x = 0
y = 0
}
}
let xKey = Foo.allKeyPaths[0]
let yKey = Foo.allKeyPaths[1]
var foo = Foo(x: 10, y: 20)
let x = foo[keyPath: xKey]!
let y = foo[keyPath: yKey]!
print(x)
print(y)
Note that the printed output is not always in the same order (probably because of the mirroring API, but not so sure about that).
After modifying rraphael's answer I asked about this on the Swift forums.
It is possible, discussion here:
Getting KeyPaths to members automatically using Mirror
Also, the Swift for TensorFlow team has this already built in to Swift for TensorFlow, which may make its way to pure swift:
Dynamic property iteration using key paths
I propose my solution. It has the advantage of dealing correctly with #Published values when using the Combine framework.
For the sake of clarity, it is a simplified version of what I have really. In the full version, I pass some options to the Mirror.allKeyPaths() function to change behaviour ( To enumerate structs and/or classes properties in sub-dictionaries for example ).
The first Mirror extension propose some functions to simplify properties enumeration.
The second extension implements the keyPaths dictionaries creation, replacing
#Published properties by correct name and value
The last part is the KeyPathIterable protocol, that add enumeration
capability to associated object
swift
// MARK: - Convenience extensions
extension String {
/// Returns string without first character
var byRemovingFirstCharacter: String {
guard count > 1 else { return "" }
return String(suffix(count-1))
}
}
// MARK: - Mirror convenience extension
extension Mirror {
/// Iterates through all children
static func forEachProperty(of object: Any, doClosure: (String, Any)->Void) {
for (property, value) in Mirror(reflecting: object).children where property != nil {
doClosure(property!, value)
}
}
/// Executes closure if property named 'property' is found
///
/// Returns true if property was found
#discardableResult static func withProperty(_ property: String, of object: Any, doClosure: (String, Any)->Void) -> Bool {
for (property, value) in Mirror(reflecting: object).children where property == property {
doClosure(property!, value)
return true
}
return false
}
/// Utility function to determine if a value is marked #Published
static func isValuePublished(_ value: Any) -> Bool {
let valueTypeAsString = String(describing: type(of: value))
let prefix = valueTypeAsString.prefix { $0 != "<" }
return prefix == "Published"
}
}
// MARK: - Mirror extension to return any object properties as [Property, Value] dictionary
extension Mirror {
/// Returns objects properties as a dictionary [property: value]
static func allKeyPaths(for object: Any) -> [String: Any] {
var out = [String: Any]()
Mirror.forEachProperty(of: object) { property, value in
// If value is of type Published<Some>, we transform to 'regular' property label and value
if Self.isValuePublished(value) {
Mirror.withProperty("value", of: value) { _, subValue in
out[property.byRemovingFirstCharacter] = subValue
}
} else {
out[property] = value
}
}
return out
}
}
// MARK: - KeyPathIterable protocol
protocol KeyPathIterable {
}
extension KeyPathIterable {
/// Returns all object properties
var allKeyPaths: [String: Any] {
return Mirror.allKeyPaths(for: self)
}
}

Swift Protocol Extension with AssociatedType Constrained to Collection, Can't Use Subscript

I'm trying to write a protocol that conforms to the Collection Protocol, and it has an associatedType - Object and a property object.
protocol DDCDataSource: Collection
{
associatedtype Object
var object: Object {get set}
}
I want to add some default functionality for the case where Object also conforms to the Collection protocol, namely just directly return Object's implementation of these required Collection properties and functions. It seems like it all works except for Collection's requirement for a subscript.
Cannot subscript a value of type 'Self.Object' with an index of type 'Self.Object.Index'
extension DDCDataSource where Object: Collection
{
typealias Index = Object.Index
var startIndex: Object.Index {
get {
return object.startIndex
}
}
var endIndex: Object.Index {
get {
return object.endIndex
}
}
subscript(position: Object.Index) -> Element
{
return object[position]
}
func index(after i: Object.Index) -> Object.Index {
return object.index(after: i)
}
}
Short answer: Change the return type of the subscript method
to Object.Element
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
or add a type alias (in a similar way as you did for the Index type)
typealias Element = Object.Element
subscript(position: Object.Index) -> Element {
return object[position]
}
That makes the code compile and run as expected.
Explanation: The subscript method of Collection is declared as
subscript(position: Self.Index) -> Self.Element { get }
where Self.Index and Self.Element are associated types
of `Collection. With your code
subscript(position: Object.Index) -> Element {
return object[position]
}
the compiler infers Self.Index to be Object.Index, but there
is no relation between Self.Element and Object.Element (which is
returned by object[position]). The error becomes more apparent
if you add an explicit cast:
subscript(position: Object.Index) -> Element {
return object[position] as Element
}
Now the compiler complains
error: 'Self.Object.Element' is not convertible to 'Self.Element'; did you mean to use 'as!' to force downcast?
The correct solution is not the forced cast but to make the compiler
know that Self.Element is Object.Element, by adding a type alias
or by changing the return type
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
so that the compiler infers DDCDataSource.Element to be Object.Element.
Full self-contained example: (Swift 4, Xcode 9 beta 6)
(Note that you can omit the get keyword for read-only computed
properties.)
protocol DDCDataSource: Collection {
associatedtype Object
var object: Object { get set }
}
extension DDCDataSource where Object: Collection {
var startIndex: Object.Index {
return object.startIndex
}
var endIndex: Object.Index {
return object.endIndex
}
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
func index(after i: Object.Index) -> Object.Index {
return object.index(after: i)
}
}
struct MyDataSource: DDCDataSource {
var object = [1, 2, 3]
}
let mds = MyDataSource()
print(mds[1]) // 2
for x in mds { print(x) } // 1 2 3
Firstly, I think you should define Element,
Secondly, you use object[position], Object Conforms To Collection , but it is not of Collection Types . Obviously it is not Array.
As apple
says: array
conforms to CustomDebugStringConvertible / CustomReflectable /
CustomStringConvertible / CVarArg /Decodable / Encodable /
ExpressibleByArrayLiteral /MutableCollection /RandomAccessCollection /
RangeReplaceableCollection
I think extension DDCDataSource where Object: Array is better.
And the element in array shall be Element defined. Just tips.
Try this:
subscript(position:Object.Index) -> Element
{
var element: Element
guard let elementObject = object[position] else {
//handle the case of 'object' being 'nil' and exit the current scope
}
element = elementObject as! Element
}