First, I printed out the unicode scalars in your string:
print(a.unicodeScalars.map { $0.value })
// [97, 10, 65039, 98]
And found out that your string indeed contains the line feed character \n, which is the value 10. However, it is followed by U+FE0F (65039), one of those variation selectors in unicode.
The overload of contains you are calling here is contains(StringProtocol), not contains(Character). The former will perform a "smarter" kind of comparison, or as the Xcode's Quick Help documentation calls it, "non-literal":
Summary
Returns true iff other is non-empty and contained within self by case-sensitive, non-literal search.
Discussion
Equivalent to self.rangeOfString(other) != nil
I can't seem to find this documentation online though... All I could find was this discussion showing contains is smart enough to recognise that "ß" means "ss".
Anyway, the point is, contains does not do a character-by-character search. It does whatever it think "makes sense".
Here are a few ways to make it print true:
If you add the variation selector in to the argument to contains, it prints true:
print(a.contains("\n️")) // you can't see it, but there *is* a variation selector after the n
Check whether the unicodeScalars contain the \n character:
print(a.unicodeScalars.contains("\n"))
Use the contains(Character) overload:
print(a.contains("\n" as Character))
Use range(of:) with .literal option:
print(a.rangeOfString("\n", options: [.literal]) != nil)