Skip to main content
added 641 characters in body
Source Link
Martin R
  • 24.2k
  • 2
  • 38
  • 96

Update: The Swift language changes constantly. For the convenience of future readers, here is an update to Swift 4 of the above code:

extension Sequence where Element: NSAttributedString {
    
    func join(withSeparator separator: NSAttributedString) -> NSAttributedString {
        let finalString = NSMutableAttributedString()
        for (index, string) in enumerated() {
            if index > 0 {
                finalString.append(separator)
            }
            finalString.append(string)
        }
        return finalString
    }
}

Also flatMap has been renamed to compactMap:

let attributedStrings = optionalAttributedStrings.compactMap { $0 }

Update: The Swift language changes constantly. For the convenience of future readers, here is an update to Swift 4 of the above code:

extension Sequence where Element: NSAttributedString {
    
    func join(withSeparator separator: NSAttributedString) -> NSAttributedString {
        let finalString = NSMutableAttributedString()
        for (index, string) in enumerated() {
            if index > 0 {
                finalString.append(separator)
            }
            finalString.append(string)
        }
        return finalString
    }
}

Also flatMap has been renamed to compactMap:

let attributedStrings = optionalAttributedStrings.compactMap { $0 }
Source Link
Martin R
  • 24.2k
  • 2
  • 38
  • 96

The second and third approach combine two tasks into one:

  • Extract the non-nil elements from the given array of optional attributed strings, and
  • concatenate these with a given separator.

My suggestion is to separate these concerns. This rules out #2 and #3 and leaves us with your #1 approach, the extension method on SequenceType.

Another disadvantage of the extension _ArrayType is that it uses an internal type, so that might break in a future version of Swift.

The flatMap() method from the Swift standard library already provides a method for the first task:

let attributedStrings = optionalAttributedStrings.flatMap { $0 }

and this can be combined with your extension method by the caller:

let joined = optionalAttributedStrings.flatMap { $0 }.join(withSeparator: separator)

A user might also want to concatenate non-optional strings, which works with the extension method:

let joined = attributedStrings.join(withSeparator: separator)

but not with the other two approaches.

Another argument for approach #1 is that it resembles the existing method to join strings:

extension SequenceType where Generator.Element == String {
    public func joinWithSeparator(separator: String) -> String
}

The method itself can be improved. The reduce() method creates a new NSMutableAttributedString in each iteration. Better create only one and append all elements (similar as on your join() function):

extension SequenceType where Generator.Element: NSAttributedString {
    
    func join(withSeparator separator: NSAttributedString) -> NSAttributedString {
        let finalString = NSMutableAttributedString()
        for (index, string) in enumerate() {
            if index > 0 {
                finalString.appendAttributedString(separator)
            }
            finalString.appendAttributedString(string)
        }
        return finalString
    }
}