Converting [NSURL] into [String] for NSUserDefaults? - swift

I'm saving images into Parse Data as an array of NSURL's. Once I have them back into my app I would like to convert them to [String] so my app can temporarily store them. Any ideas?
Here is my code....
// Saving like This....
vc.videoImageArry = defaults.setObjectForKey("vidImages)
//Retrieving like This....
vc.vidImageArray = defaults.objectForKey("vidImages") as! [NSURL]

Using NSData
You can convert each NSURL to NSData in order to save it
func save(urls: [NSURL]) {
let urlsData = urls.map { $0.dataRepresentation }
NSUserDefaults.standardUserDefaults().setObject(urlsData, forKey: "urlsData")
}
Later on you can retrieve the NSData array and convert it back to [NSURL]
func load() -> [NSURL]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [NSData]
return retrievedData?.map { NSURL(dataRepresentation: $0, relativeToURL: nil) }
}
Using String
Alternatively you can save the urls as String(s)
func save(urls: [NSURL]) {
let urlsData = urls.map { $0.absoluteString }
NSUserDefaults.standardUserDefaults().setObject(urlsData, forKey: "urlsData")
}
func load() -> [NSURL?]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [String]
return retrievedData?.map { NSURL(string: $0) }
}
As discussed in the comments below, if data is written to NSUserDefaults exclusively with the save function, we know that every element of the array is a String representing a valid NSURL.
So we can change the return type of load from [NSURL?]? to [NSURL]? using this alternate version of load.
func load() -> [NSURL]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [String]
return retrievedData?.flatMap { NSURL(string: $0) }
}

To convert from NSURL to String:
String(url)
To convert from String to NSURL:
NSURL(string: string)
Here's a fully working example that converts an array both ways:
import Cocoa
let urls = [NSURL(string: "http://www.swift.org/")!, NSURL(string: "http://www.apple.com/")!]
let strings = urls.map { String($0) }
let backToUrls = strings.map { NSURL(string: $0)! }
I believe that the above answers your specific question.
Having said that, the line for saving doesn't look right to me. You may want to look further into NSUserDefaults or ask a separate question if you're having difficulty with that line. You would need to paste some more context like lines above and below and exact error messages you're getting if any.

Related

Appending JSON data Swift

I am currently working on a school project where I need to be able to read, write and view information from a JSON file. For my assignment, I am attempting to build an app that is a dictionary where you can add your own words, definitions, etc.
I have been stuck on trying to write the new data to the JSON file without overwriting the old.
I also have an error on the last line that I am confused about.
Here is the code that I have so far.
func fileUrl() -> URL {
let documentURL = try!
FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
return documentURL.appendingPathComponent("data.json")
}
#IBAction func addWords(_ sender: UIButton) {
if let oldWords:[[String : String]] = getJsonData() as [[String:String]]? {
if let oldJson = try? JSONSerialization.data(withJSONObject: oldWords, options: []) {
// Add old words to JSON file
}
}
let data: [String:String] = [
"Name": nameField.text ?? "N/A",
"Part of Speech": posField.text ?? "N/A",
"Definition": defView.text ?? "N/A"
]
let url = fileUrl()
if let jsonData = try? JSONSerialization.data(withJSONObject: data, options: []) {
// Append data into JSON file
print(data)
nameField.text = ""
defView.text = ""
posField.text = ""
} else {
print("Failed to save")
}
}
func getJsonData() -> [[String:String]]? {
let url = fileUrl()
let responseData: Data? = try! Data(contentsOf: url)
if let responseData = responseData {
let json: String? = try? JSONSerialization.jsonObject(with: responseData, options: []) as? String
if let dictionary: [[String:String]]? = json as? [[String:String]]? {
return dictionary
}
}
} // Missing return in a function expected to return '[[String : String]]?' error
#IBAction func loadData(_ sender: UIButton) {
let url = fileUrl()
let responseData: Data? = try! Data(contentsOf: url)
if let responseData = responseData {
let json: Any? = try? JSONSerialization.jsonObject(with: responseData, options: [])
if let json = json {
let dictionary: [String: Any]? = json as? [String: Any]
if let dictionary = dictionary {
for names in dictionary {
let name: String = dictionary["Name"] as! String
let definition: String = dictionary["Definition"] as! String
let pos: String = dictionary["Part of Speech"] as! String
print(name, definition, pos)
textView.text = ("Name: \(name) (\(pos))\n Definition: \(definition)\n ")
}
}
}
}
}
I have been researching a way to add the JSON data but maybe I have been staring at the code so long that I am missing an easy fix.
There is no native way in swift, nor with a third party library to append json objects to a file.
What you need to do is, when you call getJsonData -- you save the whole contents of that JSON into a variable and append from there. I recommend looking at Daniel's answer for an easy extension for appending JSON together. Let me know if you need any more help!
Regarding your error on the last line - It is good practice to never force un-wrap variables. It's hard to tell given we can't see the JSON tree; but make sure you are accessing the correct spot. print(dictionary) your dictionary and try some debugging and/or create validation for your array.

Array is null after setting data in it

I have a JSON request that gets data from the Darksky API, I get the data properly and it is showing on the screen. However, When i'm trying to set the data from the array I get from the JSON call in another array, it stays empty.
This is my code:
just declaring the array:
var mForecastArray = [Weather]()
this is the function that calls the API:
func getForecast(){
Weather.forecast(withLocation: "37.8267,-122.4233") { (arr) in
DispatchQueue.main.async {
self.mForecastArray = arr
self.mTodayWeather = arr[0]
self.mCollectionView.reloadData()
}
}
}
The weird part is that it does work, and the data do shows on screen, but still, mForecastArray seems null.
This is the API call itself:
static func forecast(withLocation location: String, completion: #escaping ([Weather]) -> ()){
let url = basePath + location
let request = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
var forecastArray: [Weather] = []
if let data = data{
do{
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]{
if let dailyForecast = json["daily"] as? [String:Any]{
if let dailyData = dailyForecast["data"] as? [[String:Any]]{
for dataPoint in dailyData{
if let weatherObject = try? Weather(json: dataPoint){
forecastArray.append(weatherObject)
}
}
}
}
}
}catch{
print(error.localizedDescription)
}
completion(forecastArray)
}
}
task.resume()
}
It's a visual asynchronous illusion.
The static method forecast works asynchronously.
Most likely your code looks like
getForecast()
print(self.mForecastArray)
This cannot work because the array is populated much later.
Move the print line into the completion handler of the static method
func getForecast(){
Weather.forecast(withLocation: "37.8267,-122.4233") { (arr) in
DispatchQueue.main.async {
self.mForecastArray = arr
print(self.mForecastArray)
self.mTodayWeather = arr[0]
self.mCollectionView.reloadData()
}
}
}

Plist not changing value [swift]

I wanted to implement the in-app version for my app, but I don't understand why is not working? The output showing i was added successfully, but when i open the pList file from my Xcode, its not changing? Why is it? Here is my code:
func version(){
let urlString = jocomAPIKey + "/feed/version"
let CFBundleShortVersionKey = "CFBundleVersion"
//First get the nsObject by defining as an optional anyObject
let nsObject: AnyObject? = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"]
let version = nsObject as! String
print("app version: \(version)")
Alamofire.request(.POST, urlString , parameters: ["os" : "iphone"])
.responseData { response in
let versionXML = SWXMLHash.parse(response.data!)
let versionString = (versionXML["rss"]["channel"]["item"]["version"].element?.text)
print("version string: \(versionString)")
if let plist = Plist(name: "Info") {
let dict = plist.getMutablePlistFile()!
dict[CFBundleShortVersionKey] = versionString
//3
do {
try plist.addValuesToPlistFile(dict)
print("added")
} catch {
print(error)
}
//4
print(plist.getValuesInPlistFile())
} else {
print("Unable to get Plist")
}
}
}
You can't update your (Bundle plist) at run-time. If you want use then you just make one copy on your Document Directory then you can perform any operation.
Write and Read a plist in swift with simple data
Apple not allow to update info.plist update at runtime in app.

Making API call using swift

I am a TOTAL NOOB when it comes to iOS coding.
Im trying to learn how to make an API call to "http://de-coding-test.s3.amazonaws.com/books.json"
however, since I'm a total noob, all tutorials i find make no sense as to how to do it.
All i want to learn is how I can get the JSON data from the web and input it into a UITableViewCell
I have looked through about 3 dozen tutorials, and nothing makes sense.
Any help is appreciated.
Let's going by step:
1) The framework that you're going to use to make api call is NSURLSession (or some library like Alomofire, etc).
An example to make an api call:
func getBooksData(){
let url = "http://de-coding-test.s3.amazonaws.com/books.json"
(NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
//Here we're converting the JSON to an NSArray
if let jsonData = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableLeaves)) as? NSArray{
//Create a local variable to save the data that we receive
var results:[Book] = []
for bookDict in jsonData where bookDict is NSDictionary{
//Create book objects and add to the array of results
let book = Book.objectWithDictionary(bookDict as! NSDictionary)
results.append(book)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//Make a call to the UI on the main queue
self.books = results
self.tblBooks.reloadData()
})
}
}).resume()
}
Book Entity:
class Book{
var title:String
init(title:String){
self.title = title
}
class func objectWithDictionary(dict:NSDictionary)->Book{
var title = ""
if let titleTmp = dict.objectForKey("title") as? String{
title = titleTmp
}
return Book(title: title)
}
}
Note: In practice, you will check error and status code of response, and also you can extract the code of making api call to another class (or service layer).One option, using the pattern of DataMapper, you can create a class Manager by Entities (In this example for book like BookManager) and you can make something like this (You can abstract even more, creating a general api, that receive a url and return an AnyObject from the transformation of the JSON, and from there process inside your manager):
class BookManager{
let sharedInstance:BookManager = BookManager()
private init(){}
func getBookData(success:([Book])->Void,failure:(String)->Void){
let url = "http://de-coding-test.s3.amazonaws.com/books.json"
(NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
if error != nil{
failure(error!.localizedDescription)
}else{
if let jsonData = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableLeaves)) as? NSArray{
var results:[Book] = []
for bookDict in jsonData where bookDict is NSDictionary{
let book = Book.objectWithDictionary(bookDict as! NSDictionary)
results.append(book)
}
success(results)
}else{
failure("Error Format")
}
}
}).resume()
}
}

Cannot subscript a value of type [NSURL]

I am getting the following error. Is there a way around this?
i want to read text file from URL
let requestURL: NSURL = NSURL(string: "http://www.xxxxx.com/file.txt")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
struct FactBook {
let factsArray = [requestURL]
//let factsArray = ["my text"] ---> is working
func randomFact() -> String {
let randomInt = Int(arc4random_uniform(UInt32(factsArray.count)))
//Error here...
return factsArray[randomInt]
}
}
thanks.
randomFact is declared to return a String, factsArray is an [NSURL], hence indexing it is an NSURL. Change the function type to:
func randomFact() -> NSURL {
and you should be good to go
Further observations:
You're not ever loading the contents of the URL, so it's not clear that even this change really solves your problem.
As it stands, there's only one element of factsArray so randomizing it seems pointless.
It seems likely you're really wanting to strings from the URL and then return a random element of the returned strings.