How to get array key values from loop in Swift 3 - ios

I'm updating my code from Swift 2.3 to Swift 3, and I am facing these difficulties to get an array key value from a loop in Swift:
var countryarray = NSMutableArray()
self.GetCountriesResult = (responseJSON.objectForKey("GetCountriesResult") as? NSArray)!
for i in 0 ..< self.GetCountriesResult.count {
self.countryarr = self.GetCountriesResult.objectAtIndex(i).objectForKey("countryname") as? String ?? ""
self.countryarray.addObject(self.countryarr)
}

map is better suited than for-in in this case:
guard let json = responseJSON["GetCountriesResult"] as? [String : AnyObject]
else { return }
self.GetCountriesResult = json
let countryArray = self.GetCountriesResult.map {
return $0["countryname"] as? String ?? ""
}
// OR
guard let json = responseJSON["GetCountriesResult"] as? [String : AnyObject]
else { return }
let countries = json.map {
return $0["countryname"] as? String ?? ""
}
Suggestion: Read the following Swift style guides:
https://swift.org/documentation/api-design-guidelines/
https://github.com/raywenderlich/swift-style-guide
https://github.com/linkedin/swift-style-guide
https://github.com/github/swift-style-guide

Related

I am unable to pass data from model class to table view in swift 3?

In this I am getting data from server response after posting parameters and here I need to display it on table view and it should be displayed like shown below in the image 0 is the price for the particular shipping method
already i had written model class for server response data and here it is
struct ShippingMethod {
let carrierCode : String
let priceInclTax : Int
let priceExclTax : Int
let available : Any
let carrierTitle : String
let baseAmount : Int
let methodTitle : String
let amount : Int
let methodCode : String
let errorMessage : Any
init(dict : [String:Any]) {
self.carrierCode = dict["carrier_code"] as! String
self.priceInclTax = dict["price_incl_tax"]! as! Int
self.priceExclTax = dict["price_excl_tax"]! as! Int
self.available = dict["available"]!
self.carrierTitle = dict["carrier_title"] as! String
self.baseAmount = dict["base_amount"]! as! Int
self.methodTitle = dict["method_title"]! as! String
self.amount = dict["amount"]! as! Int
self.methodCode = dict["method_code"] as! String
self.errorMessage = (dict["error_message"] != nil)
}
}
by using this I had formed an array type like this by using code
var finalDict = [String: [String]]()
var responseData = [ShippingMethod]()
do
{
let array = try JSONSerialization.jsonObject(with: data, options: []) as? [[String : Any]]
for item in array! {
self.responseData.append(ShippingMethod.init(dict: item))
}
print(self.responseData)
}
catch let error
{
print("json error:", error)
}
print(self.responseData)
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [String]()
}
self.finalDict[carrierTitle]!.append(methodTitle)
}
print(self.finalDict)
the output of this finalDict is ["Flat Rate": ["Fixed"], "Best Way": ["Table Rate"]] in this carrier title key value pair should be displayed as section title and is Flat Rate and method title key value pair should be displayed as rows in section Fixed but the problem is I need amount key value pair with it also for corresponding method title can anyone help me how to get this ?
Why don't you create another struct for displaying row data:
struct CarrierInfo {
let name:String
let amount:Int
}
Change your finalDict to
var finalDict = [String: [CarrierInfo]]()
and create CarrierInfo instance and set it in finalDict
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
let amount = dict.amount
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [CarrierInfo]()
}
self.finalDict[carrierTitle]!.append(CarrierInfo(name: carrierTitle, amount: amount))
}
Likewise you can make other required changes. This would neatly wrap your row display data inside a structure.
PS: I have not tested the code in IDE so it may contain typos.
You can assign another dictionary with key as methodTitle and amount as value. i.e., ["fixed":"whatever_amount"]
OR
You can use finalDict differently, like ["Flat Rate": ["tilte":"Fixed","amount":"0"], "Best Way": ["title":"Table Rate","amount":"0"]]
If it is difficult for you to code this, you can revert back.
Edit
You can use the following code to create the array in the second solution I suggested above:
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
let amount = dict.amount
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [[String:String]]()
}
let innerDict = ["title":methodTitle,"amount":amount]
self.finalDict[carrierTitle]!.append(innerDict)
}

swift 3.0 How can I access `AnyHashable` types in `Any` in Swift 3?

I'm using sqlite file to get the diaryEntriesTeacher from the authorId. it generates the following object of authorId when I print the variable authorId is nil
Code :-
func applySelectQuery() {
checkDataBaseFile()
objFMDB = FMDatabase(path: fullPathOfDB)
objFMDB.open()
objFMDB.beginTransaction()
do {
let results = try objFMDB.executeQuery("select * from diaryEntriesTeacher", values: nil)
while results.next() {
let totalCount = results.resultDictionary
let authorId = totalCount?["authorId"]!
print("authorId",authorId)
}
}
catch {
print(error.localizedDescription)
}
print(fullPathOfDB)
self.objFMDB.commit()
self.objFMDB.close()
}
output
This is how you access Dictionary of [AnyHashable : Any]
var dict : Dictionary = Dictionary<AnyHashable,Any>()
dict["name"] = "sandeep"
let myName : String = dict["name"] as? String ?? ""
In your case
let authorId = totalCount?["authorId"] as? String ?? ""
We need to convert the property we are trying to access to AnyHashable before using it.
In your case :
do {
let results = try objFMDB.executeQuery("select * from diaryEntriesTeacher", values: nil)
while results.next() {
let totalCount = results.resultDictionary
let authorId = totalCount?[AnyHashable("authorId")]!
print("authorId",authorId)
}
This is Swift. Use strong types and fast enumeration. Dictionary<AnyHashable,Any> is the generic type of a dictionary and can be cast to <String,Any> as all keys seem to be String.
do
if let results = try objFMDB.executeQuery("select * from diaryEntriesTeacher", values: nil) as? [[String:Any]]
for item in results {
let authorId = item["authorId"] as? String
let studentName = item["studentName"] as? String
print("authorId", authorId ?? "n/a")
print("studentName", studentName ?? "n/a")
}
}
....

Swift Firebase - Contextual type 'AnyObject' cannot be used with dictionary literal

I'm getting this error which I can't figure out how to fix:
Contextual type 'AnyObject' cannot be used with dictionary literal
I've searched on the internet but failed to find an answer. Here's my code:
struct Sweet {
let key:String!
let content:String!
let addedByUser:String!
let itemReft:FIRDatabaseReference?
init (content:String, addedByUser:String, key:String = "") {
self.key = key
self.content = content
self.addedByUser = addedByUser
self.itemReft = nil
}
init (snapshot:FIRDataSnapshot) {
key = snapshot.key
itemReft = snapshot.ref
if let dict = snapshot.value as? NSDictionary, let postContent = dict["content"] as? String {
content = postContent
} else {
content = ""
}
if let dict = snapshot.value as? NSDictionary, let postUser = dict["addedByUser"] as? String {
addedByUser = postUser
} else {
addedByUser = ""
}
}
func toAnyObject() -> AnyObject {
return ["content":content, "addedByUser":addedByUser]
}
The error happens at this line:
return ["content":content, "addedByUser":addedByUser]
I've been following this tutorial iOS Swift Tutorial: Get started with Firebase and an App like Twitter
Thanks for your time!
You have to cast the literal to the desired type
func toAnyObject() -> Any {
return ["content":content, "addedByUser":addedByUser] as Any
}
But - no offense – casting up a specific type to something more unspecific is silly. Why not
func toDictionary() -> [String:String] {
return ["content":content, "addedByUser":addedByUser]
}

API doesn't contain any value for some objects and Xcode gives a fatal error when running the app

My app takes some data from this API: https://api.jqestate.ru/v1/properties/country
GitHub link to my project: https://github.com/armansharvel/JQ-Estate.git (download branch "Refreshing")
There are no compiler errors but when I run my app in the simulator Xcode prints in console "Fatal error: Index out of range".
In the ObjectModel.swift I created a class of the object with some data types. One of them is the variable mainPic (URL of picture for TableVeiw that I want to get from the API also). But the problem is not every object in the API contains value of URL of the picture.
So Xcode (when I try to run the app) marks the second line of code block that initialises mainPic variable and the error is: "Thread 7: EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0)"
Here is the whole class in code:
import Foundation
class Houses {
// Data Encapsulation
private var _mainPic: String
private var _localityName: String
private var _routeName: String
private var _mkadDistance: String
private var _rentOffer: String
private var _saleOffer: String
// Make a getted
var mainPic: String {
return _mainPic
}
var localityName: String {
return _localityName
}
var routeName: String {
return _routeName
}
var mkadDistance: String {
return _mkadDistance
}
var rentOffer: String {
return _rentOffer
}
var saleOffer: String {
return _saleOffer
}
// Initialization
init(data: JSONDictionary) {
// Main Picture
if let images = data["images"] as? JSONArray,
pic0 = images[0] as? JSONDictionary, // THIS LINE IS WITH ERROR
mainPic = pic0["url"] as? String {
self._mainPic = mainPic
} else {
_mainPic = ""
}
// Locality Name
if let location = data["location"] as? JSONDictionary,
localityName = location["localityName"] as? String {
self._localityName = localityName
} else {
_localityName = ""
}
// Route Name
if let location = data["location"] as? JSONDictionary,
routeName = location["routeName"] as? String {
self._routeName = routeName
} else {
_routeName = ""
}
// MKAD Distance
if let location = data["location"] as? JSONDictionary,
mkadDistance = location["mkadDistance"] as? String {
self._mkadDistance = mkadDistance
} else {
_mkadDistance = ""
}
// Rent Offer
if let rentDict = data["rentOffer"] as? JSONDictionary,
rentOffer = rentDict["price"] as? String {
self._rentOffer = rentOffer
} else {
_rentOffer = ""
}
// Sale Offer
if let saleDict = data["saleOffer"] as? JSONDictionary,
saleOffer = saleDict["price"] as? String {
self._saleOffer = saleOffer
} else {
_saleOffer = ""
}
}
}
Just in case, JSONDictionary and JSONArray are just typealiases:
typealias JSONDictionary = [String : AnyObject]
typealias JSONArray = Array<AnyObject>
Thanks in advance!
images[0] will crash with "Fatal error: Index out of range" if the images array is empty.
Since you're using optional binding, use first instead of [0]:
if let images = data["images"] as? JSONArray,
pic0 = images.first as? JSONDictionary,
mainPic = pic0["url"] as? String {
self._mainPic = mainPic
} else {
_mainPic = ""
}

Swift parse json to struct

I have a struct like
struct Channel {
var id : Int = 0
var name = ""
}
and I am getting json from URL as
{"channel_list":[{"channel_id":0,"channel_name":"test1"},{"channel_id":0,"channel_name":"test2"}]}
However I am not able to get data as
func parseJson(anyObj:AnyObject) -> Array<Channel>{
var list:Array<Channel> = []
if anyObj is Array<AnyObject> {
var b:Channel = Channel()
for json in anyObj as! Array<AnyObject>{
b.id = (json["channel_id"] as AnyObject? as? Int) ?? 0
b.name = (json["channel_name"] as AnyObject? as? String) ?? ""
list.append(b)
}
}
return list
}
//read code
let anyObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0),error: nil) as AnyObject?
println(anyObj)
if let myobj=anyObj["channel_list"] as AnyObject {
self.Channellist=self.parseJson(anyObj!)
}
Whats wrong with this?
First, instead of using AnyObject, you should cast the JSON response as a Dictionary: [NSObject:AnyObject] then safe cast the result of anyObj["channel_list"] to an Array of Dictionaries [[NSObject:AnyObject]], because this is your JSON response format.
Then you need to use this type in your parseJSON function. We're also simplifying it while we're at it, because there's no need to do weird castings anymore.
Also, you were passing the wrong argument to your function (you used anyObj instead of myObj).
struct Channel {
var id : Int = 0
var name = ""
}
func parseJson(anyObj: [[NSObject:AnyObject]]) -> Array<Channel>{
var list: Array<Channel> = []
var b: Channel = Channel()
for json in anyObj {
b.id = (json["channel_id"] as? Int) ?? 0
b.name = (json["channel_name"] as? String) ?? ""
list.append(b)
}
return list
}
if let anyObj = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions(0),error: nil) as? [NSObject:AnyObject] {
if let myobj = anyObj["channel_list"] as? [[NSObject:AnyObject]] {
self.Channellist=self.parseJson(myobj)
}
}
There's still room for improvement: you could create an initializer for your Struct, for example, and also create a typealias for the response types, use map to create the list, etc.
Here's how I would do it with Swift 2:
struct Channel {
var id : Int
var name: String
init?(JSON: [NSObject: AnyObject]?) {
guard let channelID = json["channel_id"] as? Int, let channelName = json["channel_name"] as? String
else { name = ""; id = 0; return nil }
name = channelName
id = channelID
}
}
func parseJSON(array: [[NSObject:AnyObject]]) -> [Channel?] {
return array.map { Channel(JSON: $0) }
// If you don't want to return optionals to channel you can do this instead:
// return array.map { Channel(JSON: $0) }.filter { $0 != nil }.map { $0! }
}
// And in the caller
do {
guard let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [NSObject : AnyObject]
else { throw NSError(domain ... // Setup error saying JSON wasn't parsed. }
guard let arrayContents = dict["channel_list"] as? [[NSObject:AnyObject]]
else { throw NSError(domain ... // Setup error saying array wasn't found. }
let channels = parseJSON(arrayContents)
}
catch {
print(error)
}

Resources