Swift - which types to use? NSString or String
You should use the Swift native types whenever possible. The language is optimized to use them, and most of the functionality is bridged between the native types and the Foundation
types.
While String
and NSString
are mostly interchangeable, i.e, you can pass String
variables into methods that take NSString
parameters and vice versa, some methods seem to not be automatically bridged as of this moment. See this answer for a discussion on how to get the a String's length and this answer for a discussion on using containsString()
to check for substrings. (Disclaimer: I'm the author for both of these answers)
I haven't fully explored other data types, but I assume some version of what was stated above will also hold true for Array
/NSArray
, Dictionary
/NSDictionary
, and the various number types in Swift and NSNumber
Whenever you need to use one of the Foundation types, you can either use them to type variables/constants explicitly, as in var str: NSString = "An NSString"
or use bridgeToObjectiveC()
on an existing variable/constant of a Swift type, as in str.bridgeToObjectiveC().length
for example. You can also cast a String
to an NSString
by using str as NSString
.
However, the necessity for these techniques to explicitly use the Foundation types, or at least some of them, may be obsolete in the future, since from what is stated in the language reference, the String
/NSString
bridge, for example, should be completely seamless.
For a thorough discussion on the subject, refer to Using Swift with Cocoa and Objective-C: Working with Cocoa Data Types
Difference between String and NSString in Struct in Swift
I think the issue is when you initialize the NSString, Swift is automatically changing it to a String. Try initializing the NSString like this and seeing if that gives you compile error:
var testingstring1: NSString = "testingstring1" as NSString
Here is an explanation of the bridging between the two languages in terms of Objective-C's reference types and Swift's value types: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html
Calling NSString method on a String in Swift
After doing some research, it looks like containsString
is not a String
function, but can be accessed by bridging to an NSString
.
Under Apple's Documentation on Using Swift with Cocoa and Objective-C, it says that
Swift automatically bridges between the String type and the NSString
class. This means that anywhere you use an NSString object, you can
use a Swift String type instead and gain the benefits of both types
But it appears that only some of NSString's functions are accessible without explicitly bridging. To bridge to an NSString and use any of its functions, the following methods work:
//Example Swift String var
var newString:String = "this is a string"
//Bridging to NSString
//1
(newString as NSString).containsString("string")
//2
newString.bridgeToObjectiveC().containsString("string")
//3
NSString(string: newString).containsString("string")
All three of these work.
It's interesting to see that only some NSString
methods are available to Strings
and others need explicit bridging. This may be something that is built upon as Swift develops.
Difference between String and NSString in Struct in Swift
I think the issue is when you initialize the NSString, Swift is automatically changing it to a String. Try initializing the NSString like this and seeing if that gives you compile error:
var testingstring1: NSString = "testingstring1" as NSString
Here is an explanation of the bridging between the two languages in terms of Objective-C's reference types and Swift's value types: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html
Which Swift character count should I use when interacting with NSString APIs?
TL;DR
The documentation for NSString.length specifies:
The number of UTF-16 code units in the receiver.
Thus, if you want to interop between String and NSString:
- You should use
string.utf16.count
, and it will match up perfectly with(string as NSString).length
.
If you want to count the number of visible characters:
You should use
string.count
, and it will match up to the same number of times you need the → (right) key on your keyboard until you get to the end of the string (assuming you start at the beginning).Note: This is not always 100% accurate, but it appears Apple is constantly improving the implementation to make it more and more accurate.
Here's a Swift 4.0 playground to test a bunch of strings and functions:
let header = "NSString .utf16❔ encodedOffset❔ NSRange❔ .count❔ .characters❔ distance❔ .unicodeScalars❔ .utf8❔ Description"
var format = " %3d %3d ❓ %3d ❓ %3d ❓ %3d ❓ %3d ❓ %3d ❓ %3d ❓ %3d ❓ %@"
format = format.replacingOccurrences(of: "❓", with: "%@") // "❓" acts as a placeholder for "%@" to align the text perfectly
print(header)
test("")
test("abc")
test("❌")
test(")
test("☾test")
test(")
test("\u{200d}\u{200d}\u{200d})
test(")
test("\u{1F468}")
test("♀️♂️)
test("你好吗")
test("مرحبا", "Arabic word")
test("م", "Arabic letter")
test("שלום", "Hebrew word")
test("ם", "Hebrew letter")
func test(_ s: String, _ description: String? = nil) {
func icon(for length: Int) -> String {
return length == (s as NSString).length ? "✅" : "❌"
}
let description = description ?? "'" + s + "'"
let string = String(
format: format,
(s as NSString).length,
s.utf16.count, icon(for: s.utf16.count),
s.endIndex.encodedOffset, icon(for: s.endIndex.encodedOffset),
NSRange(s.startIndex..<s.endIndex, in: s).upperBound, icon(for: NSRange(s.startIndex..<s.endIndex, in: s).upperBound),
s.count, icon(for: s.count),
s.characters.count, icon(for: s.characters.count),
s.distance(from: s.startIndex, to: s.endIndex), icon(for: s.distance(from: s.startIndex, to: s.endIndex)),
s.unicodeScalars.count, icon(for: s.unicodeScalars.count),
s.utf8.count, icon(for: s.utf8.count),
description)
print(string)
}
And here is the output:
NSString .utf16❔ encodedOffset❔ NSRange❔ .count❔ .characters❔ distance❔ .unicodeScalars❔ .utf8❔ Description
0 0 ✅ 0 ✅ 0 ✅ 0 ✅ 0 ✅ 0 ✅ 0 ✅ 0 ✅ ''
3 3 ✅ 3 ✅ 3 ✅ 3 ✅ 3 ✅ 3 ✅ 3 ✅ 3 ✅ 'abc'
1 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 3 ❌ '❌'
4 4 ✅ 4 ✅ 4 ✅ 1 ❌ 1 ❌ 1 ❌ 2 ❌ 8 ❌ ''
5 5 ✅ 5 ✅ 5 ✅ 5 ✅ 5 ✅ 5 ✅ 5 ✅ 7 ❌ '☾test'
11 11 ✅ 11 ✅ 11 ✅ 1 ❌ 1 ❌ 1 ❌ 7 ❌ 25 ❌ ''
11 11 ✅ 11 ✅ 11 ✅ 1 ❌ 1 ❌ 1 ❌ 7 ❌ 25 ❌ ''
8 8 ✅ 8 ✅ 8 ✅ 4 ❌ 4 ❌ 4 ❌ 4 ❌ 16 ❌ ''
2 2 ✅ 2 ✅ 2 ✅ 1 ❌ 1 ❌ 1 ❌ 1 ❌ 4 ❌ ''
58 58 ✅ 58 ✅ 58 ✅ 13 ❌ 13 ❌ 13 ❌ 32 ❌ 122 ❌ '♀️♂️'
3 3 ✅ 3 ✅ 3 ✅ 3 ✅ 3 ✅ 3 ✅ 3 ✅ 9 ❌ '你好吗'
5 5 ✅ 5 ✅ 5 ✅ 5 ✅ 5 ✅ 5 ✅ 5 ✅ 10 ❌ Arabic word
1 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 2 ❌ Arabic letter
4 4 ✅ 4 ✅ 4 ✅ 4 ✅ 4 ✅ 4 ✅ 4 ✅ 8 ❌ Hebrew word
1 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 1 ✅ 2 ❌ Hebrew letter
Conclusions:
- To get a length that is compatible with NSString/NSRange, use either
(s as NSString).length
,s.utf16.count
(preferred),s.endIndex.encodedOffset
, orNSRange(s.startIndex..<s.endIndex, in: s)
. - To get the number of visible characters, use either
s.count
(preferred),s.characters.count
(deprecated), ors.distance(from: s.startIndex, to: s.endIndex)
A helpful extension to get the full range of a String:
public extension String {
var nsrange: NSRange {
return NSRange(startIndex..<endIndex, in: self)
}
}
Thus, you can call the original method like so:
replace(", characterAtIndex: ".utf16.count - 1) // �!
how can i convert NSString to String
let myNSString: NSString = "I'm iOSDev"
let myString: String = myNSString as String
print(myString)
print(myString.isEmpty)
Casting Refer to myNSString as String
Also, You don't need to cast textField text as NSString it's by default String
So, Just use
var myString: String = myTextField.text ?? ""
I Couldn't comment so Posting as Answer
How do I bridge objc typedef NSString to Swift as String?
The answer is to use NS_SWIFT_BRIDGED_TYPEDEF
As the title of the question:
How do I bridge objc typedef NSString to Swift as String?
I found NS_SWIFT_BRIDGED_TYPEDEF
, use that for an ObjC typedef of NSString, then the type will be bridged as String to Swift.
Example
Without NS_SWIFT_BRIDGED_TYPEDEF
typedef NSString * ObjcNSString;
@interface ObjC : NSObject
+ (void)doThingsWithString:(nullable ObjcNSString)mid;
@end
The corresponding Swift interface is:
public typealias ObjcNSString = NSString
open class ObjC : NSObject {
open class func doThings(with mid: String?)
}
And that will give me error when I do this:
let str: ObjcNSString? = nil
ObjC.doThings(with: str) // Cannot convert value of type 'ObjcNSString?' (aka 'Optional<NSString>') to expected argument type 'String?'
With NS_SWIFT_BRIDGED_TYPEDEF
Add NS_SWIFT_BRIDGED_TYPEDEF
for the ObjC typedef:
typedef NSString * ObjcNSString NS_SWIFT_BRIDGED_TYPEDEF;
@interface ObjC : NSObject
+ (void)doThingsWithString:(nullable ObjcNSString)mid;
@end
Then the Swift interface becomes:
public typealias ObjcNSString = String
open class ObjC : NSObject {
open class func doThings(with mid: ObjcNSString?)
}
And the same code compiles:
let str: ObjcNSString? = nil
ObjC.doThings(with: str)
So I achieved my goal:
But I really want to use the ObjcNSString type in Swift
If you look close to the difference between the Swift interfaces with or without NS_SWIFT_BRIDGED_TYPEDEF
, you'll see by adding NS_SWIFT_BRIDGED_TYPEDEF
we get a more desired Swift interface.
Related Topics
Document Directory Path of Xcode Device Simulator
Uilabel Wrong Word Wrap in iOS 11
Passing Data to View Controllers That Are Embedded in Container Views
iOS 9 Safari: Changing an Element to Fixed Position While Scrolling Won't Paint Until Scroll Stops
How to Disable Caching in Alamofire
Uistackview Distribution Fill Equally
Swiftui - How to Avoid Navigation Hardcoded into the View
How to Differentiate Between Iphone4 and iPhone 3
Module Compiled with Swift 4.0 Cannot Be Imported in Swift 4.0.1
Open a View Controller When a iOS Push Notification Is Received
Xcode 8 Build Crash on iOS 9.2 and Below
Creating Custom Info Window in Swift with the Google Maps iOS Sdk
What Are the Device-Width CSS Viewport Sizes of the Iphone6 and iPhone 6 Plus
Uiscreen Mainscreen Bounds Returning Wrong Size
What Is Export * in Module.Modulemap File Inside Each Framework
Uitableview Within Uiscrollview Using Autolayout
What's the Difference Between "Architectures" and "Valid Architectures" in Xcode Build Settings