raw Value of optional type not unwrapped - swift3

I am trying to unwrap the raw value of an enum type in func toDictionary() , but I get an error.
How can I fix this?
enum ChatFeatureType: String {
case tenants
case leaseholders
case residents
}
class Chat {
var featureType: ChatFeatureType?
init(featureType: ChatFeatureType? = nil
self.featureType = featureType
}
//download data from firebase
init(dictionary : [String : Any]) {
featureType = ChatFeatureType(rawValue: dictionary["featureType"] as! String)!
}
func toDictionary() -> [String : Any] {
var someDict = [String : Any]()
// I get error on the line below: Value of optional type 'ChatFeatureType?' not unwrapped; did you mean to use '!' or '?'?
someDict["featureType"] = featureType.rawValue ?? ""
}
}

As featureType is an optional you have to add ? or ! as the error says
someDict["featureType"] = featureType?.rawValue ?? ""
But be aware that your code reliably crashes when you create an instance of Chat from a dictionary and the key does not exist because there is no case "".
Actually the purpose of an enum is that the value is always one of the cases. If you need an unspecified case add none or unknown or similar.
This is a safe version
enum ChatFeatureType: String {
case none, tenants, leaseholders, residents
}
class Chat {
var featureType: ChatFeatureType
init(featureType: ChatFeatureType = .none)
self.featureType = featureType
}
//download data from firebase
init(dictionary : [String : Any]) {
featureType = ChatFeatureType(rawValue: dictionary["featureType"] as? String) ?? .none
}
func toDictionary() -> [String : Any] {
var someDict = [String : Any]()
someDict["featureType"] = featureType.rawValue
return someDict
}
}

Related

How we can pass default null value in parameter list of dictionary [String:String] while posting a request to server ? Swift3

In some case i have to post null values to server either as a default value or as a parameter's value.I tried many ways but it didnt work.I'm using Alamofire for posting a request.Please help.
Method-1
var empty : String = ""
let parameters = ["User-Id":userId,"search_cat": formattedArray ?? empty,"date1":ab ?? empty,"date2" : bc ?? empty,"docname" : empty
] as! [String : String]
Method-2
let parameters = ["User-Id":userId,"search_cat": formattedArray ?? "","date1":ab ?? "","date2" : bc ?? "","docname" : ""
] as! [String : String]
Method-3
var nullvalue : String = ""
if formattedArray == nil
{
formattedArray = ""
print(formattedArray)
}
if ab == nil
{
ab = ""
print(ab)
}
if bc == nil
{
bc = ""
print(bc)
}
if nullvalue == nil
{
nullvalue = ""
}
parameters = ["User-Id":userId,"search_cat": formattedArray,"date1":ab ,"date2" : bc ,"docname" : nullvalue ]
as! [String : String]
Method - 4 According to answer i changed parameterlist dictionary to [String:AnyObject] but still it's not working.And giving me the error.
var ab:String?
var bc : String?
var formattedArray: String?
parameters = ["User-Id":userId ,"search_cat": formattedArray ?? NSNull() ,
"date1":ab ?? NSNull() ,"date2" : bc ?? NSNull(),
"docname" : NSNull()] as! [String : AnyObject]
Method-4 giving me Error while trying to set formattedArray ?? NSNull() :
Cannot convert value of type 'NSNull' to expected argument type 'String'
with rest of the parameters its working fine.Please help.
UPDATED : is this that you want?
class RequestParameter : NSObject{
var formattedArray : String!
var date1 : String!
var date2 : String!
var docName : String!
var userId : String! // or whatever if hardCoded
func param() -> NSDictionary{
var param : [String : String] = [:]
param["User-Id"] = userId
param["search_cat"] = formattedArray
param["date1"] = date1
param["date2"] = date2
param["docname"] = docName
return param as! NSDictionary
}
}
You can call these parameters where ever required. like
var reqParam = RequestParameter()
reqParam.param()
I am really unaware of your Request parameter type. For eg. should it look like date1 = nil or something else.
This is all I could answer you. If wrong maybe someone might ans better.
Good Luck!!
I actually had the same issue a couple of days ago and solved it using NSNull().
When converting my class I want to send the videoUrl property regardless of it containing a value, so that the backend can update it in the DB.
dict["videoUrl"] = videoUrl ?? NSNull()
This will set the key "videoUrl" with the value of the property videoUrl, or if nil, an instance of NSNull().
Express.js then reads this as undefined and inserts a null value to the database.
EDIT: I saw one of your comments below. When using a Dictionary of type [String:String], you will never be able to pass a nil value.

result of closure in function to variable in swift 3

I've got a function which does an asynchrone call
func getToken(completionHandler:#escaping (_ stringResponse: String) -> Void) {
var testResult: String! = "20,5º"
let url: String! = "https://the.base.url/token"
let parameters: Parameters = [
"key":"value"
]
Alamofire.request(url, method: .post, parameters: parameters, encoding: URLEncoding(destination: .methodDependent)).validate().responseJSON { response in
switch response.result {
case .success:
testResult = "21º"
case .failure(let error):
testResult = error as! String
}
completionHandler(testResult)
}
}
And I call this function
getToken(completionHandler: {
(stringResponse: String) in
print(stringResponse)
})
And it nicely prints 21º as I can see in the debugger. However, the final value of stringResponse should end up in
lblTemp.setText(String(//here the results of stringResponse))
How do I do this? I'm guessing it must be incredible simple.
You need to do it like that
getToken(completionHandler: { [weak self] (stringResponse: String) in
DispatchQueue.main.async {
self?.lblTemp.text = stringResponse
}
})

How to extend a function of a protocol in swift 3?

I have following code:
protocol Protocol_1 {
var prop_1: String { get set }
var prop_2: Int { get set }
}
protocol Protocol_2 {
var prop_3: Double { get set }
var prop_4: Bool { get set }
}
extension Protocol_1 {
mutating func set<T>(value: T, forKey key: String)
{
switch key {
case "prop_1":
if value is String {
prop_1 = value as! String
}
case "prop_2":
if value is Int {
prop_2 = value as! Int
}
default:
break
}
}
}
extension Protocol_2 {
mutating func set<T>(value: T, forKey key: String)
{
switch key {
case "prop_3":
if value is Double {
prop_3 = value as! Double
}
case "prop_4":
if value is Bool {
prop_4 = value as! Bool
}
default:
break
}
}
}
struct MyStruct : Protocol_1, Protocol_2 {
var prop_1: String
var prop_2: Int
var prop_3: Double
var prop_4: Bool
}
var myStruct = MyStruct(prop_1: "hello", prop_2: 0, prop_3: 3.5, prop_4: true)
myStruct.set(value: "bye", forKey: "prop_1")
Now the playground gives me an error because it is not clear what set function should be called. Playground execution failed: error: ProtocolsPlayground.playground:59:1: error: ambiguous use of 'set(value:forKey:)'
myStruct.set(value: "bye", forKey: "prop_1")
This is clear but how can I achieve such function extension or is there a work around? Especially if the Protocol_1 is not editable.
I don't know of any way to extend an existing function or another protocol's function using a protocol.
What I would suggest is that you separate the mechanism that assigns properties by name from the two protocols using a third protocol that does that specifically and separately.
Here's one way to approach this:
Define a class that will handle getting and setting properties based on a mapping between names (keys) and variable references:
class PropertyMapper
{
static var sharedGetter = PropertyMapperGet()
static var sharedSetter = PropertyMapperSet()
var value : Any = 0
var success = false
var key = ""
func map<T>(_ key:String, _ variable:inout T) {}
func clear()
{
value = 0
success = false
key = ""
}
}
class PropertyMapperGet:PropertyMapper
{
func get(forKey:String)
{
key = forKey
success = false
}
override func map<T>(_ key:String, _ variable:inout T)
{
if !success && self.key == key
{
value = variable
success = true
}
}
}
class PropertyMapperSet:PropertyMapper
{
override func map<T>(_ key:String, _ variable:inout T)
{
if !success && self.key == key,
let newValue = value as? T
{
variable = newValue
success = true
}
}
func set(value newValue:Any, forKey:String)
{
key = forKey
value = newValue
success = false
}
}
Then, you can define a protocol for all struct and classes that will have the ability to assign their properties by name (key):
protocol MappedProperties
{
mutating func mapProperties(_ :PropertyMapper)
}
extension MappedProperties
{
mutating func get(_ key:String) -> Any?
{
let mapper = PropertyMapper.sharedGetter
defer { mapper.clear() }
mapper.get(forKey:key)
mapProperties(mapper)
return mapper.success ? mapper.value : nil
}
#discardableResult
mutating func set(value:Any, forKey key:String) -> Bool
{
let mapper = PropertyMapper.sharedSetter
defer { mapper.clear() }
mapper.set(value:value, forKey:key)
mapProperties(mapper)
return mapper.success
}
}
Your protocols can require that the struct that adopt them offer the named assignments. (see farther down for making the property mapping part of your protocol)
protocol Protocol_1:MappedProperties
{
var prop_1: String { get set }
var prop_2: Int { get set }
}
protocol Protocol_2:MappedProperties
{
var prop_3: Double { get set }
var prop_4: Bool { get set }
}
Your struct will need to implement the property mapping in order to adopt your protocols. The key/variable mapping is performed in a centralized function for the whole struct and needs to provide keys for all the variables in both protocols.
struct MyStruct : Protocol_1, Protocol_2
{
var prop_1: String
var prop_2: Int
var prop_3: Double
var prop_4: Bool
mutating func mapProperties(_ mapper:PropertyMapper)
{
mapper.map("prop_1", &prop_1)
mapper.map("prop_2", &prop_2)
mapper.map("prop_3", &prop_3)
mapper.map("prop_4", &prop_4)
}
}
This allows the struct to assign properties by name (key):
var myStruct = MyStruct(prop_1: "hello", prop_2: 0, prop_3: 3.5, prop_4: true)
myStruct.set(value: "bye", forKey: "prop_1")
To refine this further and make the property mapping part of your protocols, you can add a mapping function to the protocol so that the structs that adopt it don't have to know the details of this mapping.
They will still need to implement the mapping protocol's function but they can simply use functions provided by your protocols to do the job.
For example( I only showed Protocol_1 but you can do it with both of them):
extension Protocol_1
{
mutating func mapProtocol_1(_ mapper:PropertyMapper)
{
mapper.map("prop_1", &prop_1)
mapper.map("prop_2", &prop_2)
}
}
With the function provided by the protocol, the struct doesn't need to know which properties are mapped. This should make maintenance of the structs and protocols less error prone and avoid duplications.
struct MyStruct : Protocol_1, Protocol_2
{
var prop_1: String
var prop_2: Int
var prop_3: Double
var prop_4: Bool
mutating func mapProperties(_ mapper:PropertyMapper)
{
mapProtocol_1(mapper)
mapper.map("prop_3", &prop_3)
mapper.map("prop_4", &prop_4)
}
}

Parse JSON string to Model Object type Array

I got Encrypted data from API hit by below method
URLSession.shared.dataTask(with: url!)
converted data into JSON but still it is encrypted
var json = try(JSONSerialization.jsonObject(with: data!, options: .allowFragments))
converted it into string
let arr:String = json as! String
decrypted it
let jsonText = arr.fromBase64()//extension method, given end of question
now it is in Json Formate as below (this is only 1 record, there are more than 1 records in Json string)
{
"CompanyAlt_Key": 1,
"Company_Name": "XYZ LTD",
"TableName": "CompanyList"
},
I have a model of same type
public class CompanyList {
public var companyAlt_Key : Int?
public var company_Name : String?
public var tableName : String?
}
here is fromBase64 method
func fromBase64() -> String {
let data = NSData.init(base64Encoded: self, options: []) ?? NSData()
return String(data: data as Data, encoding: String.Encoding.utf8) ?? ""
}
I am facing problem to get the Json String into an array of type CompanyList class
Help would be appreciate
You'll need to convert your jsonString to data first:
let jsonData = jsonString.data(using: .utf8)!
The convert the data to an array
let array = JSONSerialization.jsonObject(with: jsonData, options: nil) as? [[String: Any]]
Then iterate through the array…
let companies = array?.map {
return CompanyList(dictionary: $0)
}
Implement an init method for your CompanyList, passing in a dictionary for each record in your response…
public class CompanyList {
public var companyAlt_Key : Int?
public var company_Name : String?
public var tableName : String?
init(dictionary: [String: Any]) {
companyAlt_Key = dictionary["companyAlt_Key"] as? Int
company_Name = dictionary["company_Name"] as? String
tableName = dictionary["tableName"] as? String
}
}
You can also use this to validate the data. If the fields in your class are non-optional, you can use an optional init as follows…
public class CompanyList {
public var companyAlt_Key : Int
public var company_Name : String
public var tableName : String
init?(dictionary: [String: Any]) {
guard let companyAlt_Key = dictionary["companyAlt_Key"] as? Int,
let company_Name = dictionary["company_Name"] as? String,
let tableName = dictionary["tableName"] as? String else {
return nil
}
self.companyAlt_Key = companyAlt_Key
self.company_Name = company_Name
self.tableName = tableName
}
}
If you're using an optional init, use flatMap to ensure you don't have any optional elements in your array…
let companies = array?.flatMap {
return CompanyList(dictionary: $0)
}

Getting ambiguous reference to member subscript

I am updating my code to swift3.0 but getting ambiguous refrence to member? What wrong i might be doing. Here is the method I am getting error in.
open class func parseJsonTenantList(_ list: [NSDictionary]?, strElementName: String, attrName1: String, attrNameValue2: String) -> [TenantRegister]
{
var renantList: [TenantRegister] = []
var key: String?
if let dict : [NSDictionary] = list {
var value: String?
for i in 0..<dict.count {
/// if attribute name doesn't match then it returns nil
if let s1: AnyObject = dict[i].value(forKey: attrName1)
{
key = s1 as? String
}
if let s2: AnyObject = dict[i].value(forKey: attrNameValue2)
{
value = s2 as? String
}
if (!(String.stringIsNilOrEmpty(value) && String.stringIsNilOrEmpty(key)))
{
let t: TenantRegister = TenantRegister()
t.name = key
t.tenantId = Guid(value!)
renantList.append(t)
}
}
}
return renantList
}
The issue is you are using NSDictionary, to solved your problem simply cast the list to Swift's native type [[String:Any]] and then use subscript with it instead of value(forKey:)
if let dict = list as? [[String:Any]] {
var value: String?
for i in 0..<dict.count {
/// if attribute name doesn't match then it returns nil
if let s1 = dict[i][attrName1] as? String
{
key = s1
}
if let s2 = dict[i][attrNameValue2] as? String
{
value = s2
}
if (!(String.stringIsNilOrEmpty(value) && String.stringIsNilOrEmpty(key)))
{
let t: TenantRegister = TenantRegister()
t.name = key
t.tenantId = Guid(value!)
renantList.append(t)
}
}
}
In Swift use native type Dictionary [:] and Array [] instead of NSDictionary and NSArray to overcome this type of issues.