I have read up on Swift optionals, specifically if let, but cannot make sense of this code. The tutorial is a swift project, but I am trying to use it to update an old Obj-C project. I am not trying to create an if let optional in objective-c, but rather just figure out how to make an Obj-C version do what he is doing here with Swift. The underlying code doesn't return a value.
if let user = user << Obj alternative
Following a tutorial and here is the code:
[[FIRAuth auth] signInWithEmail:userEmail
password:userPass
completion:^(FIRUser * _Nullable user, NSError * _Nullable error) {
if(error == nil) { // No ERROR so already so user already in Firebase
if let user = user{
NSDictionary *userData = #{ #"provider": user.providerID};
[[DataService instance] createFirebaseDBUserWithUid:user.uid
userData:userData
isDriver:NO];
}
DLog(#"Email user Auth successfully in Firebase");
Here is the createFirebaseDBUserWithUid:user code (possible returns a Firebase id value)
#objc func createFirebaseDBUser(uid: String, userData: Dictionary<String, Any>, isDriver: Bool) {
if isDriver {
REF_DRIVERS.child(uid).updateChildValues(userData)
} else {
REF_USERS.child(uid).updateChildValues(userData)
}
}
Would an obj-c version just be:
if( user != nil ){
NSDictionary *userData = #{ #"provider": user.providerID};
[[DataService instance] createFirebaseDBUserWithUid:user.uid userData:userData isDriver:NO];
}
The trick with Objective-C of course is its behaviour if you send any message to anil reference.
At the time of its invention (1986) it was actually progress to have any method sent to a nil reference (which is 'faulty' in an if statement since it is 0 (the number) at the same time) simply returning nil (or 0 if you interpret it as a number) and again 'faulty'.
In this sense you are 'mostly' correct if you do
if (user != nil)
in Objective-C wherever you see
if let user = user {
...
}
in Swift. Technically speaking it is not entirely the same, since the let-expression will give you a non-nullable reference to the same content shadowing the optional object for the following scope. So you do not have to do any more unwrapping of the user object in the scope of the if. Note however, that the user will again be optional after the if statement. This is adequately covered by the nil check in the Objective-C counterpart.
There may be "unusual" edge cases you could miss if the author of the Objective-C code relied on the "strange" interpretation of method passing in Objective-C. Nowadays it is considered bad practice to rely on these, but there used to be a time when we had chains of method calls that relied on said behaviour. Swift was invented partly to get rid of those sometimes unintended effects in method chains.
Objective-C Type is equivalent to Type! in Swift. for example: Objective-C NSDictionary would be NSDictionary! in Swift
Thus, Objective-C types are optionals by default and reading a Swift Type in Objective-C won't be affected by being an optional or not.
back to your question, as the intention of the code is not clear for me, generally, the code you wrote will generate a dictionary that contains one pair of (key, value) with the providerID which is not related to being optional or not.
the issue that you would face is if the User object was created in Swift, and you want to read it in Objective-C, then you have set it up for that
Related
I'm working, tentatively, with the AudioToolbox API using Swift 2.0 and Xcode 7b6. The API uses a lot of c-language constructs, including function pointers. This is my first time working with commands like withUnsafeMutablePointer and unsafeBitCast. I am looking for a reality check to make sure that I am not way off base in what I am doing.
For example, to open a file stream, you use the following function:
func AudioFileStreamOpen(
_ inClientData: UnsafeMutablePointer<Void>
, _ inPropertyListenerProc: AudioFileStream_PropertyListenerProc
, _ inPacketsProc: AudioFileStream_PacketsProc
, _ inFileTypeHint: AudioFileTypeID
, _ outAudioFileStream: UnsafeMutablePointer<AudioFileStreamID>) -> OSStatus
Just the type signature of the function makes me start to sweat.
At any rate, the inClientData parameter needs to be an UnsafeMutablePointer<Void>, and the pointer will point to an instance of the same class I am working in. In other words, it needs to be a pointer to self. My approach is to call the function using withUnsafeMutablePointer like this:
var proxy = self
let status = withUnsafeMutablePointer(&proxy) {
AudioFileStreamOpen($0, AudioFileStreamPropertyListener
, AudioFileStreamPacketsListener, 0, &audioFileStreamID)
}
My first question is whether or not I'm using withUnsafeMutablePointer correctly here. I wasn't sure how to get a pointer to self - just writing &self doesn't work, because self is immutable. So I declared proxy as a variable and passed a reference to that, instead. I don't know if this will work or not, but it was the best idea I came up with.
Next, AudioFileStreamPropertyListener and AudioFileStreamPacketsListener are C callback functions. They each get passed the pointer to self that I created using withUnsafeMutablePointer in AudioFileStreamOpen. The pointer is passed in as an UnsafeMutablePointer<Void>, and I need to cast it back to the type of my class (AudioFileStream). To do that, I believe I need to use unsafeBitCast. For example, here is AudioFileStreamPropertyListener:
let AudioFileStreamPropertyListener: AudioFileStream_PropertyListenerProc
= { inClientData, inAudioFileStreamID, inPropertyID, ioFlags in
let audioFileStream = unsafeBitCast(inClientData, AudioFileStream.self)
audioFileStream.didChangeProperty(inPropertyID, flags: ioFlags)
}
That compiles fine, but again I'm not sure if I'm using unsafeBitCast correctly, or if that is even the correct function to be using in this kind of situation. So, is unsafeBitCast the correct way to take an UnsafeMutablePointer<Void> and cast it to a type that you can actually use inside of a C function pointer?
It's interesting that the inClientData "context" param is bridged as UnsafeMutablePointer, since I doubt the AudioToolbox APIs will modify your data. It seems it would be more appropriate if they'd used COpaquePointer. Might want to file a bug.
I think your use of withUnsafeMutablePointer is wrong. The pointer ($0) will be the address of the variable proxy, not the address of your instance. (You could say $0.memory = [a new instance] to change it out for a different instance, for example. This is a bit confusing because its type is UnsafeMutablePointer<MyClass> — and in Swift, the class type is itself a pointer/reference type.)
I was going to recommend you use Unmanaged / COpaquePointer, but I tested it, and realized this does exactly the same thing as unsafeAddressOf(self)!
These are equivalent:
let data = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
let data = unsafeAddressOf(self)
And these are equivalent:
let obj = Unmanaged<MyClass>.fromOpaque(COpaquePointer(data)).takeUnretainedValue()
let obj = unsafeBitCast(data, MyClass.self)
While the Unmanaged approach makes logical sense, I think you can see why it might be prefereable to use unsafeAddressOf/unsafeBitCast :-)
Or, you might consider an extension on Unmanaged for your own convenience:
extension Unmanaged
{
func toVoidPointer() -> UnsafeMutablePointer<Void> {
return UnsafeMutablePointer<Void>(toOpaque())
}
static func fromVoidPointer(value: UnsafeMutablePointer<Void>) -> Unmanaged<Instance> {
return fromOpaque(COpaquePointer(value))
}
}
Then you can use:
let data = Unmanaged.passUnretained(self).toVoidPointer()
let obj = Unmanaged<MyClass>.fromVoidPointer(data).takeUnretainedValue()
Of course, you will need to ensure that your object is being retained for the duration that you expect it to be valid in callbacks. You could use passRetained, but I would recommend having your top-level controller hold onto it.
See some related discussion at https://forums.developer.apple.com/thread/5134#15725.
I'm very new to swift, but proficient in other languages like Java, JavaScript, C, ... I'm lost with Swift syntax when it comes to create expressions. Look at this basic example where I just try to find out if one string is contained into another by calling String.rangeOfString that returns an Optional Range (Range?)
This works as expected:
let LEXEMA:String="http://"
let longUrl:String="http://badgirls.many/picture.png"
let range=longUrl.rangeOfString(LEXEMA);
if (range? != nil) {
// blah
}
Now I'm trying to combine the expression inside the if, something like:
if (longUrl.rangeOfString(LEXEMA)? !=nil) {
// blah
}
But I always get syntax errors, the above yields a "Expected Separator" and can't understand why. Done some more tests:
if (absolutePath.rangeOfString(URL_LEXEMA) !=nil) { }
Expected Separator before "!"
if absolutePath.rangeOfString(URL_LEXEMA) !=nil { }
Braced block of statements is an unused closure
What am I doing wrong?
If you’re coming from other like Java, you might be thinking of optionals like pointers/references, and so used to equating them to nil and if non-nil, using them. But this is probably going to lead to more confusion. Instead, think of them like a container for a possible result, that you need to unwrap to use. if let combines the test and unwrapping operation.
With this in mind, here’s how you could adapt your code:
let LEXEMA: String="http://"
let longUrl: String="http://badgirls.many/picture.png"
if let range = longUrl.rangeOfString(LEXEMA) {
// use range, which will be the unwrapped non-optional range
}
else {
// no such range, perhaps log an error if this shouldn’t happen
}
Note, that ? suffixing behaviour you were using changes in Swift 1.2 so even the code in your question that compiles in 1.1 won’t in 1.2.
It’s possible that sometimes you are whether there was a value returned, but you don’t actually need that value, just to know it wasn’t nil. In that case, you can compare the value to nil without the let:
if longUrl.rangeOfString(LEXEMA) != nil {
// there was a value, but you don't care what that value was
}
That said, the above is probably better expressed as:
if longUrl.hasPrefix(LEXEMA) { }
For starters:
You don't need parenthesis with if statements unless you have nested parenthetical subexpressions that require it.
You don't need to specify the type on the left side of the = of a let or var declaration if Swift can figure it out from the right side of the =. Very often Swift can figure it out, and you can tell that Swift can figure it out, so you can avoid that redundant clutter.
You do need to specify the type if Swift cannot figure out the type from
the right side. Example:
For example, consider the following lines:
let LEXEMA = "http://"
let longUrl = "http://badgirls.many/picture.png"
Swift can figure out that they're strings.
Similarly for this function or class that returns a UIView:
var myView = ViewReturningClassOrFunc()
Consider this:
#IBOutlet var myView : UIView!
In the above line, Swift cannot figure out ahead of time it will be assigned a UIView, so you have to provide the type. By providing a ! at the end you've made it an implicitly unwrapped optional. That means, like ?, you're indicating that it can be nil, but that you are confident it will never be nil at the time you access it, so Swift won't require you to put a ! after it when you reference it. That trick is a time saver and big convenience.
You should NOT add the ? to the line:
if (longUrl.rangeOfString(URL_LEXEMA) !=nil) {
As another answer pointed out, you're missing the let.
if let longUrl.rangeOfString(URL_LEXEMA) {
println("What do I win? :-)")
}
swift is case sensitive language. you need to check about whitespaces as well
if longUrl.rangeOfString(LEXEMA) != nil {
//your condition
}
there should be space between statement != nil
Just add a space between != and nil like:
if longUrl.rangeOfString(LEXEMA) != nil {
// blah
}
I tested your code in playground, an error of Expected ',' separator reported.
And do not forget the rules that 1s and 0s and Airspeed Velocity said.
I'm writing an application Bluetooth-controlled keynote remote. This will be using AppleScript to control Keynote based on interactions with the CoreBluetooth framework.
Consider this class, which requires the use of an optional OSALanguage initializer.
class KeynoteController {
let applescriptLanguage: OSALanguage
init?() {
if let applescriptLanguage = OSALanguage(forName: "AppleScript") {
self.applescriptLanguage = applescriptLanguage
} else {
return nil // Compile error on this line
}
}
}
In this example, I want to fail initializing my KeynoteController if there's no OSALanguage named "AppleScript" (admittedly unlikely, but good design). However, I can't return from my initializer until all stored properties are populated.
I could make applescriptLanguage an optional, but since it's non optional and constant if initialization succeeds, this seems like a hack.
What's the correct way to design this?
The problem seems to come from trying to make applescriptLanguage a non-optional. The compiler wants it to be assigned a value, even if the object is failing initialization.
Fix it by making it optional property (implicitly unwrapped because it should never actually be nil after initialization).
let applescriptLanguage: OSALanguage!
This seems like a compiler error to me, but I have no idea what's going on under the hood.
Alternatively, you could temporarily assign it to a dummy value in the else block. This adds extra initialization time and memory allocation, so probably not the best idea, but it'll work if you really want to have the property be non-optional.
} else {
self.applescriptLanguage = OSALanguage()
return nil
}
I was thinking how this "Powerful Solution" according to apple of the Optional Variables is actually powerful if it's something that we already had in Obj-c?
var mystring: String? = nil
if mystring {
//string is not nil
}
Second Scenario won't compile
var mystring: String = nil
if mystring {
//string is not nil
}
We were able to do this in Obj-C before without any additional set up.
NSString * somestring = #"Test";
if(something != [NSNull null]){
//Do something.
}
or
NSString * anotherstring = nil;
if(anotherstring == [NSNull null]){
//display error.
}
so I am really confused on how this is that powerful as they claim if it already existed in a former language.
Some info about Optional Variables
The optional is a type on its own (actually an enum), and it can hold 2 values:
an actual instance/value of the type the optional is used for (corresponding to the Some enum case)
a nil value (corresponding to the None enum case), which represent the absence of value
The difference with other languages like ObjectiveC is that optionals don't use a valid type value, which can have a meaning in some cases and a different meaning in others.
In objective C, the absence of a reference type is represented by nil, which is actually a pointer to the location 0x00000000 (in a 32 bits scenario).
The absence of a value type instead is usually by convention. A function returning an integer can define -1 as absence of value, but -1 is an integer itself, and if the function can return negative values it cannot be used.
In Swift instead an optional can have either a valid integer value, or None, which is not itself an integer (nor an instance of a class, a struct, or whatever type is used with the optional).
Also, more important, you cannot assign nil to a non optional variable - that results in a compilation error, hence preventing a lot of common bugs that usually are discovered at runtime, and frequently hard to track down.
Last, whereas in objective C you can use nil for reference types, you cannot use for value type (as mentioned above for the integer type). In swift instead an optional can be nil regardless of the contained type - so a Int? can be either an integer or nil.
Swift optionals let you make it explicit whether a variable can be nil, whereas Objective-C is all for guessing games. Less nightmares about EXC_BAD_ACCESS errors. That's where the power lies.
In Objective-C a pointer to an object could be nil, yes. But there was no enforcement about if nil made sense.
NSString *shouldNeverBeNil = #"a string!";
shouldNeverBeNil = nil;
NSLog("Hello, %#", shouldNeverBeNil); // "Hello, "
In ObjC this compiles fine though we should never say hello to nothing. That's a bug.
But if we do the same in Swift it doesn't even compile and we don't get a runtime bug at all.
var shouldNeverBeNil: String = "a string!"
shouldNeverBeNil = nil; // Compilation error.
NSLog("Hello, %#", shouldNeverBeNil); // never happens
Optionals allow you to bless variables with the ability to be nil. Compilation errors are always preferable to runtime errors since it's impossible for an end user of your app to run into a compilation error.
If you want to allow that value to be nil Swift makes you bless it explicitly, as an Optional. Now if it's nil, you explicitly allowed it and Swift reminds you to handle handle both the nil case and the value case in your code.
var okToBeNil: String? = "a string!"
okToBeNil = nil;
if okToBeNil != nil {
NSLog("Hello, %#", okToBeNil!); // never happens
} else {
NSLog("What is your name?")
}
In objC,
NSString *stringValue = #"123s";
NSInteger *intValue = [stringValue integerValue];
NSLog(#"intergerValue %#",intValue);
if(!intValue)
{
NSLog(#"caught null object");
}
else
{
// Do appropriate operation with the not null object
}
prints " interValue (null) "
" caught null object "
and the binding is done safely by using !(not) operator inside if condition...
But whereas, in swift the equivalent snippet using optional variable is
var normalValue : String = "123s"
var optionalValue = normalValue.toInt()
println("optionvalue \(optionalValue)")
if optionalValue {
// Do appropriate operation with the not nil value
}
else{
println("caught null object")
}
this "optional binding" is done in objectiveC also, then what is the exact use of having optional variable/constant. And it's also been said that we can avoid returning null object instead we can return nil value. What is the problem when we return a null object, does it cause performance issues?
Your valid thoughts....
The intention behind optional types was to let programmers make variables that might not have a value. It was the default model in Objective-C, it has been reversed in Swift, because the language requires variables to have a value by default.
Objective-C refers to all objects through pointers (hence the asterisk * after the type name). Since all pointers are allowed to have no value (i.e. nil) one could think of all Objective-C objects as optional, i.e. the corresponding variable may have no value at all.
Since Swift does not have a requirement of C compatibility on the source code level, language designers choose to require objects to have a value of the specified type, and provided support for making variables that may not have a value through optional types.