as a coding exercise, I wrote a small program to take MySql data frm the web to the iPhone. On the server side. I wrote the php script to get the script to return the json data.
On xcode I have
[code]
.
.
.
let jsonString = try? JSONSerialization.jsonObject(with: data!, options: [])
print(jsonString!)
.
.
.
[/code]
In xcode console, I have this:
[code]
(
{
Address = "1 Infinite Loop Cupertino, CA";
Latitude = "37.331741";
Longitude = "-122";
Name = Apple;
}
)
[/code]
I have a function
[code]
func convertToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
[/code]
When I pass the jsonString to convertToDictionary(text:)
[code]
let dict = convertToDictionary(text: jsonString as! String)
[/code]
In the console I get an error "Could not cast value of type '__NSSingleObjectArrayI' (0x10369bdb0) to 'NSString' (0x1004eac60)."
but if I hard code the json string then pass it to the convertToDictionary(text:)
[code]
let hardCodedStr = "{\"Address\":\"1 Infinite Loop Cupertino, CA\",\"Latitude\":\"37.331741\",\"Longitude\":\"-122\",\"Name\":\"Apple\"}"
let dict = convertToDictionary(text: hardCodedStr)
print(dict!)
[/code]
It works just fine. Why is that? Thanks
If you look closely at what jsonObject(with:options:) returns, you will see that it is a [String: Any] or a [Any], depending on your JSON.
Therefore, jsonString here actually stores a [String: Any], even thought the compiler thinks it is of type Any:
let jsonString = try? JSONSerialization.jsonObject(with: data!, options: [])
print(jsonString!)
If you try to pass this to convertToDictionary, which accepts a String, it of course will not work, because a dictionary and String are not compatible types.
How to solve this problem?
The problem is already solved! You don't need convertToDictionary at all. jsonString itself is the dictionary you wanted.
This is what you need to do:
let jsonString = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
^^^^^^^^^^^^^^^^^
Add this part
After that you can call dictionary methods on jsonString. I also suggest you to rename jsonString to something else.
Related
XCode 7, Swift 2. (don't even start on me about that, it's not my favorite set of constraints either)
So I'm sending data using node-apn (BIG thanks to those folks!). I'm using the (pushkit option) *.voip topic and I got all that working. I can see the notification is received on my device (big shout out to libimobiledevice).
In composing the note on my server i'm doing
var note = new apn.Notification();
note.topic = 'mine.voip';
note.payload = {
message: 'text',
somethingElse: 'this other one '
payload: {
k1: v1,
k2: {
k3: v2
}
}
};
How am I supposed to get at my payload object? Following some (3rd party) pushkit examples (maybe it was 1? that showed like
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
let payloadDict = payload.dictionaryPayload["aps"] as? Dictionary<String, String>
let message = payloadDict?["alert"]
}
I tried to mimic it like
let myWeirdPayload = payload.dictionaryPayload["payload"] as? Dictionary<String, String>
or even
let myWeirdPayload = payload.dictionaryPayload["payload"] as? Dictionary<String, Any>
but those don't work (I get nil).
How am I supposed to do this?
Here's how I log it in Obj-C:
NSLog(#"pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:%#",
payload.dictionaryPayload);
Let me try to make this clear by explaining the relation between PKPushPayload and its NSDictionary property with the data you are looking for.
For the func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) you have the payload argument which is a PKPushPayload.
The data you are looking for are stored in the _dictionaryPayload which is a NSDictionary and it is a property of the payload argument.
So to access the data you can do:
let payloadDictionary = payload.dictionaryPayload as NSDictionary
let myData = payloadDictionary.value(forKey: "myData_unique_key")
Previously in my APP when I was making a call to the server if there were any errors I did the following:
Alamofire.request(mutableURLRequest).responseJSON{
response in if let JSON = response.result.value{
if JSON.count != 0{
let errorList = JSON["responseErrorsList"] as? NSArray
for error in errorList!{
let erro: String = error as! String
switch erro{
case "PersonRequired":
With the migration from swift 2 to swift 4 I am having problems because theList errors comes to nil however the result of JSON is as follows:
["Rate": , "Level": , "Code": , "ID": 0, "Zone": , "Address": , "ErrorsListServer": <__NSSingleObjectArrayI 0x17400ba90>( InvalidCode ) , "SubZone": ]
Does anyone know how I can access the "ErrorsListServer" field and fetch the errors that in this case is "InvalidCode".
What I have implemented and is not working for is the following:
if let JSON = response.result.value as? [String: Any]{
if (JSON as AnyObject).count != 0{
let errorList = JSON["responseErrorsList"] as? [[String: Any]]
From your JSON response, you need to access key ErrorsListServer and it is Array of String not Array of Dictionary so you need to cast it to [String]. Also you can reduce your code with single if let statement like this way.
if let JSON = response.result.value as? [String: Any],
let errorList = JSON["ErrorsListServer"] as? [String] {
for error in errorList {
print(error)
}
}
I am trying to parse the JSON data from my server and I am getting an error when it hits the try! statement and it is crashing. It is telling me
Code=3840 "Invalid value around character 0.
It my be because I have not updated my code correctly to Swift 3. I was having an issue with if let parse for the longest time until I switched the as to as?
#IBAction func registerButtonTapped(_ sender: Any) {
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
let userRepeatPassword = repeatPasswordTextField.text;
// Check for empty fields
if((userEmail?.isEmpty)! || (userPassword?.isEmpty)! || (userRepeatPassword?.isEmpty)!){
//Display alert message
displayMyAlertMessage(userMessage: "All fields are required");
return;
}
//Check if passwords matech
if(userPassword != userRepeatPassword){
// Display alert message
displayMyAlertMessage(userMessage: "Passwords do not match");
return;
}
// Send user data to server side
let myUrl = URL(string: "http://");
let request = NSMutableURLRequest(url:myUrl!);
request.httpMethod = "Post";
let postString = "email=\(userEmail)&password=\(userPassword)";
//adding the parameters to request body
request.httpBody = postString.data(using: String.Encoding.utf8);
//creating a task to send the post request
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
if error != nil{
print("error=\(error)")
return
}
//parsing the reponse
//converting response to Any
let json = try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments) as? [String:Any]
//parsing JSON
if let parseJSON = json{
let resultValue = parseJSON["status"] as? String
print("result: \resultValue)")
var isUserRegistered:Bool = false;
if(resultValue=="Success") { isUserRegistered = true;}
var messageToDisplay:String = parseJSON["messsage"] as! String;
if(!isUserRegistered)
{
messageToDisplay = parseJSON["message"] as! String;
}
DispatchQueue.main.async {
//Display alert message with confirmation.
let myAlert = UIAlertController(title:"Alert", message:messageToDisplay, preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title:"ok", style:UIAlertActionStyle.default){ action in
self.dismiss(animated: true, completion:nil);
}
myAlert.addAction(okAction);
self.present(myAlert, animated:true, completion:nil);
};
}
}
task.resume()
}
Please help, thanks
The reason of the error is that you are sending literal "Optional(Foo)" strings to the server via String Interpolation. userEmail and userPassword will never match and the server sends no data back. In Swift 3 you have to explicitly unwrap even implicit unwrapped optional strings.
The solution is a waterproof error handling with optional bindings
#IBAction func registerButtonTapped(_ sender: AnyObject) {
// Check for empty fields
guard let userEmail = userEmailTextField.text, !userEmail.isEmpty,
let userPassword = userPasswordTextField.text, !userPassword.isEmpty,
let userRepeatPassword = repeatPasswordTextField.text, !userRepeatPassword.isEmpty else {
//Display alert message
displayMyAlertMessage(userMessage: "All fields are required")
return
}
...
Now all relevant optionals are safely unwrapped and the server will get the right data.
Further trailing semicolons and parentheses around if conditions are not needed in Swift and use URLRequest rather than NSMutableURLRequest in Swift 3
var request = URLRequest(url:myUrl!) // var is mandatory if properties are going to be changed.
PS: In any case – as already mentioned in the comments – never use carelessly try! when receiving data from a server.
Here is my code that is returning a false instead of the json array count.
I must comment that at one point it was working and then it stopped. Please help.
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
let urlError = false
if error == nil {
let urlContent = NSString(data: data!, encoding: NSUTF8StringEncoding) as NSString!
let data: NSData = urlContent.dataUsingEncoding(NSUTF8StringEncoding)!
print("data here\(data)”) // I see a lot of data in the logs with this print
do {
let jsonObject = try (NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSArray)!
print(“here is the array count\(jsonObject.count)”) // it never prints this to the logs. What I get is “false"
…The rest of the code...
I'm hoping someone may be able to help me figure out a snafu I'm having with an app I am trying to write (or learn to write) in Swift 2.0. This previously worked in Swift 1.2, but after the necessary conversions, I am continunally facing the error;
Cannot invoke initializer of type 'NSData' with an argument list of type '(contenOfURL: NSURL, options: NSDataReadingOptions, error:nil)'
Here is my code, slightly truncated, that I am using;
...
class func fetchMinionData() -> [Minion] {
let myURL = "https://myurl/test.json"
let dataURL = NSURL(string: myURL)
let data = NSData(contentsOfURL: dataURL!, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: nil)
//THIS IS THE LINE THAT THROWS THE ERROR
let minionJSON = JSON(data)
var minions = [Minion]()
for (_ , minionDictionary) in minionJSON {
minions.append(Minion(minionDetails: minionDictionary))
}
return minions
}
...
Note that I plan to use the SwiftyJSON library to further parse the data once it is downloaded. I am searching endlessly online, but I just can't seem to figure this out! Thank you!
If you are working with Swift 2, you should not pass the last argument "error". Instead put a try around the NSData initialization. If data needs to be accessed outside take the init result in a var and convert to let Modified code
var optData:NSData? = nil
do {
optData = try NSData(contentsOfURL: dataURL!, options: NSDataReadingOptions.DataReadingMappedIfSafe)
}
catch {
print("Handle \(error) here")
}
if let data = optData {
// Convert data to JSON here
}
Example code for Dictionary :) Swift 2.0
https://github.com/DaRkD0G/LoadExtension/edit/master/LoadExtensionDictionary.swift
enum EHError: ErrorType {
case Nil(String)
case NSData(String)
case JSON(String)
}
extension Dictionary {
/**
Loads a JSON file from the app bundle into a new dictionary
- parameter filename: File name
- throws: PathForResource / NSData / JSON
- returns: Dictionary<String, AnyObject>
*/
static func loadJSONFromBundle(filename: String) throws -> Dictionary<String, AnyObject> {
guard let path = NSBundle.mainBundle().pathForResource(filename, ofType: "json") else {
throw EHError.Nil("[EasyHelper][loadJSONFromBundle][->pathForResource] The file could not be located\nFile : '\(filename).json'")
}
guard let data = try? NSData(contentsOfFile: path, options:NSDataReadingOptions()) else {
throw EHError.NSData("[EasyHelper][loadJSONFromBundle][->NSData] The absolute path of the file not find\nFile : '\(filename)'")
}
guard let jsonDict = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? Dictionary<String, AnyObject> else {
throw EHError.JSON("[EasyHelper][loadJSONFromBundle][->NSJSONSerialization]Error.InvalidJSON Level file '\(filename)' is not valid JSON")
}
return jsonDict
}
}
If I do not do a mistake, for you is that
/**
Loads a JSON file from the app bundle into a new dictionary
- parameter filename: File name
- throws: EHError : PathForResource / NSData / JSON
- returns: [String : AnyObject]
*/
static func loadJSONFromBundle(filename: String, nameJson:String) throws -> [String : AnyObject] {
guard let path = NSBundle.mainBundle().pathForResource(filename, ofType: "json") else {
throw EHError.Nil("[EasyHelper][loadJSONFromBundle][->pathForResource] The file could not be located\nFile : '\(filename).json'")
}
guard let data = try? NSData(contentsOfFile: path, options:NSDataReadingOptions()) else {
throw EHError.NSData("[EasyHelper][loadJSONFromBundle][->NSData] The absolute path of the file not find\nFile : '\(filename)'")
}
guard let jsonDict = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [String : AnyObject] else {
throw EHError.JSON("[EasyHelper][loadJSONFromBundle][->NSJSONSerialization] Invalid JSON\n nameJson '\(nameJson)'\nFile '\(filename)'")
}
return jsonDict
}