~= operator in Swift - ios

I recently downloaded the Advanced NSOperations sample app from Apple and found this code...
// Operators to use in the switch statement.
private func ~=(lhs: (String, Int, String?), rhs: (String, Int, String?)) -> Bool {
return lhs.0 ~= rhs.0 && lhs.1 ~= rhs.1 && lhs.2 == rhs.2
}
private func ~=(lhs: (String, OperationErrorCode, String), rhs: (String, Int, String?)) -> Bool {
return lhs.0 ~= rhs.0 && lhs.1.rawValue ~= rhs.1 && lhs.2 == rhs.2
}
It seems to use the ~= operator against Strings and Ints but I've never seen it before.
What is it?

Simply use as a shortcut to "range": you can construct a range and "~=" means "contains".
(other can add more theoretical details, but the sense is this). Read it as "contains"
let n: Int = 100
// verify if n is in a range, say: 10 to 100 (included)
if n>=10 && n<=100 {
print("inside!")
}
// using "patterns"
if 10...100 ~= n {
print("inside! (using patterns)")
}
try with some values of n.
Is used widely for example in HTTP response:
if let response = response as? HTTPURLResponse , 200...299 ~= response.statusCode {
let contentLength : Int64 = response.expectedContentLength
completionHandler(contentLength)
} else {
completionHandler(nil)

It is an operator used for pattern matching in a case statement.
You can take a look here to know how you can use and leverage it providing your own implementation:
http://oleb.net/blog/2015/09/swift-pattern-matching/
http://austinzheng.com/2014/12/17/custom-pattern-matching/
Here is a simple example of defining a custom one and using it:
struct Person {
let name : String
}
// Function that should return true if value matches against pattern
func ~=(pattern: String, value: Person) -> Bool {
return value.name == pattern
}
let p = Person(name: "Alessandro")
switch p {
// This will call our custom ~= implementation, all done through type inference
case "Alessandro":
print("Hey it's me!")
default:
print("Not me")
}
// Output: "Hey it's me!"
if case "Alessandro" = p {
print("It's still me!")
}
// Output: "It's still me!"

You can look into Define Swift
func ~=<I : IntervalType>(pattern: I, value: I.Bound) -> Bool
func ~=<T>(lhs: _OptionalNilComparisonType, rhs: T?) -> Bool
func ~=<T : Equatable>(a: T, b: T) -> Bool
func ~=<I : ForwardIndexType where I : Comparable>(pattern: Range<I>, value: I) -> Bool

Related

how to use `if case` statement as boolean in swift

I have this enum (with associated type)
enum CookieType {
case regular(type: Int)
case gem(type: GemType)
}
struct Cookie {
let type: CookieType
let otherStuff...
}
Now if i want to do pattern matching, i can do this with no problem:
if case .gem == cookie.type {
}
However, I want to use case .gem == cookie.type as a boolean. The following gives error
var cookies: [Cookie] {
return [cookieA, cookieB]
}
var gems: [Cookie] {
return cookies.filter { case $0.type == .gem } // this has error
}
which means case $0.type == .gem is not a boolean. How can I deal with this?
case .gem = cookie.type is not a boolean expression, and if statements not only accept boolean expressions. However, in the closure argument for filter, you must write an Bool expression or a block that returns Bool.
One way to do that is:
cookies.filter {
if case $0.type = .gem { return true }
else { return false }
}
Or, you can add convenient properties to CookieType that gives you Bool values, if you tend to do this a lot:
enum CookieType {
case regular(type: Int)
case gem(type: GemType)
var isRegular: Bool {
if case .regular = self { return true }
else { return false }
}
var isGem: Bool {
if case .gem = self { return true }
else { return false }
}
}
Then you can do:
cookies.filter(\.isGem)
Normally, if case and switch statements are abstractions based on the pattern matching operator ~=, but that's not how enums with associated types are implemented. As such, for the time being, you need to reverse-engineer how it might have been done, to allow this:
cookies.filter { CookieType.regular ~= $0.type }
I do not believe it is possible to avoid the explicit CookieType there, but it still reads better than all alternatives.
/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter case: Looks like `Enum.case`.
public func ~= <Enum, AssociatedValue>(
case: (AssociatedValue) -> Enum,
instance: Enum
) -> Bool {
Mirror.associatedValue(of: instance, ifCase: `case`) != nil
}
public extension Mirror {
/// Get an `enum` case's `associatedValue`.
static func associatedValue<AssociatedValue>(
of subject: Any,
_: AssociatedValue.Type = AssociatedValue.self
) -> AssociatedValue? {
guard let childValue = Self(reflecting: subject).children.first?.value
else { return nil }
if let associatedValue = childValue as? AssociatedValue {
return associatedValue
}
let labeledAssociatedValue = Self(reflecting: childValue).children.first
return labeledAssociatedValue?.value as? AssociatedValue
}
/// Get an `enum` case's `associatedValue`.
/// - Parameter case: Looks like `Enum.case`.
static func associatedValue<Enum, AssociatedValue>(
of instance: Enum,
ifCase case: (AssociatedValue) throws -> Enum
) rethrows -> AssociatedValue? {
try associatedValue(of: instance).filter {
.equate(try `case`($0), to: instance) {
Self(reflecting: $0).children.first?.label
}
}
}
}
public extension Optional {
/// Transform `.some` into `.none`, if a condition fails.
/// - Parameters:
/// - isSome: The condition that will result in `nil`, when evaluated to `false`.
func filter(_ isSome: (Wrapped) throws -> Bool) rethrows -> Self {
try flatMap { try isSome($0) ? $0 : nil }
}
}
public extension Equatable {
/// Equate two values using a closure.
static func equate<Wrapped, Equatable: Swift.Equatable>(
_ optional0: Wrapped?, to optional1: Wrapped?,
using transform: (Wrapped) throws -> Equatable
) rethrows -> Bool {
try optional0.map(transform) == optional1.map(transform)
}
}
Inspired by #Jessy and SwiftLee, here is my solution:
// -----------------------
// CaseReflectable
// -----------------------
// designed for enums only
// (use it on other types not recommended)
protocol CaseReflectable {}
// default behaviors.
extension CaseReflectable {
/// case name
var caseName: String {
let mirror = Mirror(reflecting: self)
// enum cases:
// - normal case: no children
// - case with associated values: one child (with label)
guard let label = mirror.children.first?.label else {
return "\(self)" // normal case
}
// case with associated values
return label
}
/// associated values
var associatedValues: Any? {
// if no children, a normal case, no associated values.
guard let firstChild = Mirror(reflecting: self).children.first else {
return nil
}
return firstChild.value
}
}
// --------------------------
// custom operator ~=
// --------------------------
/// match enum cases with associated values, while disregarding the values themselves.
/// usage: `Enum.enumCase ~= instance`
func ~= <Enum: CaseReflectable, AssociatedValue>(
// an enum case (with associated values)
enumCase: (AssociatedValue) -> Enum, // enum case as function
// an instance of Enum
instance: Enum
) -> Bool
{
// if no associated values, `instance` can't be of `enumCase`
guard let values = instance.associatedValues else { return false }
// if associated values not of the same type, return false
guard values is AssociatedValue else { return false }
// create an instance from `enumCase` (as function)
let case2 = enumCase(values as! AssociatedValue)
// if same case name, return true
return case2.caseName == instance.caseName
}
// ------------
// Enum
// ------------
// enum with associated values
// (conforms to `CaseReflectable`)
enum Enum: CaseReflectable {
case int(Int)
case int2(Int)
case person(name: String, age: Int)
case str(String)
}
// ------------
// main
// ------------
let a: Enum = .int(3)
Enum.int ~= a // true
Enum.int2 ~= a // false
let joe = Enum.person(name: "joe", age: 8)
Enum.person ~= joe // true
Enum.int ~= joe // false
// array of enum cases
let items: [Enum] = [
.int(1), .str("hi"), .int(2)
]
// filter enum cases
let filtered = items.filter { Enum.int ~= $0 }
print(filtered) // [Enum.int(1), Enum.int(2)]

How to alphabetize an array of objects within Swift 3?

I try to list an array of objects in alphabetic order. I create this simple test, but doesn't know how to achieve this with an array of objects:
let names = ["Martin", "Nick", "Alex", "Ewa", "Barry", "Daniella", "Chris", "Robert", "Andrew"]
func alphabetizeArray(_ s1: String, _ s2: String) -> Bool {
return s1 < s2
}
let alphabeticNames = names.sorted(by: names)
print(reversedNames)
When I try this for an array of objects I came up with something like this:
func sorterForIDASC(this:People, that:People) -> Bool {
return this.name > that.name
}
peoples.sort(by: sorterForIDASC)
But this will give me an error of: Binary operator '>' cannot be applied to two 'String?' operands
Anyone suggestions how to solve this. I would examine the names of each object that is from the type of String. I use Swift 3/Xcode8.
If you only need > then implementing > for your optional property is sufficient:
func >(lhs: People, rhs: People) -> Bool {
if let left = lhs.name, let right = rhs.name {
return left > right
}
return false
}
Now you can use > on an array of your objects:
let result = arrayOfObjects.sorted(by: >)
You could also have your object conform to Equatable and implement at least == and > for the optional property:
struct People: Equatable {
var name: String?
}
func ==(lhs: People, rhs: People) -> Bool {
if let left = lhs.name, let right = rhs.name {
return left == right
}
return false
}
func >(lhs: People, rhs: People) -> Bool {
if let left = lhs.name, let right = rhs.name {
return left > right
}
return false
}
This opens even more possibilities.
In Swift 3.0
you can do that simply like this.
peoples.sort(by: { (this: People, that: People) -> Bool in
this. name < that. name
})

Use logical operator as combine closure in reduce

I am trying to reduce an array of Bools by applying the logical operator OR (||) using the following code, however I get an error:
func reduceBools(values: [Bool]) -> Bool {
return values.reduce(false, combine: ||)
}
Ambiguous reference to member '||'
Analogously for integers the code works like a charm.
func reduceInts(values: [Int]) -> Int {
return values.reduce(0, combine: +)
}
I was able to make it work by adding a || function (code below) or using a { $0 || $1 } closure but I dislike these approaches and I would prefer simply passing the operator.
func ||(lhs: Bool, rhs: Bool) -> Bool {
return lhs || rhs
}
The same thing happens for the logical AND (&&) operator.
How can I make it work without using the hack above?
As an alternative, you could use the following approach
// ||
func reduceBoolsOr(values: [Bool]) -> Bool {
return values.contains(true)
}
// &&
func reduceBoolsAnd(values: [Bool]) -> Bool {
return !values.contains(false)
}
Note that .reduce comes with an overhead. If the end result is the importance of your question (rather than enquiring above the unexpected behaviour of || and && operators in this context), then perhaps the pragmatic approach above can be of help, even if it doesn't really reduce the array, however producing the same result due to the simple nature of the boolean type.
Swift 4.2+ / Xcode 10.0+
In modern versions of Swift there is allSatisfy function, which checks all elements for satisfying some rule.
In OP's case:
values.allSatisfy { $0 }
UPD:
To make this work for OR, do
!values.allSatisfy{!$0}
Thanks to Andy Weinstein
Following approach will work
values.reduce(false) { $0 || $1 }
Ambiguous reference to member '||' means, that there are more than one possible candidates, from which compiler is not able to choose. In your case those are
public func ||<T : BooleanType, U : BooleanType>(lhs: T, #autoclosure rhs: () throws -> U) rethrows -> Bool
and
public func ||<T : BooleanType>(lhs: T, #autoclosure rhs: () throws -> Bool) rethrows -> Bool
probably your 'hack' using a { $0 || $1 } is the best solutions here.
This happens because of Swifts closure semantics. It takes your arguments and applies function to them, omitting argument names.
protocol Numeric {
...
public static func +(lhs: Self, rhs: Self) -> Self
...
}
In example with Ints, you would pass (Int, Int) into a closure, and + function in Numeric protocol expects exactly two ints to sum them.
Thats why code like below works just fine
[1, 2, 3, 4].reduce(0, +)
Because you just took 2 ints, and applied function, which takes just two ints.
If you write your own function, which would take just two argument, it would work as well.
func myOwnAwesomeFunc<T: Numeric>(a: T, b: T) -> T { in
return 1 // production ready
}
[1, 2, 3, 4].reduce(0, myOwnAwesomeFunc) // prints 1
Good so far. But why can't we write
[true, false, true].reduce(false, ||) // yields Cannot invoke 'reduce'
// with an argument list of type
// '(Bool, (Bool, #autoclosure () throws -> Bool) throws -> Bool)'
That's because this operator takes bool and a closure, which returns bool. Not bool, closure!
But if it is like this, why aren't we writing true || { false }() ?
Thats because of #autoclosure, which takes care of curly braces for us.
Main question, why is it implemented this way, so we can't use Swifts awesome short-hand closure syntax with booleans? Idk
Here's another approach, I modified the reduceBools function to take the operator as a parameter -
typealias LogicalOperator = ((Bool, #autoclosure () throws -> Bool) throws -> Bool)
func reduceBools(values: [Bool], combine: LogicalOperator) -> Bool {
var started: Bool = false
return values.reduce(into: true, { (result, value) in
result = started ? try! combine(result, value) : value // obviously up to you how you'd handle the try/catch
started = true
})
}
let bools = [true, false, false, true]
let result1 = self.reduceBools(values: bools, combine: ||)
print(result1) // prints true
let result2 = self.reduceBools(values: bools, combine: &&)
print(result2) // prints false
Or it could be more useful as an extension of Sequence -
extension Sequence where Element == Bool {
func reduce(_ combine: LogicalOperator) -> Bool {
var started: Bool = false
return self.reduce(into: true, { (result, value) in
result = started ? try! combine(result, value) : value
started = true
})
}
}
print(bools.reduce(||)) // prints true

Cannot invoke 'split' with an argument list of type '(String, (String) -> Bool)'. Swift 2

I am currently facing the following problem with a program that I am using to learn Swift and OAuth with: https://github.com/soundcloud/iOSOAuthDemo.
The error is as follows:
Cannot invoke 'split' with an argument list of type '(String, (String) -> Bool)'
with this snippet:
private func parameterValue(name: String, fragment: String) -> String? {
let pairs = split(fragment) { $0 == "&" }.filter({ pair in pair.hasPrefix(name + "=") })
if pairs.count > 0 {
return split(pairs[0]) { $0 == "=" }[1]
} else {
return nil
}
}
The guidance is:
Expected an argument list of type '(S, maxSplit: Int, allowEmptySlices: Bool, isSeparator: #noescape (S.Generator.Element) -> R)'
Is there anyone who may be help me to remove this error, as I am new to Swift and Swift 2.0?
Thanks In Advance,
The main idea with this duplicate link was to show you that in Swift 2 String is not a collection anymore, you have to use the String's characters property (and split is not a global function anymore). You also have to make some types back to String. Example:
private func parameterValue(name: String, fragment: String) -> String? {
let pairs = fragment.characters.split { $0 == "&" }.filter({ pair in String(pair).hasPrefix(name + "=") })
if pairs.count > 0 {
let subseq = pairs[0].split { $0 == "=" }.map { String($0) }
return subseq[1]
}
return nil
}
parameterValue("mike", fragment: "&mike=test") // "test"
Note that instead you could use the String's componentsSeparatedByString method to make your function look a bit simpler:
private func parameterValue(name: String, fragment: String) -> String? {
let pairs = fragment.componentsSeparatedByString("&").filter { $0.hasPrefix(name + "=") }
if !pairs.isEmpty {
return pairs[0].componentsSeparatedByString("=")[1]
}
return nil
}
parameterValue("mike", fragment: "&mike=test") // "test"

Sort an array based on the second value of a tuple

thanks for reading my post.
I have an array of tuples declared as such:
var myArray: [(item1: String?, item2: NSDate?)] = []
At the end of my loop I want to sort my array of tuples based on every tuple's item2, whose type is NSDate?.
Based on this answer and this answer I tried the following, but received this compiler error, "cannot invoke 'sort' with an argument list of type '((_,_) -> _)'.
Here is what I tried:
myArray.sort {$0.1.item2?.compare($1.1.item2?) == NSComparisonResult.OrderedDescending }
P.S. println() works fine and prints item1 and item2 as an optional.
You must implement Comparable protocol to NSDate
public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}
public func <(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == .OrderedAscending
}
extension NSDate: Comparable { }
After that you can sort your tuples by date:
myArray!.sort {$0.1 == $1.1 ? $0.1 > $1.1 : $0.1 > $1.1 }
An alternative to the accepted solution:
let res = myArray.sort { (left, right) -> Bool in
return left.item2?.timeIntervalSinceReferenceDate < right.item2?.timeIntervalSinceReferenceDate
}
The reason why the linked solutions did not work is because the array of tuples defined in the question contains optional types.
Checking for the optionals fixes the problem, without having to add new operators to NSDate.
An example, with 3 dates, and optional types:
var myArray: [(item1: String?, item2: NSDate?)] = []
myArray = [("now", NSDate()), ("now+30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(30))), ("now-30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(-30)))]
myArray.sortInPlace { (lhs, rhs) -> Bool in
if lhs.item2 != nil && rhs.item2 != nil {
return lhs.item2!.compare(rhs.item2!) == .OrderedAscending
}
return false // Return true if you want nil values first
}
Same code, if the types didn't allow for optionals:
var myArray: [(item1: String, item2: NSDate)] = []
myArray = [("now", NSDate()), ("now+30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(30))), ("now-30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(-30)))]
myArray.sortInPlace { (lhs, rhs) -> Bool in
return lhs.item2.compare(rhs.item2) == .OrderedAscending
}
MirekE's solution also works well, but you do not have control on where the nil values would end (they will be at the beginning).

Resources