convert array of string into Double in swift - arrays

I'm trying to convert a string into a double in swift. I managed to extract the string from a website (www.x-rates.com) into an array but I cannot convert it after in a double in order to make some work around this number. Can anyone tell me what I'm supposed to do or what I did wrong? I know that my label don't update now but I will do it later, the first thing that I'm trying to do is the conversion.
thx a lot!
Here is the code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var resultLabel: UILabel!
#IBOutlet weak var moneyTextField: UITextField!
#IBAction func convert(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = URL(string: "https://www.x-rates.com/calculator/?from=EUR&to=USD&amount=1")!
let request = NSMutableURLRequest(url : url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var message = ""
if let error = error {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "<span class=\"ccOutputRslt\">"
if let contentArray = dataString?.components(separatedBy: stringSeperator){
if contentArray.count > 0 {
stringSeperator = "<span"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
message = newContentArray[0]
var message = Float(newContentArray[0])! + 10
}
}
}
}
}
DispatchQueue.main.sync(execute: {
self.resultLabel.text = "the value of the dollar is " + message
}
)}
task.resume()
func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

I will talk about convert an Array of String to Array of Double.
In swift Array has a method called map, this is responsable to map the value from array, example, in map function you will receive an object referent to your array, this will convert this object to your new array ex.
let arrOfStrings = ["0.3", "0.4", "0.6"];
let arrOfDoubles = arrOfStrings.map { (value) -> Double in
return Double(value)!
}
The result will be
UPDATE:
#LeoDabus comments an important tip, this example is considering an perfect datasource, but if you have a dynamic source you can put ? on return and it will work, but this will return an array with nil
like that
let arrOfStrings = ["0.3", "0.4", "0.6", "a"];
let arrOfDoubles = arrOfStrings.map { (value) -> Double? in
return Double(value)
}
Look this, the return array has a nil element
If you use the tips from #LeoDabus you will protect this case, but you need understand what do you need in your problem to choose the better option between map or compactMap
example with compactMap
let arrOfStrings = ["0.3", "0.4", "0.6", "a"];
let arrOfDoubles = arrOfStrings.compactMap { (value) -> Double? in
return Double(value)
}
look the result
UPDATE:
After talk with the author (#davidandersson) of issue, this solution with map ou contactMap isn't his problem, I did a modification in his code and work nice.
first I replaced var message = "" per var rateValue:Double = 0.0 and replacedFloattoDouble`
look the final code
let url = URL(string: "https://www.x-rates.com/calculator/?from=EUR&to=USD&amount=1")!
let request = NSMutableURLRequest(url : url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var rateValue:Double = 0.0;
if let error = error {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "<span class=\"ccOutputRslt\">"
if let contentArray = dataString?.components(separatedBy: stringSeperator){
if contentArray.count > 0 {
stringSeperator = "<span"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
rateValue = Double(newContentArray[0])! + 10
}
}
}
}
}
//
print("Rate is \(rateValue)"); //Rate is 11.167
}
task.resume()
Hope to help you

The reason your code doesn’t work in my opinion is that you have two variables with the same name that are defined in different scopes and you use the wrong one at the end.
At the beginning you define
var message = ""
And then when converting to a number further down
var message = Float(newContentArray[0])! + 10
So change the last line to something like
var number = Float(newContentArray[0])! + 10
And use number in your calculations. Although I think
var number = Double(message)
should work equally fine since you have assigned newContentArray[0] to message already and Double is more commonly used than Float (I don’t understand + 10)

Related

Type '___' has no member 'array'

I am working on this quote app, and I keep running into two errors that just don't want to cooperate with me. It says "Type 'businessQuote' has no member ('array'/'dict')". In the following screen shot, you will see the error on the line. The whole point is to get the app to show a random quote in the text fields provided. Could you please help me? Thank you in advance.
Code with the error
My goal is to get "ImportList" to work
'ImportList' Swift file
If there is another question like this that I have overlooked, I would appreciate it if you could link me to it. But I just really need an answer. Thank you again.
Here's the code with the error:
import Foundation
import UIKit
import Social
class businessQuote: UIViewController {
//============================//
//********** Outlets *********//
//============================//
let utility = Utility()
#IBOutlet weak var quoteDisplay: UILabel!
#IBOutlet weak var authorDisplay: UILabel!
#IBOutlet weak var quoteBackground: UIImageView! //GET BACK TO THIS
//============================//
//********** General *********//
//============================//
let date = NSDate()
var Author: String = ""
var Quote: String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Checks if time is greater then 3pm to change background
let currentTime = utility.currentTime()
if (currentTime >= 15 ) {
quoteBackground.image = UIImage(named: "quote_background.png")
} else {
quoteBackground.image = UIImage(named:"morning_quote_background.png")
}
}
//============================//
//********* New Quote ********//
//============================//
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Generates Random Number
func randomNumber(arrayLength: Int) -> Int {
let unsignedArrayCount = UInt32(arrayLength)
let unsignedRandomNumber = arc4random_uniform(unsignedArrayCount)
let randomNumber = Int(unsignedRandomNumber)
return randomNumber
}
// Importing Quotes plist File
let businessQuotes = ImportList(FileName: "BusinessList")
// Selects Quote
let chosenQuote: String = businessQuote.array[randomNumber(businessQuote
.count())] as! String
let chosenAuthor = businessQuote.dict[chosenQuote]! as String
// Assigns Quote & Author to IBOutlet
Author = chosenAuthor
Quote = chosenQuote
quoteDisplay.text = Quote
authorDisplay.text = Author.uppercaseString
}
}
This is the code with the 'array' and 'dict'
import Foundation
struct ImportList {
let path: String
init(FileName: String) {
self.path = NSBundle.mainBundle().pathForResource("\(FileName)", ofType: "plist")!
}
var dict: Dictionary<String, String> {
return NSDictionary(contentsOfFile: path)! as! Dictionary
}
var array: Array<AnyObject> {
return [String](arrayLiteral: String(dict.keys) { $0 as AnyObject as! String })
}
func count() -> Int {
return array.count
}
}
Thank you!
You have declared variable businessQuotes as:
// Importing Quotes plist File
let businessQuotes = ImportList(FileName: "BusinessList")
But using businessQuote instead, see you are missing "s" at the end. Spelling mistake. Following lines should be:
// Selects Quote
let chosenQuote: String = businessQuotes.array[randomNumber(businessQuotes
.count())] as! String
let chosenAuthor = businessQuotes.dict[chosenQuote]! as String

How to encode an array of CGPoints with NSCoder in Swift?

I am trying to save a copy of my custom class to a file, my class has 2 arrays of CGPoints which I append to every so often, they look like this:
class BlockAttributes: NSObject {
var positions:[CGPoint] = []
var spawns:[CGPoint] = []
}
Everything is working great as far as just as using and accessing the class goes, but archiving it does not work. I can archive arrays of Strings, Bools, and Ints just fine in my other classes but my game fails every time I try to use NSCoder to encode my arrays of CGPoints. Here is my code for archiving:
func encodeWithCoder(coder: NSCoder!) {
coder.encodeObject(positions, forKey: "positions")
coder.encodeObject(spawns, forKey: "spawns")
}
....
class ArchiveData: NSObject {
var documentDirectories:NSArray = []
var documentDirectory:String = ""
var path:String = ""
func saveData(data: BlockAttributes) {
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as! String
path = documentDirectory.stringByAppendingPathComponent("data.archive")
if NSKeyedArchiver.archiveRootObject(data, toFile: path) {
print("Success writing to file!")
} else {
print("Unable to write to file!")
}
}
func retrieveData() -> NSObject {
var dataToRetrieve = BlockAttributes()
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as! String
path = documentDirectory.stringByAppendingPathComponent("data.archive")
if let dataToRetrieve2 = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? BlockAttributes {
dataToRetrieve = dataToRetrieve2 as BlockAttributes
}
return(dataToRetrieve)
}
}
....
And to save:
let archiveData = ArchiveData()
archiveData.saveData(myBlockActionsObject)
I even tried creating my own custom class to save the CGPoints to, which I call MyCGPoint (I read somewhere on SO that creating custom classes for some data types resolves some NSCoder issues):
class MyCGPoint: NSObject {
var x: CGFloat = 0.0
var y: CGFloat = 0.0
init(X: CGFloat, Y: CGFloat) {
x = X
y = Y
}
override init() {
}
}
....
class BlockAttributes: NSObject {
var positions:[MyCGPoint] = []
var spawns:[MyCGPoint] = []
}
But alas, I am still getting this error:
[Game.MyCGPoint encodeWithCoder:]:
unrecognized selector sent to instance 0x137f1d1a0 Game[20953:5814436]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[Game.MyCGPoint encodeWithCoder:]:
unrecognized selector sent to instance 0x137f1d1a0'
Any idea how I can use encodeObject to encode my array of CGPoints/MyCGPoints?
You can convert them to and from strings:
//To string
let point = CGPointMake(0, 0)
let string = NSStringFromCGPoint(point)
//Or if you want String instead of NSString
let string = String(point)
//From string
let point2 = CGPointFromString(string)
CGPoint (and its Cocoa's twin NSPoint) are structs, i.e. value type, so you can't encode them directly. Wrap them in NSValue:
let positionValues = positions.map { NSValue(point:$0) }
let spawnValues = spawns.map { NSValue(point:$0) }
coder.encodeObject(positionValues, forKey: "positions")
coder.encodeObject(spawnValues, forKey: "spawns")
// Decode:
positons = (coder.decodeObjectForKey("positions") as! [NSValue]).map { $0.pointValue }
spawns = (coder.decodeObjectForKey("spawns") as! [NSValue]).map { $0.pointValue }
When you write your custom wrapper class, you have to make it compliant with NSCoding too, which NSValeu had already done for you, for free.

Fetched data from Parse not saving in Array - Swift 2.0

I am working with Parse and would like to download images for offline use. I understand that this is not possible with Local Datastore so I have decided to add them to Core Data.
I have successfully downloaded the PFFiles and put them in to an Array. I am then trying to create an Array for the NSData, but the Array count is always 0 when I use the code below
class DealsDownloadViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var trailID = [Int]()
var trailStep = [Int]()
var dealNumber = [Int]()
var imageFile = [PFFile]()
var imagesArray = [UIImage]()
var imageDataArray = [NSData]()
var number = 0
override func viewDidLoad() {
super.viewDidLoad()
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
let dealsQuery = PFQuery(className: ("Deals"))
dealsQuery.orderByAscending("TrailId")
dealsQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.trailID.append(object["TrailID"] as! Int)
self.trailStep.append(object["TrailStep"] as! Int)
self.dealNumber.append(object["dealNumber"] as! Int)
self.imageFile.append(object["dealImage"] as! PFFile!)
}
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
self.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
} else {print("error here")}
})
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.imagesArray.count)
}
} else {print("problem making arrays")}
}
}
If I move the Print statement up, I just get it printing every iteration of the loop.
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
self.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
} else {print("error here")}
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.imagesArray.count)
})
}
} else {print("problem making arrays")}
}
}
In this case I can see that the data is added to both the imagesArray and imageDataArray.
This seems like such a simple issue but I am going crazy over it. What am I doing wrong, and is this the most efficient way of adding this data to Core Data? Am I overlooking something obvious?
I am new to programming so please do point out any mistakes I have made, and I am especially new as a questioner to stackoverflow (you have been indispensable while learning) so please let me know if you need any information that I have missed.
Thanks for your help.
Update 1
I have tried editing the code as explained in the comments and I am still getting the same result. I have moved the Print statement around on this code and it is still giving me the same results as above.
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
weak var aBlockSelf = self
let image = UIImage(data: imageData!)
aBlockSelf!.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
}
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.imagesArray.count)
})
}
} else {print("problem making arrays")}
}
}
Am I missing something very simple? Thanks again for your help.
Update 2
This is the same code with (I think) the print statements moved outside of the For Loop. This is giving me counts of 9,9,9,0,0 from the print statements, whereas I think I should be expecting 9,9,9,9,9.
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
weak var aBlockSelf = self
let image = UIImage(data: imageData!)
aBlockSelf!.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
}
})
}
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.imagesArray.count)
} else {print("problem making arrays")}
}
}
There is no issue here!
You are being deceived by the way asynchronous block works. Asynchronous block gets queued up to get executed some point later in next run loop.
Your first print statement is just after you pass the code to block which is yet to be executed. Which is why you do see your image array empty.

Swift - Populating an array with NSDictionary String data

I have successfully set up a session to pull json data from a site - it works. However, when I start to populate my array with the data the array is not populating the information. Please help.
Here is my Person class:
class Person {
var name1 = "name"
var info1 = "info"
init(name1: String, info1: String) {
self.name1 = name1
self.info1 = info1
}
}
Here is my setUpPerson function:
func setUpPerson() {
let url = NSURL(string: "http://www.my-sites-jsondata")
if url != nil {
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
let urlError = false
if error == nil {
var urlContent = NSString(data: data!, encoding: NSUTF8StringEncoding) as NSString!
var data: NSData = urlContent.dataUsingEncoding(NSUTF8StringEncoding)!
do {
let jsonObject = try (NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSArray)!
var index1 = 0
while index1 < jsonObject.count {
var maindata = (jsonObject[index1] as! NSDictionary)
var nameA = maindata["Name"] as! String
var infoA = maindata["Info"] as! String
var data1 = Person(name1: nameA, info1: infoA)
self.arrayOfPersons.append(data1)
index1++
}
} catch {
print(urlError)
}
}
})
task.resume()
}
}
Your code looks all good to me.
To me your self.arrayOfPersons is also being set (I just tested it with my test app).
You're probably attempting to print it before it's been set in the async dataTaskWithURL block.
An async block performs in the background, so as the code written immediately after your block executes on the main thread, the code within the async block will not yet be complete.
To me, the code has definitely executed. But because the async code hasn't finished executing yet, you are not seeing it set.
You will have to wait until after the async code executes before checking for the new value.
You can try like this:
dispatch_async(dispatch_get_main_queue(), ^{
// Set your data on property here
});
I was able to successfully complete this project. The answer came in two fold...
I needed dispatch_async(dispatch_get_main_queue()
reloadData()

Swift Array: [AnyObject] vs. [xxxxxxxxClass] and the method "append"

Here's my code. You don't need to look at all of it. I added comments where I'm confused:
class ProductData: NSObject {
var title = ""
var icon = ""
private init(dict: NSDictionary){
title = dict["title"] as! String
icon = dict["icon"] as! String
super.init()
}
class func getTheData(fromJSONPath JSONPath: String) -> [ProductData] {
let JSONData = NSData(contentsOfFile: JSONPath)!
var JSONArray = [[String : AnyObject]]()
do {
JSONArray = try NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions.MutableContainers) as! [Dictionary]
} catch { print("error")}
-----------------------------------------------------------------------------------------
//↓↓↓↓↓↓↓↓↓ different: data = "[AnyObject]()" or "[ProductData]()" ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
var data = [AnyObject]()
// var data = [ProductData]()
for d in JSONArray {
data.append(ProductData(dict: d))
}
return data as! [ProductData]
// return data
//↑↑↑↑↑↑↑↑↑ and here: return "data as! [ProductData]" or "data" ↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
}
I use "var data = [ProductData](), retun data" first. There's no error or warning, but when I run my app, and run to the code data.append(ProductData(dict: d)), it crashes with the error: thread 1:exc_bad_access(code=1,address=0x10). What?!
I found a way to fix it: if I use var datas = [AnyObject]() and return datas as! [ProductData], it works very well.
I am so confused:
Why does [AnyObject] make the code OK?
When I use [ProductData], why does the code: data.append(ProductData(dict: d)) crash?
What is the different between [AnyObject] and [ProductData]?
Your original version works for me (screenshot) (only slightly modified for testing with my data). You shouldn't have to do this dance, something else is causing trouble.
I suggest cleaning up your class a bit and take advantage of Swift 2 using guard, map and error. It will be easier to debug and will work more efficiently anyway.
Here's an example. The only difference is that I'm using NSURL to access the data in my case and I've removed the icon value, but it's easy to change it back to your case.
class ProductData: NSObject {
var title = ""
private init(dict: [String : AnyObject]){
if let t = dict["title"] as? String { self.title = t }
super.init()
}
class func getTheData(fromJSONPath JSONPath: String) -> [ProductData] {
do {
// safely unwrap and typecast the values else return empty array
guard let url = NSURL(string: JSONPath),
let JSONData = NSData(contentsOfURL: url),
let JSONArray = try NSJSONSerialization.JSONObjectWithData(JSONData, options: [])
as? [[String : AnyObject]] else { return [] }
return JSONArray.map() { ProductData(dict: $0) }
} catch {
// this `error` variable is created by the `catch` mechanism
print(error)
// return empty array if unkown failure
return []
}
}
}
let test = ProductData.getTheData(fromJSONPath: "http://localhost:5678/file/test.json")
Note: I'm sure you know it but just in case for the readers, NSData(contentsOf... is a synchronous function, so it will block the main thread (unless executed from a background thread). It's better practice to use asynchronous functions when possible.

Resources