One important point is that Swift's == on strings might not be equivalent to Objective-C's -isEqualToString:. The peculiarity lies in differences in how strings are represented between Swift and Objective-C.
Just look on this example:
let composed = "Ö" // U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
let decomposed = composed.decomposedStringWithCanonicalMapping // (U+004F LATIN CAPITAL LETTER O) + (U+0308 COMBINING DIAERESIS)
composed.utf16.count // 1
decomposed.utf16.count // 2
let composedNSString = composed as NSString
let decomposedNSString = decomposed as NSString
decomposed == composed // true, Strings are equal
decomposedNSString == composedNSString // false, NSStrings are not
NSString's are represented as a sequence of UTF–16 code units (roughly read as an array of UTF-16 (fixed-width) code units). Whereas Swift Strings are conceptually sequences of "Characters", where "Character" is something that abstracts extended grapheme cluster (read Character = any amount of Unicode code points, usually something that the user sees as a character and text input cursor jumps around).
The next thing to mention is Unicode. There is a lot to write about it, but here we are interested in something called "canonical equivalence". Using Unicode code points, visually the same "character" can be encoded in more than one way. For example, "Á" can be represented as a precomposed "Á" or as decomposed A + ◌́ (that's why in example composed.utf16 and decomposed.utf16 had different lengths). A good thing to read on that is this great article.
-[NSString isEqualToString:], according to the documentation, compares NSStrings code unit by code unit, so:
[Á] != [A, ◌́]
Swift's String == compares characters by canonical equivalence.
[ [Á] ] == [ [A, ◌́] ]
In swift the above example will return true for Strings. That's why -[NSString isEqualToString:] is not equivalent to Swift's String ==. Equivalent pure Swift comparison could be done by comparing String's UTF-16 Views:
decomposed.utf16.elementsEqual(composed.utf16) // false, UTF-16 code units are not the same
decomposedNSString == composedNSString // false, UTF-16 code units are not the same
decomposedNSString.isEqual(to: composedNSString as String) // false, UTF-16 code units are not the same
Also, there is a difference between NSString == NSString and String == String in Swift. The NSString == will cause isEqual and UTF-16 code unit by code unit comparison, where as String == will use canonical equivalence:
decomposed == composed // true, Strings are equal
decomposed as NSString == composed as NSString // false, UTF-16 code units are not the same
And the whole example:
let composed = "Ö" // U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
let decomposed = composed.decomposedStringWithCanonicalMapping // (U+004F LATIN CAPITAL LETTER O) + (U+0308 COMBINING DIAERESIS)
composed.utf16.count // 1
decomposed.utf16.count // 2
let composedNSString = composed as NSString
let decomposedNSString = decomposed as NSString
decomposed == composed // true, Strings are equal
decomposedNSString == composedNSString // false, NSStrings are not
decomposed.utf16.elementsEqual(composed.utf16) // false, UTF-16 code units are not the same
decomposedNSString == composedNSString // false, UTF-16 code units are not the same
decomposedNSString.isEqual(to: composedNSString as String) // false, UTF-16 code units are not the same