Is it possible to create an optional literal in Swift? - objective-c

I have explained my query in the code snippet below. I am looking for this type of syntax for Obj-C interoperability. Specifically I see a difference in behaviour of aCoder.encode(count, forKey: "count") API when count is Int (non-optional) vs Int? (optional)
import Foundation
let num = 5
// Swift's type system will infer this as Int (non-optional)
print(type(of: num))
// Prints: Int
let optNum: Int? = 5
// This is explicitly typed as an optional Int
print(type(of: optNum))
// Prints Optional<Int>
Is it possible to use a literal to implicitly type a var/let to optional?
// let imlicitOptional = 5?
// print(type(of: imlicitOptional))
// The above line should print: Optional<Int>
// or
// let imlicitOptional = num?
// print(type(of: imlicitOptional))
// The above line should print: Optional<Int>

Optional is a plain enum not any specific magic type. So, you can create a value using Optional:
let implicitOptional = Optional(5)
print(type(of: implicitOptional)) // Optional<Int>

I don't know why you need this but you can do like this
let opt = 5 as Int?
// or
let opt = Optional(5)
// or
let opt = 5 as Optional // thanks to vacawama for this
Actually you can even create an operator that returns an optional but I think it's kinda useless.
postfix operator >?
postfix func >?<T>(value: T) -> T? {
return Optional(value) // or return value as T?
}
let a = 5>?

Related

Extract value out of Kotlin arrow Either type and assign it to const

It would be a basic question, but I couldn't figure out a solution. I need to initialize a constant out of the right-side value of below either type.
val test: Either<String, Int> = 1.right()
I tried something like below but it shrinks the scope of the constant.
when(test) {
is Either.Right -> {val get:Int = test.b}
is Either.Left -> println(test.a)
}
I want that get to be scoped outside of when statement. Is there any way to do it or Arrow Either is not made for this purpose?
The important question is: what should happen if the Either is Left. In this example it is created close to where it's used, so it is obvious to you as a developer. But to the compiler what is inside the Either can be either an Int or a String.
You can extract the value using for example fold:
val x = test.fold({ 0 }, {it}) // provide 0 as default in case the Either was a `Left`
// x = 1
another option is getOrElse
val test = 1.right()
val x = test.getOrElse { 42 } // again, default in case it was a `Left`
// x = 42
You can also work with it without unwrapping it:
val test = 1.right()
val testPlus10 = test.map { it + 10 } // adds 10 to `test` if it is `Right`, does nothing otherwise
val x = testPlus10.getOrElse { 0 } // unwrap by providing a default value
// x = 11
For more example check the official docs.
Recommended reading: How do I get the value out of my Monad

Unable to understand the certain operator in Swift

I have been changing some SWIFT code into OBJECTIVE-C, and I am stuck at certain part of the code, where I am unable to understand if it is a condition or something else.
Following is the code and I am stuck on 9th line stating :
if let channel1Buffer = buffer.floatChannelData?[0]
What I do not understand here is the above if condition is checking if "buffer.floatChannelData" is null, and then proceeding to get the first index, or is it something else.
input.installTap(onBus: 0, bufferSize:4096, format:format, block: { [weak self] buffer, when in
guard let this = self else {
return
}
print("Buffer Float Channel Data: ", buffer.floatChannelData as Any);
**if let channel1Buffer = buffer.floatChannelData?[0]** {
print("channel1Buffer: ", channel1Buffer);
/// encode PCM to mp3
let frameLength = Int32(buffer.frameLength) / 2;
print("frameLength: ", frameLength);
let bytesWritten = lame_encode_buffer_interleaved_ieee_float(this.lame, channel1Buffer, frameLength, this.mp3buf, 4096);
// `bytesWritten` bytes stored in this.mp3buf now mp3-encoded
print("\(bytesWritten) encoded");
this.file.append(this.mp3buf, length: Int(bytesWritten));
// #TODO: send data, better to pass into separate queue for processing
}
})
Let's take it part by part - buffer.floatChannelData?[0]
buffer has property named floatChannelData which is optional so it has ? at the end. then it takes that optional which accepts subscription [0] which also returns optional value. So it continues inside {} only if floatChannelData is not nil AND it's first value is not nil
Your Objc should look like
float *const *channelData = [buffer floatChannelData];
if (channelData) {
float *channel1Buffer = channelData[0]; //this might crash if channelData is empty
...
The line tries to assign the variable channel1Buffer the value of buffer.floatChannelData[0], and the code within {} is only executed if that assignment is successful. It may for instance fail if buffer.floatChannelData is nil or buffer.floatChannelData[0] is nil.

How can I tell Kotlin that an array or collection cannot contain nulls?

If I create an array, then fill it, Kotlin believes that there may be nulls in the array, and forces me to account for this
val strings = arrayOfNulls<String>(10000)
strings.fill("hello")
val upper = strings.map { it!!.toUpperCase() } // requires it!!
val lower = upper.map { it.toLowerCase() } // doesn't require !!
Creating a filled array doesn't have this problem
val strings = Array(10000, {"string"})
val upper = strings.map { it.toUpperCase() } // doesn't require !!
How can I tell the compiler that the result of strings.fill("hello") is an array of NonNull?
A rule of thumb: if in doubts, specify the types explicitly (there is a special refactoring for that):
val strings1: Array<String?> = arrayOfNulls<String>(10000)
val strings2: Array<String> = Array(10000, {"string"})
So you see that strings1 contains nullable items, while strings2 does not. That and only that determines how to work with these arrays:
// You can simply use nullability in you code:
strings2[0] = strings1[0]?.toUpperCase ?: "KOTLIN"
//Or you can ALWAYS cast the type, if you are confident:
val casted = strings1 as Array<String>
//But to be sure I'd transform the items of the array:
val asserted = strings1.map{it!!}
val defaults = strings1.map{it ?: "DEFAULT"}
Why the filled array works fine
The filled array infers the type of the array during the call from the lambda used as the second argument:
val strings = Array(10000, {"string"})
produces Array<String>
val strings = Array(10000, { it -> if (it % 2 == 0) "string" else null })
produces Array<String?>
Therefore changing the declaration to the left of the = that doesn't match the lambda does not do anything to help. If there is a conflict, there is an error.
How to make the arrayOfNulls work
For the arrayOfNulls problem, they type you specify to the call arrayOfNulls<String> is used in the function signature as generic type T and the function arrayOfNulls returns Array<T?> which means nullable. Nothing in your code changes that type. The fill method only sets values into the existing array.
To convert this nullable-element array to non-nullable-element list, use:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
Which is fine because your map call converts to a list anyway, so why not convert beforehand. Now depending on the size of the array this could be performant or not, the copy might be fast if in CPU cache. If it is large and no performant, you can make this lazy:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asSequence().filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
Or you can stay with arrays by doing a copy, but really this makes no sense because you undo it with the map:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings: Array<String> = Array(nullableStrings.size, { idx -> nullableStrings[idx]!! })
Arrays really are not that common in Java or Kotlin code (JetBrains studied the statistics) unless the code is doing really low level optimization. It could be better to use lists.
Given that you might end up with lists anyway, maybe start there too and give up the array.
val nullableStrings = listOf("a","b",null,"c",null,"d")
val strings = nullableStrings.filterNotNull()
But, if you can't stop the quest to use arrays, and really must cast one without a copy...
You can always write a function that does two things: First, check that all values are not null, and if so then return the array that is cast as not null. This is a bit hacky, but is safe only because the difference is nullability.
First, create an extension function on Array<T?>:
fun <T: Any> Array<T?>.asNotNull(): Array<T> {
if (this.any { it == null }) {
throw IllegalStateException("Cannot cast an array that contains null")
}
#Suppress("CAST_NEVER_SUCCEEDS")
return this as Array<T>
}
Then use this function new function to do the conversion (element checked as not null cast):
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asNotNull() // magic!
val upperStrings = strings.map { it.toUpperCase() } // no error
But I feel dirty even talking about this last option.
There is no way to tell this to the compiler. The type of the variable is determined when it is declared. In this case, the variable is declared as an array that can contain nulls.
The fill() method does not declare a new variable, it only modifies the contents of an existing one, so it cannot cause the variable type to change.

Objective C ^ Declare block in Swift [duplicate]

I am trying to rewrite from Objective-C to Swift, I cannot work out the syntax or understand the docs
Here is a simplified example in Objective-C I wrote:
[UIView animateWithDuration:10.0 animations:^{self.navigationController.toolbar.frame = CGRectMake(0,10,0,10);}];
How do I write this in Swift?
This is the template autocomplete gives:
UIView.animateWithDuration(duration: NSTimeInterval, animations: (() -> Void))
This is the swift closure format:
{(parameter:type, parameter: type, ...) -> returntype in
//do stuff
}
This is what you should do:
//The animation closure will take no parameters and return void (nothing).
UIView.animateWithDuration(duration: NSTimeInterval, animations: {() -> Void in
//Animate anything.
})
Here is the documentation for closures.
Since the expected argument types and return type to the animations argument are known the compiler can infer them without a problem. This should work (though I don't have the playground available right at the moment:
UIView.animateWithDuration(10.0, animations: {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
})
for more info about closures see the chapter in the swift docs
note about CGRect() - the developer docs show CGRect() being used in swift code. Perhaps it requires an import?
update for comments: you can also use a trailing closure like so:
UIView.animateWithDuration(10.0) {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
}
Following code can guide to write your own block.
class func testFunc(completion: ((list : NSArray!) -> Void)?) {
//--- block code.
if completion! != nil {
completion! (list: NSArray())
}
}
and you can call it like -
className.testFunc {
(list: NSArray!) -> Void in
}
You can basically write it in 3 identical ways:
write what to do right in the closure/code block:
UIView.animateWithDuration(10.0) {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
}
This is also known as trailing closure ( You can only do trailing closure if the closure parameter is the last parameter)
This doesn't mean the parameter 'animations' is no longer written. It is written but just as in the format of above.
Write exactly within the lines, most developers avoid such, because it's a little buggy to write with all the parenthesis and braces.
UIView.animateWithDuration(10.0, animations: {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
})
(Contrary to trailing closure you wrote name ie 'animations')
This is known as inline closure
Write in a more modular sense
UIView.animateWithDuration(duration: NSTimeInterval, animations: animatingFunc)
func animatingFunc() {
self.navigationController.toolbar.frame = CGRect(x:0.0, y:10.0, width:10.0, height:0.0)
}
Remember the type of the parameter 'animations' was () -> Void
Exactly as what we are doing, animatingFunc takes no parameters ie '()' and returns nothing ie 'void'
(In Swift, functions are types and can be passed in as parameters)
Some might say this is more readable some might say trailing closure is...
Side note1
You can also do nothing ( which really doesn't make sense but in many other handlers/animations/completion handlers you may not want to do anything)
UIView.animateWithDuration(duration: NSTimeInterval, animations: nil)
Side note2
Closures becomes more interesting when you have to capture a value. See this simple demonstration.
For more information about Swift closures see Apple's Documentation
How Do I Declare a Closure in Swift?
As a variable:
var closureName: (ParameterTypes) -> ReturnType
As an optional variable:
var closureName: ((ParameterTypes) -> ReturnType)?
As a type alias:
typealias ClosureType = (ParameterTypes) -> ReturnType
As a constant:
let closureName: ClosureType = { ... }
As a parameter to another function:
funcName(parameter: (ParameterTypes) -> ReturnType)
Note: if the passed-in closure is going to outlive the scope of the method, e.g. if you are saving it to a property, it needs to be annotated with #escaping.
As an argument to a function call:
funcName({ (ParameterTypes) -> ReturnType in statements })
As a function parameter:
array.sorted(by: { (item1: Int, item2: Int) -> Bool in return item1 < item2 })
As a function parameter with implied types:
array.sorted(by: { (item1, item2) -> Bool in return item1 < item2 })
As a function parameter with implied return type:
array.sorted(by: { (item1, item2) in return item1 < item2 })
As the last function parameter:
array.sorted { (item1, item2) in return item1 < item2 }
As the last parameter, using shorthand argument names:
array.sorted { return $0 < $1 }
As the last parameter, with an implied return value:
array.sorted { $0 < $1 }
As the last parameter, as a reference to an existing function:
array.sorted(by: <)
As a function parameter with explicit capture semantics:
array.sorted(by: { [unowned self] (item1: Int, item2: Int) -> Bool in return item1 < item2 })
As a function parameter with explicit capture semantics and inferred parameters / return type:
array.sorted(by: { [unowned self] in return $0 < $1 })
This site is not intended to be an exhaustive list of all possible uses of closures. ref: http://goshdarnclosuresyntax.com/

How do I access an enum in Swift from Objective-C?

There is a global enum defined in Objective-C:
typedef enum {
UMSocialSnsTypeNone = 0,
UMSocialSnsTypeQzone = 10,
UMSocialSnsTypeSina = 11, //sina weibo
} UMSocialSnsType;
This code sets the sharetype of a platform:
snsPlatform.shareToType = UMSocialSnsTypeDouban;
In Swift, I want to get the sharetype of the platform:
var snstype = snsPlatform!.shareToType
println(snstype)
Result: UMSocialSnsType (has 1 child)
snstype.toRaw()
Error: UMSocialSnsType does not have a member named "toRaw"
From what I can tell, UMSocialSNSType was declared in Objective-C without using the NS_ENUM macro, so it wasn't imported as a Swift enum. That means that instead of being able to use .toRaw() or UMSocialSNSType.Douban you have to use the different enumeration values as constant structs. Unfortunately the type also doesn't have the appropriate operators (== or ~=) set up, so you have to compare the value property.
var snstype = snsPlatform!.shareToType
switch snstype.value {
case UMSocialSnsTypeDouban.value:
println("douban")
case UMSocialSnsTypeEmail.value:
println("email")
default:
println("other")
}
if snstype.value == UMSocialSnsTypeDouban.value {
println("douban")
}
The good news is that it looks like all the constants autocomplete in Xcode, so you should be able to do find the comparisons you need to do that way.
It looks like the Swift-version of the bridged typedef...enum must be something like:
struct UMSocialSnsType {
var value:Int
init(_ val:Int) {
value = val
}
}
let UMSocialSnsTypeNone = UMSocialSnsType(0)
let UMSocialSnsTypeQzone = UMSocialSnsType(10)
let UMSocialSnsTypeSina = UMSocialSnsType(11)
// etc
Whereas if it had been declared in Objective-C with the NS_ENUM macro, it would look like:
enum UMSocialSnsType: Int {
case UMSocialSnsTypeNone = 0
case UMSocialSnsTypeQzone = 10, UMSocialSnsTypeSina // etc.
}