Implementing `components(separatedBy:)` method in Swift On looking at the components(separatedBy separator: String) -&gt; [String] method from the Swift Standard Library, I tried to come up with an implementation just for practice. Your comments are welcome to improve the same. Thanks.



Input:

func main() {
    let sampleString = "Do not be sorry. Be better."
    print(sampleString.components(separatedBy: "."))
}


Output:

["Do not be sorry", " Be better", ""]


Implementation:

extension StringProtocol {

    func components&lt;T&gt;(separatedBy separatorString: T) -&gt; [String] where T: StringProtocol {

        var currentIndex = 0; var stringBuffer = ""; var separatedStrings:[String] = []

        forEach { (character) in

            if String(character) == separatorString {
                separatedStrings.append(stringBuffer); stringBuffer = ""
            } else {
                stringBuffer += .init(character)
            }

            if currentIndex == lastIndex { separatedStrings.append(stringBuffer) }
            currentIndex += 1
        }
        return separatedStrings
    }
}

extension Collection {
    var lastIndex:Int {
        get {
            return self.count - 1
        }
    }
}
Coding style

This is of course a matter of personal taste, but would split multiple statements like


var currentIndex = 0; var stringBuffer = ""; var separatedStrings:[String] = []



into separate lines 

var currentIndex = 0
var stringBuffer = ""
var separatedStrings:[String] = []


I would also start new lines for nested code blocks, i.e.


if currentIndex == lastIndex { separatedStrings.append(stringBuffer) }



becomes

if currentIndex == lastIndex {
    separatedStrings.append(stringBuffer)
}


Correctness

Your function takes a separator of type string (protocol) as an argument, but actually works only for separators consisting of a single character. Example:

let sampleString = "Do not be sorry. Be better."
print(sampleString.components(separatedBy: ". "))
// ["Do not be sorry. Be better."]


The reason is that here


if String(character) == separatorString



a single character of the source string is compared with the separator.

Your method also behaves differently from the standard library version when called with an empty string, 

"".components(separatedBy: ".")


returns an empty array [] instead of a single-element array [""]. The reason is that the check


if currentIndex == lastIndex { separatedStrings.append(stringBuffer) }



is never done for an empty input string. (Checking for the last iteration inside a loop always makes me suspicious.)

Simplifications

Instead of converting a character to a string for appending


stringBuffer += .init(character)



you can append it directly:

stringBuffer.append(character)


Keeping track of the current character position can be done with
enumerated()

for (currentIndex, character) in self.enumerated() {
    // ...
}


instead of incrementing var currentIndex.

Efficiency

The main bottleneck is the

var lastIndex:Int


extension method. For Strings (and other collections which are not random accessible) determining self.count is a O(N) operation (N = number of characters). It requires traversing the entire string.

This method is called for each character in the source string, so that this contributes O(N^2) to the execution time.

It would also be more efficient to locate the next occurrence of the separator and append an entire substring to the result array, instead of appending single characters repeatedly.

Alternative implementation

Here is an alternative implementation, considering the above suggestions:

func components&lt;T&gt;(separatedBy separatorString: T) -&gt; [String]
    where T: StringProtocol, Index == String.Index {
        var separatedStrings: [String] = []
        var pos = startIndex
        while let range = self[pos...].range(of: separatorString) {
            separatedStrings.append(String(self[pos..&lt;range.lowerBound]))
            pos = range.upperBound
        }
        separatedStrings.append(String(self[pos...]))
        return separatedStrings
}