Swift Generic issue - swift

I am following this Generic tutorial from Apple https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html.
But in the end of tutorial. I got some problem with this:
var myStack = Stack<String>()
myStack.push("a")
myStack.push("b")
myStack.push("c")
var arrayOfStrings = ["a", "b", "c"]
if allItemsMatch(myStack, arrayOfStrings) {
print("All items match.")
} else {
print("Not all items match.")
}
at the line if allItemsMatch(myStack, arrayOfStrings), it says:
Cannot invoke 'allItemsMatch' with an argument list of type
'(Stack< String>, [String])'
Here is my code:
import UIKit
struct Stack<Element> {
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
mutating func append(item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? {
for (index, value) in array.enumerate() {
if value == valueToFind {
return index
}
}
return nil
}
let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
protocol Container {
associatedtype ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
extension Array: Container {}
func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, _ anotherContainer: C2) -> Bool {
if someContainer.count != anotherContainer.count {
return false
}
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
var myStack = Stack<String>()
myStack.push("a")
myStack.push("b")
myStack.push("c")
var arrayOfStrings = ["a", "b", "c"]
if allItemsMatch(myStack, arrayOfStrings) {
print("All items match.")
} else {
print("Not all items match.")
}
Am I missing somewhere?

You never explicitly conform your Stack<Element> struct to your Container protocol. Therefore Swift's strict type safety will prevent you from passing it into a parameter that expects something that conforms to the Container protocol (even if it implicitly conforms).
You can explicitly conform your Stack to Container via an extension. For example:
extension Stack:Container {}

Related

Encountered an error in "Generic Where Clauses", still not able to figure out why! While it is same in the swift book

Topic: Generic Where Clauses
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
struct Stack<Element>: Container {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
if someContainer.count != anotherContainer.count {
return false
}
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]
Error comes here in if-else control flow
if allItemsMatch(stackOfStrings, arrayOfStrings) {
print("All items match.")
} else {
print("Not all items match.")
}
Error says that "arrayOfStrings" doesn't conform to the Container Protocol.
Tried many times but not able to figure out
Error exactly says - " Global function 'allItemsMatch' requires that '[String]' conform to 'Container' "
As already mentioned in comments you forgot to declare that Array conforms to the Container protocol.
extension Array: Container { }
Not related to your question but you should also make your Stack structure conform to Sequence as well. It will allow you to use Sequence method elementsEqual
func elementsEqual<OtherSequence>(_ other: OtherSequence) -> Bool where OtherSequence : Sequence, Self.Element == OtherSequence.Element
So just add an index property to your Stack type and implement Sequence next property as follow:
struct Stack<Element>: Container {
typealias Item = Element
var items = [Item]()
mutating func push(_ item: Item) { items.append(item) }
mutating func pop() -> Item { items.removeLast() }
mutating func append(_ item: Item) { push(item) }
var count: Int { items.count }
subscript(i: Int) -> Item { items[i] }
var index: Int = 0
}
extension Stack: Sequence, IteratorProtocol {
mutating func next() -> Item? {
guard index < items.endIndex else { return nil }
defer { index += 1}
return items[index]
}
}
Playground testing:
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]
stackOfStrings.elementsEqual(arrayOfStrings) // true

WeakBox requires that WeakArray<Element>.Element be a class type

I'm trying to implement a storing weak references solutions from objc talks (https://www.objc.io/blog/2017/12/28/weak-arrays/) but I'm not able to get it working.
The exact error message tells:
'WeakBox' requires that 'WeakArray<Element>.Element' (aka 'Optional<Element>') be a class type
With this code:
final class WeakBox<A: AnyObject> {
weak var unbox: A?
init(_ value: A) {
unbox = value
}
}
struct WeakArray<Element: AnyObject> {
private var items: [WeakBox<Element>] = []
init(_ elements: [Element]) {
items = elements.map { WeakBox($0) }
}
init() {}
}
extension WeakArray: Collection {
var startIndex: Int { return items.startIndex }
var endIndex: Int { return items.endIndex }
subscript(_ index: Int) -> Element? {
return items[index].unbox
}
func index(after idx: Int) -> Int {
return items.index(after: idx)
}
mutating func append(_ element: Element) {
items.append(WeakBox(element))
}
mutating func removeAll() {
items.removeAll()
}
}
**
Update:
**
After some time I realized that error message is completely misleading. The real problem is in calling methods of a Sequence protocol. For example, adding something like this below produces an error message from the above screenshot. But I haven't found a solution yet.
class De {
let de = "de"
}
let de = De()
var ar = WeakArray<De>([])
ar.append(de)
ar.append(de)
ar.forEach({ $0 })
I believe 'Element' is being redefined by the SDK somewhere. Renaming Element to T should fix the issue.
import Foundation
final class WeakBox<T:AnyObject> {
weak var unbox: T?
init(_ value: T?) {
unbox = value
}
}
struct WeakArray<T: AnyObject> {
private var items: [WeakBox<T>] = []
init(_ elements: [T]) {
items = elements.map { WeakBox($0) }
}
init(_ elements: [T?]) {
items = elements.map { WeakBox($0) }
}
mutating func append(_ obj:T?) {
items.append(WeakBox(obj))
}
mutating func remove(at:Int) {
items.remove(at: at)
}
}
extension WeakArray: Collection {
var startIndex: Int { return items.startIndex }
var endIndex: Int { return items.endIndex }
subscript(_ index: Int) -> T? {
return items[index].unbox
}
func index(after idx: Int) -> Int {
return items.index(after: idx)
}
}

Extending a Collection where the Element type is generic (Swift)

It's simple to extend a Collection type in swift to have a custom function for particular Element types:
struct MyStruct {
let string: String
}
extension Set where Element == MyStruct {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
But suppose your Element type is generic?
protocol MyProtocol {}
struct MyGenericStruct<T: MyProtocol> {
let string: String
}
// error on next line:
// error: expected '>' to complete generic argument list
// extension Set where Element == MyGenericStruct<T: MyProtocol> {
// ^
extension Set where Element == MyGenericStruct<T: MyProtocol> {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
// error on next line:
// error: use of undeclared type 'T'
// extension Set where Element == MyGenericStruct<T> {
// ^
extension Set where Element == MyGenericStruct<T> {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
It's unclear to me how to declare that my element is a generic.
Details
Swift 3.1, xCode 8.3.3
Solution
import Foundation
struct MyStruct:Hashable {
let string: String
static func == (lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.string == rhs.string
}
var hashValue: Int {
return string.hash
}
}
extension Set where Element == MyStruct {
func getFirstWith(string: String) -> Element? {
return filter({ $0.string == string }).first
}
}
var set = Set<MyStruct>()
set.insert(MyStruct(string: "123"))
set.insert(MyStruct(string: "231"))
print(String(describing:set.getFirstWith(string: "123")))
print(String(describing:set.getFirstWith(string: "231")))
///////////////////////////////////
protocol MyProtocol {}
struct MyType1: MyProtocol {}
struct MyType2: MyProtocol {}
struct MyGenericStruct<T: MyProtocol>: Hashable {
let string: String
static func == (lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.string == rhs.string
}
var hashValue: Int {
return string.hash
}
}
extension Set where Element == AnyHashable {
func getFirstWith(string: String) -> Element? {
return filter{ element -> Bool in
if let _element = element as? MyGenericStruct<MyType1> {
if _element.string == string {
return true
}
}
if let _element = element as? MyGenericStruct<MyType2> {
if _element.string == string {
return true
}
}
return false
}.first
}
}
var set2 = Set<AnyHashable>()
set2.insert(MyGenericStruct<MyType1>(string: "abc"))
set2.insert(MyGenericStruct<MyType2>(string: "cba"))
print(String(describing: set2.getFirstWith(string: "abc")))
print(String(describing:set2.getFirstWith(string: "cba")))
Result
Answering my own question here on request.
I was unable to figure out how to make this an extension on Set.
Instead, we wrote a little utility function. Not as clear as a extension, but gets the work done. It looks like this:
func getFirst<T: MyProtocol>(fromSet set: Set<MyGenericStruct<T>>, whereStringIs string: String) -> MyGenericStruct<T>? {
return set.first(where: { $0.string == string })
}
As others have noted, MyGenericStruct needs to be Hashable.
If I do understand the purpose, you probably need to have more implementation in your protocol and structure.
First, element's protocol has to be hashable and equitable.
protocol MyProtocol: Hashable, Equatable {
var string: String { get }
}
Therefore, MyGenericStruct will look like the following.
struct MyGenericStruct: MyProtocol {
let string: String
var hashValue: Int {
get {
return string.hashValue
}
}
static func ==(lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
Then, declare extension with constraints specified by a where clause for MyProtocol
extension Set where Element: MyProtocol {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
Finally, let's do a test and see its result.
// Example
let set: Set = [
MyGenericStruct(string: "Watermelon"),
MyGenericStruct(string: "Orange"),
MyGenericStruct(string: "Banana")
]
let output = set.getFirstWith(string: "Orange")
print(output)
In my playground with Xcode 8, I can get Optional(MyGenericStruct(string: "Orange")) in log.
[UPDATED1] To make an Extension on Set<MyGenericStruct> only:
extension Set where Element == MyGenericStruct {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
[UPDATED2] In case to keep MyGenericStruct<T: MyProtocol> declaration as stated is necessary, another approach to implement Set extension:
protocol MyProtocol {
}
struct BaseStruct1: MyProtocol {
}
struct BaseStruct2: MyProtocol {
}
protocol ContainStringProtocol: Hashable {
var string: String { get }
}
struct MyGenericStruct<T: MyProtocol>: ContainStringProtocol {
var string: String
var hashValue: Int {
get {
return string.hashValue
}
}
init(string: String) {
self.string = string
}
static func ==(lhs: MyGenericStruct, rhs: MyGenericStruct) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
extension Set where Element: ContainStringProtocol {
func getFirstWith(string: String) -> Element? {
return filter({$0.string == string}).first
}
}
// Example
let set1: Set = [
MyGenericStruct<BaseStruct1>(string: "Watermelon"),
MyGenericStruct<BaseStruct1>(string: "Orange"),
MyGenericStruct<BaseStruct1>(string: "Banana")
]
let output1 = set1.getFirstWith(string: "Orange")
print(output1!)
let set2: Set = [
MyGenericStruct<BaseStruct2>(string: "Watermelon"),
MyGenericStruct<BaseStruct2>(string: "Orange"),
MyGenericStruct<BaseStruct2>(string: "Banana")
]
let output2 = set2.getFirstWith(string: "Orange")
print(output2!)

function with optionals, generics and comparable does satisfy compiler requirements

Im reading up on some algorithms and im working on a binary search tree. I have made the following
class TreeNode<T: Comparable>{
var value: T
var parent: TreeNode?
var leftChild: TreeNode?
var rightChild: TreeNode?
init(value: T) {
self.value = value
}
// ... more stuff ...
Which works fine. I have search, insert and print up and running, however when im trying to implement isSame, to se if two trees are the same, the compiler wont accept any of my function declarations:
First i try:
func isSame(leftNode: TreeNode?, rightNode: TreeNode?) -> Bool {
Compiler tells me "Reference to generic type 'TreeNode' requires arguments"
and suggests adding , which i then do, but now the compiler wants me to insert a ',' after the last parameter
func isSame(leftNode: TreeNode?, rightNode: TreeNode<T: Comparable>?) -> Bool {
if I do, it suggest i add another :D
resulting in something like
func isSame(leftNode: TreeNode?, rightNode: TreeNode<T: Comparable>?,,,) -> Bool {
Compiler, I have obeyed your every command. What more do you want from me?
I can get around this by stripping the function of the generic part like this:
func isSame(leftNode: TreeNode<Int>?, rightNode: TreeNode<Int>?) -> Bool {
But im curious to what I have to do to make the function work with generic types?
TLDR: How do I make an isSame function accept these two generic arguments?
Full code:
import UIKit
class TreeNode<T: Comparable>{
var value: T
var parent: TreeNode?
var leftChild: TreeNode?
var rightChild: TreeNode?
init(value: T) {
self.value = value
}
convenience init(array: [T]){
precondition(array.count > 0) // test denne
self.init(value: array.first!)
for item in array.dropFirst(){
self.insert(item)
}
}
func insert(_ newValue: T){
if newValue < self.value {
if leftChild == nil {
leftChild = TreeNode(value: newValue)
leftChild?.parent = self
} else {
leftChild!.insert(newValue)
}
}
else {
if rightChild == nil {
rightChild = TreeNode(value: newValue)
rightChild?.parent = self
} else {
rightChild!.insert(newValue)
}
}
}
func search(_ targetValue: T) -> Bool{
if targetValue < self.value{
if let leftChild = leftChild{
return leftChild.search(targetValue)
}
}
if targetValue > self.value{
if let rightChild = rightChild{
return rightChild.search(targetValue)
}
}
if targetValue == value{
print("found \(targetValue)")
return true
}
print("did not find \(targetValue)")
return false
}
}
// MARK: - Extensions
extension TreeNode: CustomStringConvertible{
var description: String {
var s = ""
if let left = self.leftChild {
s += "(\(left.description)) <-"
}
s += " \(value) "
if let right = self.rightChild {
s += "-> (\(right.description))"
}
return s
}
}
// isSame
func isSame(leftNode: TreeNode<Int>?, rightNode: TreeNode<Int>?) -> Bool {
if ((leftNode != nil && rightNode == nil) || (rightNode == nil && leftNode != nil)){
print("xor check returned false")
return false
}
if leftNode?.value != rightNode?.value {
return false
}
if isSame(leftNode: leftNode?.leftChild, rightNode: rightNode?.leftChild){
return isSame(leftNode: leftNode?.rightChild, rightNode: rightNode?.rightChild)
} else {
return false
}
}
// MARK: - Testing
var smallTree: TreeNode<Int> = TreeNode(value: 13)
var rootNodeFromArray = TreeNode(array: [7, 2, 5, 10, 9, 1])
// isSame test
let treeA: TreeNode<Int> = TreeNode(array: [7, 2, 5, 10, 9, 1])
let treeB: TreeNode<Int> = TreeNode(array: [7, 2, 5, 10, 9, 1])
If the function is a global function, you can make it a generic function:
func isSame<T>(leftNode: TreeNode<T>?, rightNode: TreeNode<T>?) -> Bool {
This is exactly how the Equatable protocol can be implemented:
func == <T: Equatable>(lhs: TreeNode<T>, rhs: TreeNode<T>) -> Bool {
return lhs.value == rhs.value
&& lhs.leftChild == rhs.leftChild
&& lhs.rightChild == rhs.rightChild
}
Note that the Equatable protocol doesn't need optional parameters. The comparison with nil is handled automatically.
Then you can simply compare using leftNode == rightNode.
You could put the isSame function inside the class like this:
class TreeNode<T: Comparable> {
// code here
class func isSame(leftNode: TreeNode<T>?, rightNode: TreeNode<T>?) -> Bool {
// code here
return true
}
}

Iterating through an Enum in Swift 3.0

I have a simple enum that I would like to iterate over. For this purpose, I've adopted Sequence and IteratorProtocol as shown in the code below. BTW, this can be copy/pasted to a Playground in Xcode 8.
import UIKit
enum Sections: Int {
case Section0 = 0
case Section1
case Section2
}
extension Sections : Sequence {
func makeIterator() -> SectionsGenerator {
return SectionsGenerator()
}
struct SectionsGenerator: IteratorProtocol {
var currentSection = 0
mutating func next() -> Sections? {
guard let item = Sections(rawValue:currentSection) else {
return nil
}
currentSection += 1
return item
}
}
}
for section in Sections {
print(section)
}
But the for-in loop generates the error message "Type 'Sections.Type' does not conform to protocol 'Sequence'".
The protocol conformance is in my extension; so, what is wrong with this code?
I know there are other ways of doing this but I'd like to understand what's wrong with this approach.
Thanks.
Note that Martin’s solution can be refactored as a protocol:
import Foundation
protocol EnumSequence
{
associatedtype T: RawRepresentable where T.RawValue == Int
static func all() -> AnySequence<T>
}
extension EnumSequence
{
static func all() -> AnySequence<T> {
return AnySequence { return EnumGenerator() }
}
}
private struct EnumGenerator<T: RawRepresentable>: IteratorProtocol where T.RawValue == Int {
var index = 0
mutating func next() -> T? {
guard let item = T(rawValue: index) else {
return nil
}
index += 1
return item
}
}
Then, given an enum
enum Fruits: Int {
case apple, orange, pear
}
you slap the protocol and a typealias:
enum Fruits: Int, EnumSequence {
typealias T = Fruits
case apple, orange, pear
}
Fruits.all().forEach({ print($0) }) // apple orange pear
Update: As of Swift 4.2, you can simply add protocol conformance
to CaseIterable, see How to enumerate an enum with String type?.
You can iterate over a value of a type which conforms to the Sequence
protocol. Therefore
for section in Sections.Section0 {
print(section)
}
would compile and give the expected result. But of course that is not
really what you want because the choice of the value is arbitrary and the
value itself not needed in the sequence.
As far as I know, there is no way to iterate over a type itself, so that
for section in Sections {
print(section)
}
compiles. That would require that the "metatype" Sections.Type conforms
to Sequence. Perhaps someone proves me wrong.
What you can do is to define a type method which returns a sequence:
extension Sections {
static func all() -> AnySequence<Sections> {
return AnySequence {
return SectionsGenerator()
}
}
struct SectionsGenerator: IteratorProtocol {
var currentSection = 0
mutating func next() -> Sections? {
guard let item = Sections(rawValue:currentSection) else {
return nil
}
currentSection += 1
return item
}
}
}
for section in Sections.all() {
print(section)
}
Simply add to enum:
static var allTypes: [Sections] = [.Section0, .Section1, .Section2]
And than you can:
Sections.allTypes.forEach { (section) in
print("\(section)")
}
This looks so much simpler:
public protocol EnumSequence {
init?(rawValue: Int)
}
public extension EnumSequence {
public static var items: [Self] {
var caseIndex: Int = 0
let interator: AnyIterator<Self> = AnyIterator {
let result = Self(rawValue: caseIndex)
caseIndex += 1
return result
}
return Array(interator)
}
}
Iterated upon the solutions above, see below a protocol that can be implemented by enumerations to add the allValues sequence but also to allow the possibility to convert to and from string value.
Very convenient for String-like enumerations which need to support objective c (only int enumerations are allowed there).
public protocol ObjcEnumeration: LosslessStringConvertible, RawRepresentable where RawValue == Int {
static var allValues: AnySequence<Self> { get }
}
public extension ObjcEnumeration {
public static var allValues: AnySequence<Self> {
return AnySequence {
return IntegerEnumIterator()
}
}
public init?(_ description: String) {
guard let enumValue = Self.allValues.first(where: { $0.description == description }) else {
return nil
}
self.init(rawValue: enumValue.rawValue)
}
public var description: String {
return String(describing: self)
}
}
fileprivate struct IntegerEnumIterator<T: RawRepresentable>: IteratorProtocol where T.RawValue == Int {
private var index = 0
mutating func next() -> T? {
defer {
index += 1
}
return T(rawValue: index)
}
}
For a concrete example:
#objc
enum Fruit: Int, ObjcEnumeration {
case apple, orange, pear
}
Now you can do:
for fruit in Fruit.allValues {
//Prints: "apple", "orange", "pear"
print("Fruit: \(fruit.description)")
if let otherFruit = Fruit(fruit.description), fruit == otherFruit {
print("Fruit could be constructed successfully from its description!")
}
}
If your enum is an Int based one, you can do an effective but slightly dirty trick like this.
enum MyEnum: Int {
case One
case Two
}
extension MyEnum {
func static allCases() -> [MyEnum] {
var allCases = [MyEnum]()
for i in 0..<10000 {
if let type = MyEnum(rawValue: i) {
allCases.append(type)
} else {
break
}
}
return allCases
}
}
Then loop over MyEnum.allCases()..