This is the fourth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021


Without further ado, let’s jump right into the second part of the first part of the portal of 5 extensions to Make Swift more elegant: 5 Extensions to Make Swift more elegant — Pt.1

3. Customize operators

Let’s start by defining a weak variable someViewRef, and then creating, setting, and adding attributes to the parent view. Will it work, or will there be a problem?

class ViewController: UIViewController {
    private weak var someViewRef: UIView?

    override func viewDidLoad(a) {
        super.viewDidLoad()

        someViewRef = UIView(frame: CGRect(x: 100, y: 50, width: 100, height: 100))
        someViewRef?.backgroundColor = .red
        view.addSubview(someViewRef!)}}Copy the code

SomeViewRef = UIView(x: 100, y: 50, width: 100, height: 100) someViewRef = UIView(x: 100, y: 50, width: 100, height: 100

  • Because someViewRef is weak, it will be released immediately
  • SomeViewRef is nil because it was freed, forcing unpack to collapse

So to think differently, let’s pull out a function:

class ViewController: UIViewController {
    private weak var someViewRef: UIView?
    
    override func viewDidLoad(a) {
        super.viewDidLoad()

        let someView = assign(someViewRef: &someViewRef, someView: UIView(frame: CGRect(x: 100, y: 50, width: 100, height: 100)))
        view.addSubview(someView)
        someViewRef?.backgroundColor = .red
    }

    func assign(someViewRef: inout Optional<UIView>, someView: UIView) -> UIView {
        someViewRef = someView
        return someView
    }
}
Copy the code

So you have a little red square. But it’s a little limited, it only applies to UIView, so let’s generalize it and open up the pattern:

func assign<T> (target: inout Optional<T>, value: T) -> T {
    target = value        
    return value
}

Copy the code

At this point we look at downsizing:

let someView = assign(target: &someViewRef, value: UIView(frame: CGRect(x: 100, y: 50, width: 100, height: 100)))
Copy the code

found

  • Passing in a reference to the target variable each time,&someViewRef, slightly troublesome.
  • In addition, when passing the value parameter, UIView instance is always created first and then passed in, which is a hidden danger. We need to delay the execution of UIView and create it when it goes inside the Assign method

Now that the closure is ready, let’s define an operator like + – and use @AutoClosure’s features to create the following implementation:

infix operator <-
public func <- <T> (target: inout T? .value: @autoclosure() - >T) -> T {
    let val = value()
    target = val
    return val
}
Copy the code

Let’s look at downsizing:

let someView = someViewRef <- UIView(a)Copy the code

perfect~


4. Count the number of elements in a set

I’m sure you’ve all encountered the need to request data from the network, get an array, and then we want to count how many elements there are in that array. Let’s take a look at the following common methods. Have you used them?

let array = ["A"."A"."B"."A"."C"]

/ / 1.
var count = 0
for value in array {
    if value = = "A" {
        count + = 1}}/ / 2.
count = 0
for value in array where value = = "A" {
    count + = 1
}

/ / 3.
count = array.filter { $0 = = "A" }.count

/ / 4...
Copy the code

And we know that we can get the total number of sets using.count. To make your code more uniform and maintainable in your project, instead of writing it in multiple ways, define a similar extension: count(where:)

extension Sequence where Element: Equatable {
    func count(where isIncluded: (Element) - >Bool) -> Int {
        self.filter(isIncluded).count
    }
}
Copy the code

We add extensions to Sequence directly, so that the pattern is open again, like ArraySlice, which is also supported.

["A"."A"."B"].count(where: { $0 = = "A" }) / / 2

["B"."A"."B"].dropLast(1) // --> ArraySlice<String>
.count(where: { $0 = = "B" }) / / 1
Copy the code

5. Remove duplicate objects specified in the collection

In daily development, I imagine I would encounter the need to request data from the network and get an array that now needs to remove duplicate elements while maintaining the original order. Let’s look at the first method:

  • First, the inner elements are hashable
extension Sequence where Element: Hashable {
    func uniqued(a)- > [Element] {
        var seen = Set<Element> ()return filter { seen.insert($0).inserted }
    }
}

[ 1.2.3.1.2 ].uniqued()   // [1, 2, 3]
Copy the code

Internally, Set is used for contains lookup, which is less time complex than the contains (_:) method of array.

  • Second, the inner elements are not hashable

If the elements inside the array are parsed models and have not complied with Hashable, the above method will not work.

extension Sequence {
    func uniqued(comparator: (Element.Element) - >Bool)- > [Element] {
        var result: [Element] = []
        for element in self {
            if result.contains(where: {comparator(element, $0) {})continue
            }
            result.append(element)
        }
        return result
    }
}

let article1 = ArticleModel(title: "111", content: "aa", articleID: "11111", comments: [])
let article2 = ArticleModel(title: "222", content: "aaa", articleID: "22222", comments: [])
let article3 = ArticleModel(title: "111", content: "aaaa", articleID: "33333", comments: [])
let article4 = ArticleModel(title: "333", content: "aaaaa", articleID: "44444", comments: [])
let articles = [article1, article2, article3, article4]

let newarticles = articles.uniqued(comparator: {$0.title = = The $1.title})
print(newarticles)    // Article3 will be deleted as a result
Copy the code
  • The third, keypath version

Finally, there is the keypath version, which is just a wrapper around the second one and still calls the second one internally

extension Sequence {
    func uniqued<T: Equatable> (_ keyPath: KeyPath<Element.T>)- > [Element] {
        uniqued { $0[keyPath: keyPath] = = The $1[keyPath: keyPath] }
    }
}

let newarticles = articles.uniqued(\.title)
Copy the code

conclusion

As you can see from the five examples mentioned above, Swift’s Extension feature is powerful and flexible, making code much more extensible.

More extension usage, everyone slowly explore, hahaha