Need to update Swift code to the newest version - ios

Prior to the new Swift version I was using the following code in my app.
Now it launches an exception.
for (i, in 0 ..< len){
let length = UInt32 (letters.length)
let rand = arc4random_uniform(length)
randomString.appendFormat("%C", letters.characterAtIndex(Int(rand)))
}
XCode says:
Expected pattern
Expected "," separator
Expected "in" after for-each pattern
Expected SequenceType expression for for-each loop
Changing the code with the proposed solutions doesn't change the exceptions thrown.
Any help is welcome to update the code to the current Swift version.

For for syntax you are using have been deprecated, and should be changed to
for _ in 0..<len
// rest of your code

the question already has correct answer still i have converted it so posting here may be some get help from it
let len = 5
let letters:NSString = "str"
for i in 0 ..< len {
let length = UInt32 (letters.length)
let rand = arc4random_uniform(length)
let randomString:NSMutableString = ""
randomString.appendFormat("%C", letters.characterAtIndex(Int(rand)))
}
As some of the variable are not shown in the code i have made them based on the parameters

Related

How do i fill up the Textbox input reminder bytes with ascii spaces in Swift 4?

Hi I am trying to use a UITextbox and restrict the number of characters input by the user to 10.
I have looked at using the below link,
Max length UITextField
My Questions,
1.Its not working as characters are depreciated in Swift 4 so the string.characters.count is throwing an error so what would be a workaround in Swift 4?
2.After the user enters his x number of characters, I want to make the reminders that is (limitlength - x) into empty spaces (ascii for space = 32 in decimal) so that reminder of the byte array is equal to dec 32.
I have tried doing this,
if let receivedData = rxCharacteristic?.value
let myByteArray = Array(receivedData) {
let b0 = myByteArray[0]
let b1 = myByteArray[1]
let b2 = myByteArray[2]
let b3 = myByteArray[3]
//Now reading data from textbox input
var userdata = textbox.text
let userdataarray: [UInt8] = Array(userdata!.utf8)
//I tried putting values into myByteArray as below
userdataarray[0] = myByteArray[0]
userdataarray[1] = myByteArray[1]
userdataarray[2] = myByteArray[2]
//The last value in myByteArray will remain unchanged so I'm not overwriting it
So from the question when I try to input textbox data less than its length its throwing an index out of range exception. But I went a little extreme to try the below code.
if(userdataarray[0] != 0 && userdataarray[0] != nil)
{
userdataarray[0] = myByteArray[0]
}
else
{
userdataarray[0] = 32 //Which is space in ascii
userdataarray[0] = myByteArray[0]
}
I don't think it worked but wanted to check on how its properly done?
If I understand your question correctly, then your trials are very much overenginering. In Swift you can just add characters to a String (as long as it is declared as var that is). This just boils down to
let orig = "Hello World"
var copy = orig
while copy.count < 15 {
copy.append(" ")
}
let dta = copy.data(using:.isoLatin1)!
let arr = Array(dta)
Since Swift is using some Unicode-encoding internally it is probably crucial to convert your String to data using a specific encoding if you plan to "directly" transfer it to some device that is limited to a certain character set. Still a lot less code than what you provided.

How to use Optionals to stop a compiler error in Swift [duplicate]

This question already has answers here:
can I split a numeric string using multiple separators in a Swift closure?
(3 answers)
Closed 6 years ago.
I want a way to detect input errors in a string and notify the user.
Take the following example:
let fraction = "15/8"
let fractionArray = fraction.components(separatedBy: "/")
let numerator = Double(fractionArray[0])
let denominator = Double(fractionArray[1])
var linearFactor = numerator! / denominator!
print(numerator!, "/", denominator!, " = ", linearFactor)
But if I force unwrap, invalid characters in the string will force a compile error and I’d rather notify the user that the input string contains an invalid fraction. Optional chaining seems like the way to go but I can’t get the syntax right.
In my code (below), I place the optional chaining operator next to the array as shown including fraction?.components(separatedBy: “/“) but Fix-it tells me to delete it.
If there is a better way than optional chaining to address this problem can someone please explain what I might have missed when I searched for answers here so I can make the code work ? Thanks
let fraction = “15/8”
if let fractionArray = fraction?.components(separatedBy: “/“) {
let numerator = Double(fractionArray[0])
let denominator = Double(fractionArray[1])
var linearFactor = numerator / denominator
print(numerator, "/", denominator, " = ", linearFactor)
} else {
print(“Invalid. Re-enter fraction”)
}
fraction is String not String? so you don't have to use fraction?
components return [] not []?, so you can use fractionArray without unwrap anything
the only thing you have to unwrap is numerator and denominator, their type is Double?
Thanks #OOPer, should check denominator != 0
Thanks #Martin R, should check fractionArray.count == 2
so I'll refactor to the following code:
let fraction = "15/8"
let fractionArray = fraction.components(separatedBy: "/")
guard let numerator = Double(fractionArray[0]),
let denominator = Double(fractionArray[1]),
denominator != 0,
fractionArray.count == 2 else {
print("Invalid. Re-enter fraction, or denominator == 0, or fractionArray.count != 2")
return
}
let linearFactor = numerator / denominator
print(numerator, "/", denominator, " = ", linearFactor)

Decimal to Double conversion in Swift 3

I'm migrating a project from Swift 2.2 to Swift 3, and I'm trying to get rid of old Cocoa data types when possible.
My problem is here: migrating NSDecimalNumber to Decimal.
I used to bridge NSDecimalNumber to Double both ways in Swift 2.2:
let double = 3.14
let decimalNumber = NSDecimalNumber(value: double)
let doubleFromDecimal = decimalNumber.doubleValue
Now, switching to Swift 3:
let double = 3.14
let decimal = Decimal(double)
let doubleFromDecimal = ???
decimal.doubleValue does not exist, nor Double(decimal), not even decimal as Double...
The only hack I come up with is:
let doubleFromDecimal = (decimal as NSDecimalNumber).doubleValue
But that would be completely stupid to try to get rid of NSDecimalNumber, and have to use it once in a while...
Well, either I missed something obvious, and I beg your pardon for wasting your time, or there's a loophole needed to be addressed, in my opinion...
Thanks in advance for your help.
Edit : Nothing more on the subject on Swift 4.
Edit : Nothing more on the subject on Swift 5.
NSDecimalNumber and Decimal are bridged
The Swift overlay to the Foundation framework provides the Decimal
structure, which bridges to the NSDecimalNumber class. The Decimal
value type offers the same functionality as the NSDecimalNumber
reference type, and the two can be used interchangeably in Swift code
that interacts with Objective-C APIs. This behavior is similar to how
Swift bridges standard string, numeric, and collection types to their
corresponding Foundation classes. Apple Docs
but as with some other bridged types certain elements are missing.
To regain the functionality you could write an extension:
extension Decimal {
var doubleValue:Double {
return NSDecimalNumber(decimal:self).doubleValue
}
}
// implementation
let d = Decimal(floatLiteral: 10.65)
d.doubleValue
Another solution that works in Swift 3 is to cast the Decimal to NSNumber and create the Double from that.
let someDouble = Double(someDecimal as NSNumber)
As of Swift 4.2 you need:
let someDouble = Double(truncating: someDecimal as NSNumber)
Solution that works in Swift 4
let double = 3.14
let decimal = Decimal(double)
let doubleFromDecimal = NSDecimalNumber(decimal: decimal).doubleValue
print(doubleFromDecimal)
Swift 5
let doubleValue = Double(truncating: decimalValue as NSNumber)
Decimal in Swift 3 is not NSDecimalNumber. It's NSDecimal, completely different type.
You should just keep using NSDecimalNumber as you did before.
You are supposed to use as operator to cast a Swift type to its bridged underlying Objective-C type. So just use as like this.
let p = Decimal(1)
let q = (p as NSDecimalNumber).doubleValue
In Swift 4, Decimal is NSDecimalNumber. Here's citation from Apple's official documentation in Xcode 10.
Important
The Swift overlay to the Foundation framework provides the Decimal
structure, which bridges to the NSDecimalNumber class. For more
information about value types, see Working with Cocoa Frameworks in
Using Swift with Cocoa and Objective-C (Swift 4.1).
There's no NSDecimal anymore.
There was confusing NSDecimal type in Swift 3, but it seems to be a bug.
No more confusion.
Note
I see the OP is not interested in Swift 4, but I added this answer because mentioning only about (outdated) Swift 3 made me confused.
In Swift open source, the implementation is actually done in Decimal.swift, but it is private. You can re-use the code from there.
extension Double {
#inlinable init(_ other: Decimal) {
if other._length == 0 {
self.init(other._isNegative == 1 ? Double.nan : 0)
return
}
var d: Double = 0.0
for idx in (0..<min(other._length, 8)).reversed() {
var m: Double
switch idx {
case 0: m = Double(other._mantissa.0)
break
case 1: m = Double(other._mantissa.1)
break
case 2: m = Double(other._mantissa.2)
break
case 3: m = Double(other._mantissa.3)
break
case 4: m = Double(other._mantissa.4)
break
case 5: m = Double(other._mantissa.5)
break
case 6: m = Double(other._mantissa.6)
break
case 7: m = Double(other._mantissa.7)
break
default: break
}
d = d * 65536 + m
}
if other._exponent < 0 {
for _ in other._exponent..<0 {
d /= 10.0
}
} else {
for _ in 0..<other._exponent {
d *= 10.0
}
}
self.init(other._isNegative != 0 ? -d : d)
}
}
For swift 5, the function is
let doubleValue = Double(truncating: decimalValue as NSNumber)
the example in the below, show the number of float.
let decimalValue: Decimal = 3.14159
let doubleValue = Double(truncating: decimalValue as NSNumber)
print(String(format: "%.3f", doubleValue)) // 3.142
print(String(format: "%.4f", doubleValue)) // 3.1416
print(String(format: "%.5f", doubleValue)) // 3.14159
print(String(format: "%.6f", doubleValue)) // 3.141590
print(String(format: "%.7f", doubleValue)) // 3.1415900

NSData to Data swift 3

var dataFile: NSData = NSMutableData.init(data: wav.subdataWithRange(NSRange.init(location: currentByte, length: wavDataSize)))
How to me convert this code to using Data with Swift 3? Or how to parseNSRange to Range
Separating Data into smaller Data instances
Assumptions
This answer is the Swift 3 & 4 equivalent of the code in the question. It will produce the same result, dataFile, given the same input values: wav, currentByte and wavDataSize assuming none of the surrounding code changes.
I did not make assumptions about what the variables: wav, dataFile, currentByte or wavDataSize mean. To avoid the variable names implying things not stated in the question, I will use the following names instead: sourceData, subdata, rangeStartByte and subdataLength. I assume the code (not shown in the question) surrounding this would assure that rangeStartByte and subdataLength were in a valid range to avoid an error.
Converting NSRange to Range<Int>
The Swift 2 implementation from the question uses an NSRange defined by a start point and a length like this:
NSRange.init(location: rangeStartByte, length: subdataLength)
The Swift 3 & 4 implementation I propose creates an equivalent Range<Int> defined by a start point and end point like this:
rangeStartByte ..< (rangeStartByte + subdataLength)
I converted an app from Swift 2.2 to 3 which used similar code to upload a photo in smaller chunks. During conversion we missed this nuance and used the Swift 2 implementation's length in place of the Swift 3 & 4 implementation's end point. This caused a defect that was tricky to resolve. The first iteration succeeded but subsequent iterations failed.
Another answer implements the issue I just described as the solution. It uses subdataLength from the length of the Swift 2 range as the end point of the Swift 3 & 4 range. This will produce the same result in the special case where currentByte is 0 and subdataLength is <= the length of the NSData instance (which is why the first iteration succeeded in the issue I described). That assumption was not explicitly stated in the question and yields a less flexible solution for others.
Swift 3 & 4 equivalent
var subdata = sourceData.subdata(in: rangeStartByte ..< (rangeStartByte + subdataLength))
Swift 2.2
(code from question with updated variable names)
var subdata: NSData = NSMutableData.init(data: sourceData.subdataWithRange(NSRange.init(location: rangeStartByte, length: subdataLength)))
Runnable sample code
I've included sample code you can run in a playground demonstrating how this line of code could be used to separate a Data instance into smaller Data instances. The source Data instance is created from a string "ABCDEFGHIJKL". This instance is separated into smaller Data instances of length 5.
Swift 3 & 4 with context
import UIKit
var sourceString = "ABCDEFGHIJKL"
let sourceData = sourceString.data(using: String.Encoding.utf8)! // sourceData is equivalent to "wav" from question
var rangeStartByte = 0 // rangeStartByte is equivalent to "currentByte" from question
let maxSubdataLength = 5
let dataLength = sourceString.lengthOfBytes(using: String.Encoding.utf8)
precondition(maxSubdataLength <= dataLength, "maxSubdataLength must be <= to dataLength")
while rangeStartByte < dataLength {
// subdataLength is equivalent to "wavDataSize" from question
let subdataLength = min(maxSubdataLength, dataLength - rangeStartByte)
// subdata is equivalent to "dataFile" from question
let subdata = Data(sourceData.subdata(in: rangeStartByte ..< (rangeStartByte + subdataLength)))
let subdataString = String(data: subdata, encoding: String.Encoding.utf8) ?? ""
print("'\(subdataString)'")
rangeStartByte += subdataLength
}
The result is:
'ABCDE'
'FGHIJ'
'KL'
Swift 2.2 with context
import UIKit
var sourceString = "ABCDEFGHIJKL"
let sourceData = sourceString.dataUsingEncoding(NSUTF8StringEncoding)! // sourceData is equivalent to "wav" from question
var rangeStartByte = 0 // rangeStartByte is equivalent to "currentByte" from question
let maxSubdataLength = 5
let dataLength = sourceString.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
precondition(maxSubdataLength <= dataLength, "maxSubdataLength must be <= to dataLength")
while rangeStartByte < dataLength {
// subdataLength is equivalent to "wavDataSize" from question
let subdataLength = min(maxSubdataLength, dataLength - rangeStartByte)
// subdata is equivalent to "dataFile" from question
let subdata: NSData = NSMutableData.init(data: sourceData.subdataWithRange(NSRange.init(location: rangeStartByte, length: subdataLength)))
let subdataString = String(data: subdata, encoding: NSUTF8StringEncoding) ?? ""
print("'\(subdataString)'")
rangeStartByte += subdataLength
}
The result is:
'ABCDE'
'FGHIJ'
'KL'
Swift 3 & 4 using NSRange
pedrouan's answer uses NSRange like this:
var subdata: Data = Data(sourceData.subdata(with: NSRange(location: rangeStartByte, length: subdataLength)))
I could not get this to compile initially so I disregarded it. Now I realize that it works if sourceData is declared or cast at NSData and not Data
If you want to run this approach within the "Swift 3 & 4 with context" sample code above, replace the corresponding code in that sample with this:
// subdata is equivalent to "dataFile" from question
let sourceNSData = sourceData as NSData
let subdata = sourceNSData.subdata(with: NSRange(location: rangeStartByte, length: subdataLength))
I'm trying not to use "NS" classes like NSRange where possible so I favored the solution using a Swift Range.
Some 'little' changes in Swift 3.0
var dataFile: Data = Data(wav.subdata(with: NSRange(location: currentByte, length: wavDataSize)))
In Swift 3 your code will be like this one:
var dataFile = sourceData.subdata(in: currentByte..<currentByte+wavDataSize)

Trim end off of string in swift, getting error at runtime

I'm making a calculator app in Swift, once my answer is obtained I want to display it in a UILabel. Only problem is I want to limit said answer to 8 characters. Here is my code:
let answerString = "\(answer)"
println(answer)
calculatorDisplay.text = answerString.substringToIndex(advance(answerString.startIndex, 8))
This does not return any compiler errors but at runtime I get:
fatal error: can not increment endIndex
Any and all help would be greatly appreciated.
There are two different advance() functions:
/// Return the result of advancing `start` by `n` positions. ...
func advance<T : ForwardIndexType>(start: T, n: T.Distance) -> T
/// Return the result of advancing start by `n` positions, or until it
/// equals `end`. ...
func advance<T : ForwardIndexType>(start: T, n: T.Distance, end: T) -> T
Using the second one you can ensure that the result is within the valid bounds of the string:
let truncatedText = answerString.substringToIndex(advance(answerString.startIndex, 8, answerString.endIndex))
Update for Swift 2/Xcode 7:
let truncatedText = answerString.substringToIndex(answerString.startIndex.advancedBy(8, limit: answerString.endIndex))
But a simpler solution is
let truncatedText = String(answerString.characters.prefix(8))
Update for Swift 3/Xcode 8 beta 6: As of Swift 3, "collections move
their index", the corresponding code is now
let to = answerString.index(answerString.startIndex,
offsetBy: 8,
limitedBy: answerString.endIndex)
let truncatedText = answerString.substring(to: to ?? answerString.endIndex)
The simpler solution
let truncatedText = String(answerString.characters.prefix(8))
still works.

Resources