Xcode 10.2 still with Swift 4.2 Array of optional StringProtocol extension not compiling anymore - swift

I've just updated to Xcode 10.2 and my formerly compiling extension can't compile anymore:
extension Array where Element == StringProtocol? {
func joined(by separator: String = " ") -> String {
return compactMap { $0?.description }.joined(separator: separator)
}
}
The famous Protocol 'StringProtocol' can only be used as a generic constraint because it has Self or associated type requirements error now shows up...
Someone can tell me what happened? I have not migrated to Swift 5, just opened my project.
I have changed to:
extension Array where Element == CustomStringConvertible?
But I would like to understand why this changed... Any idea?

Related

A Swift Tour, compiler error around public property in Protocol and Extensions section

I'm new to Swift and am going through the playground for the official A Swift Tour tutorial. I'm executing the code as-is, but am getting a compiler error when running the following block:
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)
The protocol is defined as follows:
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
The compiler error is:
error: Protocols and Extensions.xcplaygroundpage:41:9: error: property 'simpleDescription' must be declared public because it matches a requirement in public protocol 'ExampleProtocol'
var simpleDescription: String {
^
Protocols and Extensions.xcplaygroundpage:41:9: note: mark the property as 'public' to satisfy the requirement
var simpleDescription: String {
^
public
I've tried adding public in front of simpleDescription but then I get an unexpected pattern error.
Anyone know what's going on here?
Turns out although my Xcode version was correctly 12.1, my local Swift version was 5.2.4 (the tutorial assumes 5.3). I installed the Command Line Tools for Xcode 12 package, which updated my Swift to 5.3 and now the code compiled as expected. I used xcrun swift -version to see which version of Swift Xcode was using.

Swift generic sequence observable ambiguity

I've got following code
protocol NamedOption {
var optionTitle: String { get }
}
struct DebugOption: NamedOption {
let optionTitle: String
let debugViewControllerType = UIViewController.self
}
func testFunk<T: Sequence>(d: Observable<T>) where T.Element == NamedOption {
}
func bindFullResultsRx() {
let dd: Observable<[DebugOption]> = self.dataModel.debugOptions // this is defined and properly assigned earlier
testFunk(d: dd)
}
and at the line where I call testFunk Xcode gives me following error:
Expression type '()' is ambiguous without more context
I have no idea why this is happening :( So far I was able to make it working by changing constraints on testFunk into this:
func funk<T: NamedOption>(d: Observable<[T]>) {
}
which seems to me more restrictive then version at the top. Does any one knows how to make it working with T: Sequence ?
Xcode version is 9.4, Swift version is 4.1.
After some digging and help from work colleagues I was able to make it working by simply changing == into : so it looks like this
func testFunk<T: Sequence>(d: Observable<T>) where T.Element: NamedOption {
}
It's just a matter of swift syntax
https://docs.swift.org/swift-book/ReferenceManual/GenericParametersAndArguments.html
conformance-requirement → type-identifier : protocol-composition-type
OO and generics don't play too well together. In order to do what you want, you have to manually cast up as in:
testFunk(d: dd.map { $0.map { $0 as NamedOption } })

Swift last(:where) element in an array

For an array:
var arr = [3,2,1,2,1,4,5,8,6,7,4,2]
I can find the first matching element
arr.first(where: {$0 == 2})!
But the last
arr.last(where: {$0 == 2})
Returns:
cannot call value of non-function type 'Int?'
Why?
last(where:) was added in Swift 4.2 (Xcode 10).
If you need to find the last match using Swift 4.1 or earlier, you can do:
let last = arr.reversed().first { $0 == 2 }
As already mentioned by rmaddy this method still in beta. If you would like to add this functionality also to Swift 4.1 you can extend BidirectionalCollection as follow:
extension BidirectionalCollection {
func last(where predicate: (Element) throws -> Bool) rethrows -> Element? {
return try reversed().first(where: predicate)
}
}

Reference to self with Swift 4's KeyPath

Is there any way to reference self with Swift 4's new KeyPaths?
Something like this works fine, we can address a property of an object:
func report(array: [Any], keyPath: AnyKeyPath) {
print(array.map({ $0[keyPath: keyPath] }))
}
struct Wrapper {
let name: String
}
let wrappers = [Wrapper(name: "one"), Wrapper(name: "two")]
report(array: wrappers, keyPath: \Wrapper.name)
But addressing an object itself seems impossible to me:
let strings = ["string-one", "string-two"]
report(array: strings, keyPath: \String.self) // would not compile
I suppose there should be some obvious way for this?
EDIT:
Or simply:
let s = "text-value"
print(s[keyPath: \String.description]) // works fine
print(s[keyPath: \String.self]) // does not compile
Unfortunately, this isn't something Swift keypaths currently support. However, I do think this is something they should support (with the exact syntax you're attempting to use, e.g \String.self). All expressions have an implicit .self member that just evaluates to the expression, so it seems like a perfectly natural extension to allow .self in keypaths (Edit: This is now something that's being pitched).
Until supported (if at all), you can hack it with a protocol extension that adds a computed property that just forwards to self:
protocol KeyPathSelfProtocol {}
extension KeyPathSelfProtocol {
var keyPathSelf: Self {
get { return self }
set { self = newValue }
}
}
extension String : KeyPathSelfProtocol {}
let s = "text-value"
print(s[keyPath: \String.description])
print(s[keyPath: \String.keyPathSelf])
You just need to conform types that you want to use "self keypaths" with to KeyPathSelfProtocol.
Yes there is a way to reference self but it is not available in Swift 4. It was implemented for Swift 5 in september 2018 and it is called the Identity key path.
You use it like you suggested
let s = "text-value"
print(s[keyPath: \String.self]) // works in Swift 5.0
Even though Swift 5 is not yet released, you can already try it out by downloading a development version at: https://swift.org/download/#releases
The behavior of keyPath is the same as similar to Key-Value Coding: Getting the value of a member of the class / struct by key subscription.
In Swift self is not a member of the class, description is.
What result would you expect with
report(array: wrappers, keyPath: \Wrapper.self)

Swift 4 : Cannot call value of non-function type '[Self.Element.Type]' when instantiating associated type array

I was doing some exercises on Xcode 9 beta 2 Swift 4 from this article (https://www.uraimo.com/2016/01/06/10-Swift-One-Liners-To-Impress-Your-Friends/) when I came across an error while doing item no. 6:
extension Sequence{
typealias Element = Self.Iterator.Element
func partitionBy(fu: (Element)->Bool)->([Element],[Element]){
var first=[Element]()
var second=[Element]()
for el in self {
if fu(el) {
first.append(el)
}else{
second.append(el)
}
}
return (first,second)
}
}
Xcode 9 was throwing an error in the following lines:
var first=[Element]()
var second=[Element]()
The full error is below:
error: Swift-Playground.playground:6:29: error: cannot call value of non-function type '[Self.Element.Type]'
var second=[Element]()
The error persists even if I remove the typealias and use the full Self.Iterator.Element type.
This code works perfectly on Swift 3. I see no reason why it shouldn't work on Swift 4. Can someone help me out if it's a change in Swift 4 in terms of handling associated types and if so, what's the alternative to instantiate the array.
In Swift 4, protocol Sequence already defines the
associatedtype Element where Self.Element == Self.Iterator.Element
so you can just remove the
typealias Element = Self.Iterator.Element
to make it compile.