Am I taking crazy pills? Directly out of the documentation:
“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—the String type’s interpolation and Swift-designed APIs and the NSString class’s broad functionality. For this reason, you should almost never need to use the NSString class directly in your own code. In fact, when Swift imports Objective-C APIs, it replaces all of the NSString types with String types. When your Objective-C code uses a Swift class, the importer replaces all of the String types with NSString in imported API.
To enable string bridging, just import Foundation.”
I've done this... consider:
import Foundation
var str = "Hello World"
var range = str.rangeOfString("e")
// returns error: String does not contain member named: rangeOfString()
However:
var str = "Hello World" as NSString
var range = str.rangeOfString("e")
// returns correct (2, 1)
Am I missing something?
To go from String to NSString use the following constructor:
let swiftString:String = "I'm a string."
let objCString:NSString = NSString(string:swiftString)
With Xcode 7 (beta), using a downcast from String to NSString, as in below example, will result in a warning message, Cast from 'String?' to unrelated type 'NSString' always fails:
let objcString:NSString = swiftString as! NSString // results in error
You already have the answer in your question. You're missing the cast. When writing Swift code, a statement such as this one
var str = "Hello World"
creates a Swift String, not an NSString. To make it work as an NSString, you should cast it to an NSString using the as operator before using it.
This is different than calling a method written in Objective-C and supplying a String instead of an NSString as a parameter.
Here is example for this :
string str_simple = "HELLO WORLD";
//string to NSString
NSString *stringinObjC = [NSString stringWithCString:str_simple.c_str()
encoding:[NSString defaultCStringEncoding]];
NSLog(stringinObjC);
Related
I have a method that was originally defined to take an NSString parameter but now needs to be able to take an NSString parameter or an NSAttributedString parameter. Unfortunately these do not share an inheritance hierarchy but each inherit from NSObject. For this reason, I modified the method to take an NSObject and then check the class of the parameter within the method body.
This is working as I would like. However, now I am getting a compiler error from some swift code that calls the method:
Cannot convert value of String 'String' to expected argument type 'NSObject!'
I'm only beginning to work with Swift, and this particular code was written by someone else. I tried to surround the string in the method call with NSObject(...) but that didn't work. What would be the best way to accommodate this situation?
Cast it with as:
Either str as NSObject or str as NSString will work.
Example:
func test(_ s: NSObject) {
if s is NSString {
print("it is an NSString")
} else if s is NSAttributedString {
print("it is an NSAttributedString")
}
}
let str = "hello"
test(str as NSObject)
Output:
it is an NSString
Using the as operator should solve your immediate problem.
func someFunction(withAnObjectParameter: NSObject!) {
//...
}
let myString: String = "aString"
let myAttributedString: NSAttributedString = NSAttributedString()
someFunction(withAnObjectParameter: myString as NSObject)
someFunction(withAnObjectParameter: myAttributedString as NSObject)
Here are some links describing its usage in other scenarios:
swift blog
developer documentation
While trying to integrate the Address Book framework and converting CF types to NS Classes to Swift classes, I noticed something strange:
ABRecordCopyCompositeName(record)?.takeRetainedValue() as? NSString
returns nil
ABRecordCopyCompositeName(record)?.takeRetainedValue() as NSString?
returns Optional("John Smith")
My question is that isn't as? NSString synonymous to as NSString? as? NSString? (If so, why not?)
Therefore,
ABRecordCopyCompositeName(record)?.takeRetainedValue() as? NSString
should be equivalent to
ABRecordCopyCompositeName(record)?.takeRetainedValue() as NSString? as? NSString
which should return "John Smith".
(This was working on iOS 8.3, but iOS 8.4 broke my AddressBook feature.)
as (NS)String? is no supported syntax, even it might work in some way.
Either you can cast forced (as!) or optional (as?) or you can bridge (as) and there's no exclamation/question mark after the type.
ABAddressBookCopyArrayOfAllPeople() returns Unmanaged<CFArray>! and ABRecordCopyCompositeName() returns Unmanaged<CFString>!, both types are unwrapped optionals, so after calling takeRetainedValue() you can bridge to NSString
ABRecordCopyCompositeName(record).takeRetainedValue() as NSString
or further to String
ABRecordCopyCompositeName(record).takeRetainedValue() as NSString as String
I am calling a Objective method from Swift by using a Bridging header.
-(NSString *) PatternSetCreator: (char)Signature detection_time_in_sec:(int)detection_time_in_sec patternLength:(int)patternLength maxPatternSetSize:(int)maxPatternSetSize
There are two issuse:
a) I am not able to pass a single character as parameter while calling this method from Swift
b) I am not exactly sure how to get the return type NSString and assign it to a String variable
A single C char in Swift is represented as CChar, a typealias for Int8.
(similarly, C int in Swift is CInt, a typealias for Int32)
If you want a specific character and are using Swift 1.2, there’s an initializer for UInt8 that takes a UnicodeScalar. Annoyingly, though, you have to then convert it to a Int8 to make it compatible with the C method:
let ch = CChar(UInt8(ascii: "x"))
let i = CInt(100)
let s = obj.PatternSetCreator(ch,
detection_time_in_sec: i,
patternLength: i,
maxPatternSetSize: i)
You should not need to do anything special to turn the returned NSString to a String. The bridging will do that automatically.
(or rather, it’ll return a String! – but if the objective c code is guaranteed to return a valid string every time and never a null pointer, the definition can be changed to -(nonnull NSString *) PatternSetCreator: etc… which means it will return a String instead)
I use obj-c and swift classes together. And at one swift class, I try to convert objective c code to swift. However, I have a problem about NSURL.
the original code is:
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#://", appItem.URLSchema]];
and URLSchema is declared in the header file like this:
#property (nonatomic, copy) NSString *URLSchema;
I convert the objective c code which is above to swift:
var url: NSURL = NSURL(string:"%#://",relativeToURL: appItem.URLSchema)
but it says "missing argument for parameter "path" in call"
when I try this:
var url: NSURL = NSURL.URLWithString("%#://", appItem.URLSchema)
it says extra argument in call.
what do you suggest to convert it properly?
The second argument : RelativeToURL has the type NSURL and you pass a String
Try this :
var url:NSURL = NSURL(string: "\(appItem.URLSchema)://")
For more informations, you can take a look on the 'String Interpolation' section in the "Swift programming langage" iBook.
String interpolation is a way to construct a new String value from a
mix of constants, variables, literals, and expressions by including
their values inside a string literal. Each item that you insert into
the string literal is wrapped in a pair of parentheses, prefixed by a
backslash
let dataStr = data.base64EncodedStringWithOptions(options: Encoding64CharacterLineLength)
Doesn't compile with "Use of unresolved identifier 'Encoding64CharacterLineLength'"
When I just change the param to zero with
let dataStr = data.base64EncodedStringWithOptions(options: 0)
It gives even stranger error: "Cannot convert the expression of type 'String!' to type 'String!'" I found a way to init NSString with NSData (however, I still can't get the difference between String and NSString), but I'm really curious why these two lines of code don't work.
Unless explicitly given an external name, first argument of a method in Swift is not a named argument. Therefore you should be doing: data.base64EncodedStringWithOptions(x) without the options: part.
If you actually look at the argument type, NSDataBase64EncodingOptions, you'll notice that it is a struct conforming to RawOptionSet with static variables for option constants. Therefore to use them you should do: NSDataBase64EncodingOptions.Encoding64CharacterLineLength
The NSDataBase64EncodingOptions struct (or RawOptionSet in general) is also not convertible from integer literals (like 0). But it does conform to NilLiteralConvertible so if you don't want any options you can pass nil.
Putting it together:
let dataStr = data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
or
let dataStr = data.base64EncodedStringWithOptions(nil)
Swift3.0
let dataStr = data.base64EncodedString(options: [])
For Swift 2.x use an array for options:
let dataStr = data.base64EncodedStringWithOptions([.Encoding64CharacterLineLength])
let dataStr = data.base64EncodedStringWithOptions([])
For swift 3.0+ use this ,
var dataStr = data.base64EncodedString(options: .lineLength64Characters)
Swift 3.x
let fileStream = fileData?.base64EncodedString(options: NSData.Base64EncodingOptions.init(rawValue: 0))
You don't have to put in the "options:" identifier in the argument. You DO have to specify that Encoding64CharacterLineLength is a static member of NSDataBase64EncodingOptions, like so:
var dataStr = data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let dataStr = data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.allZeros)
Since the default parameter value is set be an empty array…
/// Returns a Base-64 encoded string.
///
/// - parameter options: The options to use for the encoding. Default value is `[]`.
/// - returns: The Base-64 encoded string.
#inlinable public func base64EncodedString(options: Data.Base64EncodingOptions = []) -> String
you just need to call
let dataStr = data.base64EncodedString()