How would a simple function like index(of:) be implemented in a swift 3 extension. Please provide an example with the practices required to implement the example are.
If you are looking for the syntax of an extension of Array in Swift 3 here it is
extension Array {
func foo() { }
}
You can also restrict the extension to be available only where the Element of the array does conform to some protocol
extension Array where Element : Equatable {
func foo() { }
}
Related
What type should I use in swift 5 if I want a function to accept either a [Double] or an ArraySlice<Double>?
In other words, is there some type I can use which would allow me to combine both of the following declarations into one, so that the function can be called with one or the other?
func appendToX(_ data: ArraySlice<Double>)
func appendToX(_ data: [Double])
As #MartinR said in the comment, they both conform to RandomAccessCollection protocol, but you need to use generics in your function due to the fact that RandomAccessCollection has Self or associated type requirements:
func appendToX<T: RandomAccessCollection>(_ data: T) where T.Element == Double, T.Index == Int {
}
Swift 5.6
Thanks to several recent improvements to Swift's generics UI, you can replace your original two function declarations with the following:
func appendToX(_ data: any RandomAccessCollection<Double>)
I am attempting to extend Array<MutatingCollection> so I can mirror the contents of an Array of Arrays, but the compiler says I can't call reverse() on the elements in the array, despite reverse() being defined in MutatingCollection protocol.
I want to do something like this:
var table = [[0,1,2],
[3,4,5],
[6,7,8]]
table.mirror()
//table now [[2,1,0],
// [5,4,3],
// [8,7,6]]
Here is my (not working) code:
extension Array where Element == MutableCollection {
mutating func mirror() {
for index in self.indices {
self[index].reverse()
}
}
}
I have tried it as self.map {array in array.reverse()} as well (which I think does the same thing, but I don't fully grok map()) Both ways result in the same error message:
Member 'reverse' cannot be used on value of type 'MutableCollection'
Edit: I can call the same code directly and it works as I intended.
Playgrounds Screenshot
Perhaps I'm using extension improperly, or Swift Playgrounds is blocking my access somehow.
First of all, the extension should be declared like this:
extension Array where Element : MutableCollection {
You want to check that Element adheres to the protocol MutableCollection, not that it is a MutableCollection
However, then I'm not able to call the reverse method on the subscript for some reason. The best I've been able to do is this:
extension Array where Element : MutableCollection {
mutating func mirror() {
for index in self.indices {
self[index] = self[index].reversed() as! Element
}
}
}
Which works as you need it to work although the forced cast is very ugly and I dislike doing it. I suppose I should test the cast to be certain but I can't see any case where calling reversed() would result in a collection that couldn't be cast back to Element.
Edit:
I figured out the issue. The reverse() method is only valid on MutableCollection when it is also a BidirectionalCollection. This code now works correctly:
extension MutableCollection where
Iterator.Element : MutableCollection &
BidirectionalCollection,
Indices.Iterator.Element == Index {
mutating func mirror() {
for index in self.indices {
self[index].reverse()
}
}
}
Now the code should work for all MutableCollection whose elements are both a MutableCollection and BidirectionalCollection - such as [Array<Int>] or even [ArraySlice<Int>]
You can see the full code for reverse() in Swift 3.1 here:
Reverse.swift
extension MutableCollection where Self : BidirectionalCollection
After converting from Swift 2.2 to 3.0 my Array extension does not compile anymore, because it contains a call to global standard library function min<T>(T,T) and shows compiler error extra argument in call.
Here's a simple way to reproduce the error:
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
return min(first, second) // compiler error: "Extra argument in call"
}
}
I get the same error when adding the same function to an extension of Dictionary, while the exact same code compiles just fine in an extension of other types (e.g. String or AudioBuffer):
Looking at the documentation of Array and Dictionary, I find that there are instance methods on Sequence named public func min() -> Element? and public func min(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?. While both String and AudioBuffer do not have any kind of min(...) function.
Is it possible that this is the reason why I can't call the global function? The compiler can't distinguish between global func min<T>(T,T) and self.min(...) although they have completely different signatures?
Is this a bug or a feature? What am I doing wrong? How can I call min(T,T) correctly inside an Array extension?
I see no reason why the compiler shouldn't be able to resolve this function call, therefore I would consider it a bug (it has already been filed – see SR-2450).
It seems to occur whenever attempting to call a top-level function with the same name, but unambiguously different signature to a method or property that's accessible from the same scope in a given type (instance or static).
An even simpler example would be:
func foo(_ a: Int) {}
struct Foo {
func foo() {} // or static func foo() {}, var foo = 0, static var foo = 0
func bar() {
foo(2) // error: argument passed to call that takes no arguments
}
}
Until fixed, a simple solution would be to prefix the call with the name of the module in which it resides in order to disambiguate that you're referring to the top-level function, rather than the instance one. For the standard library, that's Swift:
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
return Swift.min(first, second)
}
}
In Swift 4, the compiler has a better diagnostic for this error (though the fact that it's still an error is a bug IMO):
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
// Use of 'min' refers to instance method 'min(by:)'
// rather than global function 'min' in module 'Swift'
// - Use 'Swift.' to reference the global function in module 'Swift'
return min(first, second)
}
}
Although what's interesting is that the compiler will now also warn on attempting to call a standard library method with the same name as a stdlib top-level function:
extension Array where Element : Comparable {
func smallest() -> Element? {
// Use of 'min' treated as a reference to instance method in protocol 'Sequence'
// - Use 'self.' to silence this warning
// - Use 'Swift.' to reference the global function
return min()
}
}
In this case, as the warning says, you can silence it by using an explicit self.:
extension Array where Element : Comparable {
func smallest() -> Element? {
return self.min()
}
}
Although what's really curious about this warning is it doesn't appear to extend to non-stdlib defined functions:
func foo(_ a: Int) {}
struct Foo {
func foo() {}
func bar() {
foo() // no warning...
}
}
I'm playing around with Swift trying to make it look more "dynamically typed" – just for fun, no production value expected.
Now I'm stuck with overwriting behavior of converting builtin types to String.
For example, I'd like to see this output for Array:
let nums = [1, 2, 3]
print(nums) // "I'm an array"
So far I tried to
make an extension to NSArray (not compiles)
implement CustomStringConvertible (not compiles)
make an extension to Array (compiles, changes nothing)
Seems like I'm on the wrong path:
extension Array: CustomStringConvertible {
public var description: String { return "An array" }
}
gives the warning:
Conformance of 'Array' to protocol 'CustomStringConvertible' was already stated in the type's module 'Swift'
Is this doable in Swift?
This does not work because Array overrides description. If array did not override it then it would print "An array". The class method 'wins' over the extension.
extension Array {
public var description: String { return "An array" }
}
You could create a Wrapper class for your array. It's a workaround but doesn't override array's description itself.
class ArrayWrapper<T> : CustomStringConvertible{
var array : Array<T> = Array<T>()
var description: String { return "An array" }
}
You could then use it like this.
var array = ArrayWrapper<Int>()
array.array = [1,2,3]
print(array) //prints "An Array"
print(array.array) //still prints "[1, 2, 3]"
Seems like it is not possible to neither subclass nor overwrite a builtin Array type.
We could use a wrapper though (credits to #Yannick) and implement a ArrayLiteralConvertible protocol, so we could use square brackets for initialization.
struct Array<T> {
let array: [T]
}
extension Array: ArrayLiteralConvertible {
init(arrayLiteral elements: T...) {
self.array = elements
}
}
extension Array: CustomStringConvertible {
var description: String { return "An array" }
}
let array = [1,2,3]
print(array) // "An array\n"
Swift does not allow you to provide a protocol override for a type that already declares that protocol:
Imagine that you have a set of integers and you want to override the default implementation of CustomStringConvertible for a Set
The simplest answer to all of this is: you can't, and that's a feature not a bug. Set isn't your type and you don't get to change its already-defined behavior like this.
The capability to intercept and alter the behavior of a type to do something different to what that type is originally designed to do is powerful, but fraught with downsides. In this case, you've chosen a fairly benign thing to alter, but in other cases doing this could do all sorts of damage to assumed invariants of a type by changing its program-wide behavior.
The best way to alter a type to do something different like this is to wrap it in your own low-cost struct. Unfortunately this does mean writing a fair amount of boilerplate for forwarding – though less and less these days as we gain features like synthesized conformances and dynamic member lookup. Hopefully someday we'll get features that do make easier to create a newtype with customized behavior.
https://forums.swift.org/t/whats-the-best-way-to-override-customstringconvertible-for-a-collection/24844
Trying to make and shuffle a card deck in Swift, made up of Card objects in an array called cardDeck. Then I want to shuffle it. Relevant code:
var cardDeck = [card]()
for ind in 1 ... 4
{
for ind2 in 1 ... 13
{
cardDeck.append(card(number: ind2, color: ind))
}
}
cardDeck = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(cardDeck)
"Cannot assign value of type [anyobject] to logic.card" ("logic" is the overall class name, and Card is another class within it)
Anyone know whats wrong? I guess the array is not an AnyObject type of array since I declared it as containing cards, right?
The problem is that arrayByShufflingObjectsInArray takes an [AnyObject] and returns an [AnyObject].
Therefore its going to completely throw away your [Card] type information that you provide as an input, and thus give you an error about not being able to convert types when you try to assign the output back to your original array. I suspect this is due to the method being implemented in Objective-C, not Swift.
A more Swifty version of the method would look like this:
func arrayByShufflingObjectsInArray<T:AnyObject>(array:[T]) -> [T] {
...
}
Through using generics, you can preserve the type information that you pass in to begin with, returning the same type that you inputted. In fact, you can write your own extension of GKRandomSource in order to do just that:
extension GKRandomSource {
func arrayOfSameTypeByShufflingObjectsInArray<T:AnyObject>(array:[T]) -> [T] {
return arrayByShufflingObjectsInArray(array) as! [T]
}
}
(Feel free to come up with a more catchy name for the method)
The force downcast is used as an ugly solution to the problem – which is why I recommend creating an extension, rather than using this directly. It cannot crash, as the output array is guaranteed to contain objects of the same type as the input (as the array you pass in can only contain a single type).
You can now use it like so:
cardDeck = GKRandomSource.sharedRandom().arrayOfSameTypeByShufflingObjectsInArray(cardDeck)