Swift Syntax Error and init()? - ios

I study swift. I have a question about initializers init().
For example, I want to initialize Int.
var number: Int = 20
var number = Int(20)
var number = Int.init(20)
All expression is same?
Second, Why this expression occurs?
var check = "123"
var phoneNum:Int?
if((phoneNum = Int.init(check)) != nil)
{
print("Success");
}
There is no error!
var check = "123"
var phoneNum:Int? = Int.init(check)
if(phoneNum != nil)
{
print("Success");
}

Yes, these all have the same effect:
var number: Int = 20
var number = Int(20)
var number = Int.init(20)
And this is one more way to do it:
var number = 20
This produces an error:
var check = "123"
var phoneNum:Int?
if((phoneNum = Int.init(check)) != nil)
{
print("Success");
}
You get an error (“error: value of type '()' can never be nil, comparison isn't allowed”) because assignment in Swift returns (), the sole value of type Void, but nil is type Optional, which is different than Void. Assignments in Swift cannot generally be used as expressions.

I wanted to add this as a comment to rob's answer but since I don't have enough reputation, here's my answer as? a comment (pun intended ;).
Regarding the last two examples you can also use optional binding to help in an assignment:
var check = "123"
var phoneNumber: Int?
if let number = Int.init(check) {
phoneNumber = number
print("Success")
}
print(phoneNumber)
// Success
// Optional(123)
Changing the check value:
var check = "A23"
var phoneNumber: Int?
if let number = Int.init(check) {
phoneNumber = number
print("Success")
}
print(phoneNumber)
// nil
I hope this helps too.

Related

iterate through two custom arrays and set values if variables are equal Swift

I have an easy question that is also hard at the same time. I have two separate structs (this can also work for classes):
struct FBTweet {
var tweetId: Int? //set
var tweetText: String? //set
}
and
struct Status {
var statusId: Int? //set
var statusText: String? //no value
}
I have an array of both structs var fbTweetArray: [FBTweet] = [] and var statusArray: [Status] = []
I have set every variable in to a certain value in each index in fbTweetArray but I only set the .statusId variable in each index for statusArray. For every statusArray.statusId value in statusArray, there is only one fbTweetArray.tweetId that has the same exact Int value. I am trying to make is so that if these two variables are the same then I should set set
statusArray.statusText to whatever fbTweetarray.tweetText is. So for example only fbTweetArray[1].tweetid = 2346 and statusArray[4].statusId = 2346 have 2346 as their value. There for if fbTweetArray[1].tweetText = "hello friend" then statusArray[4].statusText needs to be set to "hello friend".
So far I have
func testWhat () {
var fbTweetArray: [FBTweet] = []
var statusArray: [Status] = []
for fbTweet in fbTweetArray {
for var status in statusArray {
if (status.statusId == fbTweet.tweetId ) {
status.statusText = fbTweet.tweetText
}
}
}
}
how do I set the for var status in the for loop back into the statusArray since it is now a var and is different than one of the indexes in var statusArray: [Status] = []
Basically, you need only one for/forEach loop to achieve what you want:
var fbTweetArray: [FBTweet] = [
FBTweet(tweetId: 1, tweetText: "1"),
FBTweet(tweetId: 2, tweetText: "2"),
FBTweet(tweetId: 3, tweetText: "3")
]
var statusArray: [Status] = [
Status(statusId: 2, statusText: nil),
Status(statusId: 1, statusText: nil),
Status(statusId: 3, statusText: nil)
]
fbTweetArray.forEach { tweet in
if let index = statusArray.index(where: { $0.statusId == tweet.tweetId }) {
statusArray[index].statusText = tweet.tweetText
}
}
print(statusArray.map { $0.statusText }) // [Optional("2"), Optional("1"), Optional("3")]
Note, that your ids in both structures can be nil. To handle this situation (if both id is nil - objects are not equal) you can write custom == func:
struct Status {
var statusId: Int? //set
var statusText: String? //no value
static func == (lhs: Status, rhs: FBTweet) -> Bool {
guard let lhsId = lhs.statusId, let rhsId = rhs.tweetId else { return false }
return lhsId == rhsId
}
}
...
// rewrite .index(where: ) in if condition
if let index = statusArray.index(where: { $0 == tweet }) { ... }
Also, there is some pro-tip. If you adopt your structs to Hashable protocol, you will be able to place FBTweets and Statuses into Set structure. The benefits of that:
If you instead store those objects in a set, you can theoretically
find any one of them in constant time (O(1)) — that is, a lookup on a
set with 10 elements takes the same amount of time as a lookup on a
set with 10,000.
You can find more in-depth info about it in a new great article by NSHipster.
Your question is interesting only if both the arrays are not ordered.
To find the element from fbTweet array, you can sort it and employ binary search.
Then enumerate status array and find the fbTweet object with the same identifier and modify the status object. It needs to be saved again in the array as structs get copied on write.
extension Array where Element == FBTweet {
func binarySearchFBTweetWith(_ id:Int) -> FBTweet? {
var range = 0..<self.count
while range.startIndex < range.endIndex {
let midIndex = range.startIndex + (range.endIndex - range.startIndex) / 2
guard let tweetId = self[midIndex].tweetId else {
continue
}
if tweetId == id {
return self[midIndex]
} else if tweetId < id {
range = midIndex+1..<range.endIndex
} else {
range = range.startIndex..<midIndex
}
}
return nil
}
}
fbTweetArray.sort{($0.tweetId ?? 0) < ($1.tweetId ?? 0)}
for (index, status) in statusArray.enumerated() {
guard let statusId = status.statusId else {continue}
guard let fbTweet = fbTweetArray.binarySearchFBTweetWith(statusId) else {continue}
var status = status
status.statusText = fbTweet.tweetText
statusArray[index] = status
}
An alternative would be use dictionaries instead of arrays, for better performance and easier implementation (in this case). You can easily get the array of keys and values later If you need.
In this case, the Id would be the Key of the dictionary, and the text the value.

Unwrapping Optional String To Int & Making it non-optional

It has been a long time since I have coded in iOS and I am upgrading the old app to swift 3. I am really struggling with one issue of using optional variables.
I have a textfield which is optional. I want it unwrapped into a non-optional Int so that I can use it in the other functions for calculation.
#IBOutlet weak var txtRuns: UITextField!
func sendScore()
{
let runs = txtRuns.text!
let overs = txtOvers.text!
let balls = txtBalls.text!
let wkts = txtWkts.text!
let target = txtTarget.text!
let totalOvers = txtTotalOvers.text!
let strData = "S|R\(runs)" + getOptionalScoreData(
runs: Int(runs),
wkts: Int(wkts),
overs: Int(overs),
balls: Int(balls),
target: Int(target),
totalOvers: Int(totalOvers)
)
}
func getOptionalScoreData(runs: Int, wkts: Int, overs: Int, balls: Int, target: Int, totalOvers: Int) -> String
{
if ( runs == 0 ) {
return getCurrentRunRate(runs: runs)
}
return "";
}
As you can see, I have so many functions to call and I want this textfield to turn into non-optional INT.
Now I have tried several options that I read over here but the error messages only change. The problem didn't solve.
Current ERROR is
The value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
Please help.
Thanks
.................
Please note that I don't think nested check is a nice idea here because all these variables are independent of each other. One can be nil but other can be still passed and used.
If I nest checks like this, it means that no other value will be passed if runs are nil.
if let runs = txtRuns.text, let runsInInt = Int(runs) {
if let overs = txtOvers.text, let oversInInt = Int(overs) {
if let wkts = txtWkts.text, let wktsInInt = Int(wkts) {
strData = "S|R\(runs)\(overs)\(wkts)" + getOptionalScoreData( runs: runsInInt, overs: oversInInt, wkts: wktsInInt)
}
If you are frequently getting value from UITextField as Int, you can add an extension as follows:
extension UITextField {
var intValue: Int {
get {
if let text = self.text {
return Int(text) ?? 0
}
return 0
}
set {
self.text = String(newValue)
}
}
}
You can add the above as private extension in your viewcontroller too. Now you can rewrite your code as:
func sendScore() {
let strData = "S|R\(txtRuns.intValue)\(overs.intValue)\(wkts.intValue)" + getOptionalScoreData(
runs: txtRuns.intValue,
wkts: wkts.intValue,
overs: overs.intValue,
balls: balls.intValue,
target: target.intValue,
totalOvers: totalOvers.intValue)
)
}
Int(runs) call constructor of Int following:
public init?(_ text: String, radix: Int = default)
Because String to Int might failed due to the String might not a valid integer.
How would you deal with it?
You can reference Sallu's comment.
user ! to guarantee the String in UITextField is absolute a valid integer, or app crash.
runs: Int(runs)!
user ?? to give a default value if the String in UITextField is not a valid integer.
runs: Int(runs) ?? 0
In the case the default value is 0

Swift 2 Instance member 'circleIndexes' cannot be used on type 'GameScene'

I am making a rhythm app, but I can't seem to randomize the circles. Here is my code:
var alternator = 0
var fallTimer:NSTimer?
var flag:Bool = true
let circleIndexes = (0..<5).map { return NSNumber(integer: $0) }
let randomIndexes = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(circleIndexes) as! [Int]
func fallCircleWrapper() {
if (flag == true) {
self.alternator += 1
} else {
self.alternator -= 1
}
if (self.alternator == 0) {
flag = true
} else if (self.alternator == 5) {
flag = false
}
self.hitAreaArray[randomIndexes[self.alternator]].emitNote(self.texture!)
}
The problem is with this line:
let randomIndexes = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(circleIndexes) as! [Int]
which gives the error "Instance member 'circleIndexes' cannot be used on type 'GameScene'". How should I go about fixing this?
Consider this example that reproduces your error on a simpler scale:
func agePlusOne(_ num: Int) -> Int { return num + 1 }
class Cat {
var age = 5
var addedAge = agePlusOne(age) // ERROR!
}
You're trying to use a property /before/ it has been initialized... that is, before init() has finished, thus giving you a self to work with.
A way around this is to use lazy properties... lazy properties are initialized only when first called by an object.. that is, they are properly initialized only after there is a self available.
private(set) lazy var randomIndices: [Int] = {
GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(self.circleIndexes) as! [Int]
}()
computed and static properties are also lazy by default, without needing to use the keyword. Unfortunately, you cannot use lazy let, so the private(set) was added to give you more immutability, but not total.
PS, this is Swift3 so you may need to convert it to Swift2.

Swift 3: What's the safest way to unwrap optional values coming from an array?

First, I initialize the variables to hold the stock data
var applePrice: String?
var googlePrice: String?
var twitterPrice: String?
var teslaPrice: String?
var samsungPrice: String?
var stockPrices = [String]()
I fetch current stock prices from YQL, and put those values into an array
func stockFetcher() {
Alamofire.request(stockUrl).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let json = JSON(responseData.result.value!)
if let applePrice = json["query"]["results"]["quote"][0]["Ask"].string {
print(applePrice)
self.applePrice = applePrice
self.tableView.reloadData()
}
if let googlePrice = json["query"]["results"]["quote"][1]["Ask"].string {
print(googlePrice)
self.googlePrice = googlePrice
self.tableView.reloadData()
}
if let twitterPrice = json["query"]["results"]["quote"][2]["Ask"].string {
print(twitterPrice)
self.twitterPrice = twitterPrice
self.tableView.reloadData()
}
if let teslaPrice = json["query"]["results"]["quote"][3]["Ask"].string {
print(teslaPrice)
self.teslaPrice = teslaPrice
self.tableView.reloadData()
}
if let samsungPrice = json["query"]["results"]["quote"][4]["Ask"].string {
print(samsungPrice)
self.samsungPrice = samsungPrice
self.tableView.reloadData()
}
let stockPrices = ["\(self.applePrice)", "\(self.googlePrice)", "\(self.twitterPrice)", "\(self.teslaPrice)", "\(self.samsungPrice)"]
self.stockPrices = stockPrices
print(json)
}
}
}
in cellForRowAt indexPath function I print to the label
if self.stockPrices.count > indexPath.row + 1 {
cell.detailTextLabel?.text = "Current Stock Price: \(self.stockPrices[indexPath.row])" ?? "Fetching stock prices..."
} else {
cell.detailTextLabel?.text = "No data found"
}
I'm running into the issue of printing Current Stock Price: Optional("stock price"), with the word optional. I gather that this is because I'm giving it an array of optional values, but I sort of have to since I actually don't know if there will be data coming from YQL, one of the 5 stocks might be nil while the others have data. From reading other similar questions I can see that the solution would be to unwrap the value with !, but I'm not so sure how to implement that solution when it's an array with data that might be nil, and not just an Int or something.
How can I safely unwrap here and get rid of the word Optional?
First off:
Any time you repeat the same block of code multiple times and only increase a value from 0 to some max, it is a code smell. You should think about a different way to handle it.
You should use an array to do this processing.
How about a set of enums for indexes:
enum companyIndexes: Int {
case apple
case google
case twitter
case tesla
//etc...
}
Now you can run through your array with a loop and install your values more cleanly:
var stockPrices = [String?]()
Alamofire.request(stockUrl).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let json = JSON(responseData.result.value!)
let pricesArray = json["query"]["results"]["quote"]
for aPriceEntry in pricesArray {
let priceString = aPriceEntry["ask"].string
stockPrices.append(priceString)
}
}
}
And to fetch a price from the array:
let applePrice = stockPrices[companyIndexes.apple.rawValue]
That will result in an optional.
You could use the nil coalescing operator (??) to replace a nil value with a string like "No price available.":
let applePrice = stockPrices[companyIndexes.apple.rawValue] ?? "No price available"
or as shown in the other answer:
if let applePrice = stockPrices[companyIndexes.apple.rawValue] {
//we got a valid price
} else
//We don't have a price for that entry
}
I'm writing this outside of Xcode (so there might be typos), but this kind of logic should work.
if self.stockPrices.count > indexPath.row + 1 {
var txt = "Fetching stock prices..."
if let price = self.stockPrices[indexPath.row] {
txt = price
}
cell.detailTextLabel?.text = txt
} else {
cell.detailTextLabel?.text = "No data found"
}
For safe unwrap use that code:
if let currentStockPrice = self.stockPrices[indexPath.row]
{
// currentStockPrice available there
}
// currentStockPrice unavailable
If you need to unwrap multiple variables in one if after another it may lead to unreadable code. In such case use this pattern
guard let currentStockPrice = self.stockPrices[indexPath.row]
else
{
// currentStockPrice is unavailable there
// must escape via return, continue, break etc.
}
// currentStockPrice is available

trying to return nil if a certain type passed to function in swift

HI related to an earlier question which I kind of made a mess of so re posting. What I need to do is return nil if a certain type of blog post is passed into my function
this is the class instance that I am passing in
class BlogPost {
var type = Int(arc4random_uniform(UInt32(types.count)))
var views : Int?
var author : String
var email : String
var order : Int = 0
init(author : String, email : String, order : Int) {
self.author = author
self.email = email
self.order = order
}
func teaser() -> (authorName : String, numberOfViews : Int) {
author = ""
views = 0
if views == 0 {
println("no one has read your blog yet")
} else {
println("\(author) has \(views)")
}
return(author, views!)
}
}
and this is the function which I am having a problem with
func randomViews(blog : BlogPost?) {
var vids = types[2]
if blog?.type == vids {
return nil
} else {
let viewCount : [Int] = [10, 20, 30, 40, 50]
var unsignedArrayCount = UInt32(viewCount.count)
var unsignedRandom = arc4random_uniform(unsignedArrayCount)
var random = unsignedRandom
switch (random) {
case 10:
println("Nil")
case 10, 20 :
println("0")
default:
random
}
}
}
the error I receive is
"$T4?? does not conform to protocol '_RawOptionSetType'"
hope this is clearer than before and hope you can help.
Many thanks
no probs here is my types array
let types : [String] = ["technology", "memos", "animals"]
I have no also dropped the Int and updated the type within blog class
var type = (arc4random_uniform(UInt32(types.count)))
basically if "animals" is passed in as a blog type I need to return nil otherwise do nothing and just carry on. So just need to know what return type would be best to use? thanks
cheers Rob ok this is what I did I just created a seperate function to access a random element of the array
func randType() -> String {
var unsignedArrayCount = UInt32(types.count)
var unsignedrandom = arc4random_uniform(unsignedArrayCount)
var random = Int(unsignedrandom)
return types[random]
}
then as you said no need for a return type just return
func randomViews(blog : BlogPost?) {
var vids = types[2]
if blog?.type == vids {
//println("NIL")
return
} else {
let viewCount : [Int] = [10, 20, 30, 40, 50]
var unsignedArrayCount = UInt32(viewCount.count)
var unsignedRandom = arc4random_uniform(unsignedArrayCount)
var random = unsignedRandom
switch (random) {
case 10:
println("Nil")
case 10, 20 :
println("0")
default:
random
}
}
}
this seems to work so I ll go with for now. Sorry for the dragged out post but at least I hung in there , didnt quit and got it going (somewhat) thanks
Your randomViews is not specifying the return type:
func randomViews(blog : BlogPost?) -> UInt32? {
I presume it's a UInt32? because that is what I see in the else branch.
Moreover, the else branch is missing a return statement, which I presume should be random. The fixed function should look like:
func randomViews(blog : BlogPost?) -> UInt32? {
var vids = types[2]
if blog?.type == vids {
return nil
} else {
let viewCount : [Int] = [10, 20, 30, 40, 50]
var unsignedArrayCount = UInt32(viewCount.count)
var unsignedRandom = arc4random_uniform(unsignedArrayCount)
var random = unsignedRandom
switch (random) {
case 10:
println("Nil")
case 10, 20 :
println("0")
default:
random
}
return random
}
}
A couple of observations:
You've defined BlogPost.type to be an Int. But I presume that types is still defined as [String]. You need to make sure these two match.
For example, now that you've defined type to be Int, you might want if blog?.type == 2 rather than if blog?.type == types[2]. You don't want to compare your numeric type to a string, but rather to the number 2.
You haven't specified any return type in your definition of randomViews, but you try to do return nil. What do you want randomViews to return (if anything)? Make sure your function's return type matches what you supply in the return statements.

Resources