Typecasting or Initialization, Which Is Better in Swift

Typecasting or initialization, which is better in Swift?

You are talking about casting vs. coercion. It isn't a matter of "recommended". They are completely different things.

  • Casting is merely a way of redescribing something to the compiler. Your redescription must be true already, or you will crash at runtime.

    For example, you can cast an AnyObject to a String (with as!) just in case it is a string; you are simply telling the compiler about this, so that you can then send String-related messages to this object. The cast is legal because String is bridged to NSString and an NSString is an AnyObject, so this AnyObject might indeed be a String. But if you lie and this is not a string, you'll crash later when the app runs and you try to cast to a String something that is not in fact already a String.

  • Coercion makes a new object entirely. It works only just in case the new type has an initializer that accepts the old object.

    For example, you cannot cast between numeric types in Swift. You have to coerce, which is a completely different thing - that is, you must make a new object of a different numeric type, based on the original object. The only way to use an Int8 where a UInt8 is expected is to coerce it: UInt8(x). And this is legal because UInt8 has an Int8 initializer (as you can see in the Swift header):

    extension UInt8 {
    public init(_ v: Int8)
    // ...
    }

Swift type casting and parenthesis

It looks like you can add an arbitrary number of parentheses (e.g. (((Int64)))). The main reason for the parentheses is to make a cast like (object as SomeClass).method()

Initialize a swift variable without knowing the type

You should use definitely use an enum with associated values here. As Schottky said in their answer, you'll need a custom Codable implementation. Here's an example, implementing just 4 of the cases, assuming that User, Badge, Message, Debate are all Codable.

enum UserNotification: Codable {
case getBadge(Badge)
case groupReply(Message, Debate, Message)
case levelUnlock(User)
case userFollowLevelUnlock

var notifyType: String {
switch self {
case .getBadge:
return "get_badge"
case .groupReply:
return "group_reply"
case .levelUnlock:
return "level_unlock"
case .userFollowLevelUnlock:
return "user_follow_level_unlock"
}
}

enum CodingKeys: String, CodingKey {
/*
When decoding/encoding, use convertFromSnakeCase/convertToSnakeCase to convert the key names to/from snake case
No need to hard code them here
e.g.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
*/
case notifyType
case target
case secondTarget
case thirdTarget
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(notifyType, forKey: .notifyType)
switch self {
case .getBadge(let badge):
// you can also encode the unused targets here if your backend needs them
try container.encode(badge, forKey: .secondTarget)
case .groupReply(let msg1, let debate, let msg2):
try container.encode(msg1, forKey: .target)
try container.encode(debate, forKey: .secondTarget)
try container.encode(msg2, forKey: .thirdTarget)
case .levelUnlock(let user):
try container.encode(user, forKey: .target)
case .userFollowLevelUnlock:
break // nothing more to encode in this case
}
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
switch try container.decode(String.self, forKey: .notifyType) {
case "get_badge":
self = .getBadge(
try container.decode(Badge.self, forKey: .secondTarget)
)
case "group_reply":
self = .groupReply(
try container.decode(Message.self, forKey: .target),
try container.decode(Debate.self, forKey: .secondTarget),
try container.decode(Message.self, forKey: .thirdTarget)
)
case "level_unlock":
self = .levelUnlock(
try container.decode(User.self, forKey: .target)
)
case "user_follow_level_unlock":
self = .userFollowLevelUnlock
default:
throw DecodingError.dataCorruptedError(forKey: .notifyType, in: container, debugDescription: "Unknown notifyType")
}
}
}

This way, the notification property can just be:

private var notification: UserNotification

and setResource is trivial.

Setting type of generic struct during initialization (Swift)

All you have to do is to tell the compiler that T is a String

init(fromString: String, separator: String) where T == String {
// ...
}

Does Swift support implicit conversion?

There is no implicitly cast in Swift.

Easy way of conversion in swift is using constructor of particular type.

Like if you want to get Float from double then you can use Float(doubleValue) and Same way if you want to convert float to integer then you can use Int(floatValue).

In your case:

let intValue = UInt8(doubleValue)

Beware that you will lose any value after the decimal point. So, choose a better way. Above conversion is just to help you in understanding.

Note that Swift always chooses Double (rather than Float) when inferring the type of floating-point numbers.

How to typecast a subclassed UIViewController in Swift?

Here is what's happening. It took me days to figure out, but my problem was on how my classes were being exported to the targets:

File Inspector Target Membership Incorrect vs. Correct

This was causing two binary copies of my class, one in the app target and another in the test target. If we pay more attention to the logs we might notice:

Test Case '-[_TtC15mytabletopTests22GameListControllerTest testInstance]'

The above is the test method testInstance and it's part of mytabletopTests execution context. Now lets take a look at the instance pulled from Storyboard:

sut  <_TtC10mytabletop18GameListController: 0xb338d50>

This in turn is running on the mytabletop context. This explains why the tests are unable to typecast to GameListController. The GameListController that it knows of is the one compiled inside the test target.

Since removing the class from the test target makes the class unknown to my test case, now I need to import my app target into the test case:

import XCTest
import UIKit
import mytabletop // LINE ADDED

class GameListControllerTest: XCTestCase {

Now, the only GameListController that the test have access to is the same one the storyboard is instantiating, and I am finally able to type cast it. Here is the new testcase:

import XCTest
import UIKit
import mytabletop

class GameListControllerTest: XCTestCase {
let sut: GameListController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("GameListController") as GameListController

override func setUp() {
super.setUp()
UIApplication.sharedApplication().keyWindow.rootViewController = sut
XCTAssertNotNil(sut.view)
}

func testInstance() {
XCTAssertNotNil(sut)
XCTAssertNotNil(sut.tableView) // UITableViewController property
XCTAssertNotNil(sut.store) // instance property
XCTAssertNotNil(sut.someButton) // outlet
}
}

Now I am able to typecast correctly (scroll to see as GameListController) during the instance initialization. In order to force all outlets to be bound correctly and subviews to render accordingly to the device the test is being run, we can make the view controller the rootViewController for the app and pull the view from it, as seen in the setUp function above. Even myCustomOutlet is now working correctly.

Typecasting in enumeration not working in Swift

This is an enum that has a raw value that is a CGFloat.

What's wrong with the top case is that only a literal number is legal as a raw value. You cannot assign a variable like UIScreen.main.bounds.height. You must write out an actual number, there and then.

Taking a longer view, it looks like what you want here might not be an enum, or might not be an enum that takes a raw value. For example, you can have an enum that has an associated value:

enum CardPosition {
case top(CGFloat)
case middle(CGFloat)
case bottom(CGFloat)
}

Now you can attach the value at initialization time:

let myPosition = CardPosition.top(UIScreen.main.bounds.height)
let myOtherPosition = CardPosition.middle(500)

Note that you cannot mix and match; if we're going to use an associated value, then this enum can't have a fixed raw value.



Related Topics



Leave a reply



Submit