Swift 3.0 localization Bundle for enum - swift3

Using swift 3.0, I am trying to create enum where different values and their description will get as per enum type, depends on localization language. But below code is not compile due to Bundle parameters are not valid as here self is not valid class in enum. Any suggestion about how can we resolve this issue for localization?
public enum TempEnumValues : Int
{
case first
case second
case third
var values: Int
{
switch self
{
case .first:
return 1
case .second:
return 2
case .third:
return 3
}
}
var description: String
{
let bundle = Bundle(for: CLASS_NAME.self))
switch self
{
case .first:
return NSLocalizedString("First Value", bundle: bundle, comment: "")
case .second:
return NSLocalizedString("Second Value", bundle: bundle, comment: "")
case .third:
return NSLocalizedString("Third Value", bundle: bundle, comment: "")
}
}
}
I found the answer & updated code

Related

SwiftUI custom error with additional detail for support/tracking

I want to track additional information about errors to help support issues and be proactive where errors/bugs are encountered. I am new to iOS/Swift, but I have followed a few YouTube videos and posts on error handling to alert users of an error (this was the most up to date/best one I found) but I also want to provide more detail as well to help support. I created a custom error:
enum CustomError: Error {
case apiDecodingError
...
}
and extended it:
extension CustomError: LocalizedError {
var errorDescription: String? {
switch self {
case .apiDecodingError:
return NSLocalizedString("Problem understanding service response.", comment: "")
...
}
}
}
but I cannot add additional properties to the extension because: Extensions must not contain stored properties.
When I catch and throw a custom error from, for example, decoding the response of an api call:
} catch let DecodingError.keyNotFound(key, context) {
print("Key '\(key)' not found:", context.debugDescription)
print("codingPath:", context.codingPath)
throw CustomError.DecodingError
This makes for a simplified user message:
but I also want to send the detail to the bug tracking system with the specific problem, such as the key which was having the problem.
} catch {
print("\(error)")
let properties = ["Error" : error.localizedDescription]
Analytics.trackEvent("UpdateNotificationSetting", withProperties: properties, flags: .critical)
isAlertErrorPresented = true
errorAlert = ErrorAlert(error: error)
}
I would like to send along the detail but that means the custom error needs additional information from the inner error. I'm a novice at SwiftUI, is there a standard way to address this?
You can do it by expanding CustomError
enum CustomError: LocalizedError{
///Takes in any error and makes it compatible with `LocalizedError`
case error(Error)
///Allows for throwing an error with a custom description
case custom(description: String, failureReason: String? = nil, helpAnchor: String? = nil, recoverySuggestion: String? = nil)
///Can be used as a custom key using localization features
case customKey
///Mimic your decoding error
case apiDecodingError(key: String, path: String)
var errorDescription: String?{
switch self{
case .error(let error):
return error.localizedDescription
case .custom(let description, _ , _, _):
return description
case .apiDecodingError(let key, path: let path):
return String(format: "Key %# not found: codingPath: %#", key, path)
default:
///Add `customKeyErrorDescription` to Localizable.strings
return NSLocalizedString("\(self)" + "ErrorDescription", comment: "AppError")
}
}
var failureReason: String?{
switch self{
case .error(let error):
let nsError = error as NSError
return nsError.localizedFailureReason
case .custom(_ , let failureReason , _, _):
return failureReason
case .apiDecodingError(_, _):
return nil
default:
///Add `customKeyFailureReason` to Localizable.strings
return NSLocalizedString("\(self)" + "FailureReason", comment: "AppError")
}
}
var helpAnchor: String?{
switch self{
case .error(let error):
let nsError = error as NSError
return nsError.helpAnchor
case .custom(_ , _ , let helpAnchor, _):
return helpAnchor
case .apiDecodingError(_, _):
return nil
default:
///Add `customKeyHelpAnchor` to Localizable.strings
return NSLocalizedString("\(self)" + "HelpAnchor", comment: "AppError")
}
}
var recoverySuggestion: String?{
switch self{
case .error(let error):
let nsError = error as NSError
return nsError.localizedRecoverySuggestion
case .custom(_, _ , _, let recoverySuggestion):
return recoverySuggestion
case .apiDecodingError(_, _):
return nil
default:
///Add `customKeyRecoverySuggestion` to Localizable.strings
return NSLocalizedString("\(self)" + "RecoverySuggestion", comment: "AppError")
}
}
}
Then you can pass along any standard error
do{
}catch{
CustomError.error(error)
}

raw Value of optional type not unwrapped

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
}
}

Can't create object with existing primary key value

I'm in my first steps of using Realm Mobile Database, and I would like to know if there is a way of handling the error caused by trying to add an object with the same primaryKey as one previously inserted, causing the following Can't create object with existing primary key value.
Here are my snippets:
class CategoryRLM: Object {
dynamic var name: String = ""
dynamic var desc: String = ""
override static func primaryKey() -> String? {
return "name"
}
}
static func addCategory(category: CategoryRLM) -> Bool {
let realm = try! Realm()
do {
try realm.write {
realm.add(category)
}
return true
}
catch let error {
print(error)
return false
}
}
Using the previous function:
if !CategoryRLM.addCategory(category: newCategory) {
// There was an error while adding the object
}
The thing is that the error doesn't get handled by the do-catch.
Attempting to add an object with a primary key that already exists is classed as programmer error (that is, misuse of the API), and as such it is not possible to handle this error at runtime.
In your case you should instead check if an object exists with that primary key prior to attempting to add the category to the Realm:
static func addCategory(category: CategoryRLM) -> Bool {
let realm = try! Realm()
if let existingCategory = realm.object(ofType: CategoryRLM.self, forPrimaryKey: category.name) {
return false
}
try realm.write! {
realm.add(category)
}
return true
}

Swift 3, Sort an Array of Dictionaries

I'm having a heck of a time in Swift 3 sorting an array of dictionaries.
In Swift 2, I would do it this way, which worked fine:
var dicArray = [Dictionary<String, String>()]
let dic1 = ["last": "Smith", "first": "Robert"]
dicArray.append(dic1)
let dic2 = ["last": "Adams", "first": "Bill"]
dicArray.append(dic2)
let sortedArray = dicArray.sort { ($0["last"] as? String) < ($1["last"] as? String) }
Converting the same code to Swift 3 has not gone well. The system guided me to this (through a circuitous route):
let sortedArray = dicArray.sorted { ($0["last"]! as String) < ($1["last"]! as String) }
But the app always crashes, with the error that it found nil while unwrapping an Optional value.
After banging my head against the table for too long, putting ?s and !s in every imaginable combination, I resorted to an old approach to get the job done:
let sortedArray = (dicArray as NSArray).sortedArray(using: [NSSortDescriptor(key: "last", ascending: true)]) as! [[String:AnyObject]]
That works, and I'm moving along, but it's not very Swifty, is it?
Where did it all go wrong? How can I make the pure Swift sort function work in a case like this?
Where did it all go wrong?
It went wrong on your first line:
var dicArray = [Dictionary<String, String>()]
That never did what you want, even in Swift 2, because you are actually inserting an extra, empty dictionary into the array. That's where the crash comes from; the empty dictionary has no "last" key, because it is, uh, empty.
You want this:
var dicArray = [Dictionary<String, String>]()
See the difference? After that change, everything falls into place:
var dicArray = [Dictionary<String, String>]()
let dic1 = ["last": "Smith", "first": "Robert"]
dicArray.append(dic1)
let dic2 = ["last": "Adams", "first": "Bill"]
dicArray.append(dic2)
let sortedArray = dicArray.sorted {$0["last"]! < $1["last"]!}
// [["first": "Bill", "last": "Adams"], ["first": "Robert", "last": "Smith"]]
Rather than using dictionaries with a fixed set of keys, it's generally advisable to create your own custom types:
struct Person {
let lastName: String
let firstName: String
}
This way, you never have to worry about whether you got the key for a particular value in a dictionary right, because the compiler will enforce checks for the names of the property. It makes it easier to write robust, error-free code.
And, coincidentally, it makes sorting cleaner, too. To make this custom type sortable, you make it conform to the Comparable protocol:
extension Person: Comparable {
public static func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.lastName == rhs.lastName && lhs.firstName == rhs.firstName
}
public static func < (lhs: Person, rhs: Person) -> Bool {
// if lastnames are the same, compare first names,
// otherwise we're comparing last names
if lhs.lastName == rhs.lastName {
return lhs.firstName < rhs.firstName
} else {
return lhs.lastName < rhs.lastName
}
}
}
Now, you can just sort them, keeping the comparison logic nicely encapsulated within the Person type:
let people = [Person(lastName: "Smith", firstName: "Robert"), Person(lastName: "Adams", firstName: "Bill")]
let sortedPeople = people.sorted()
Now, admittedly, the above dodges your implicit question of how to compare optionals. So, below is an example where firstName and lastName are optionals. But, rather than worrying about where to put the ? or !, I'd use nil-coalescing operator, ??, or a switch statement, e.g.:
struct Person {
let lastName: String?
let firstName: String?
}
extension Person: Comparable {
public static func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.lastName == rhs.lastName && lhs.firstName == rhs.firstName
}
public static func < (lhs: Person, rhs: Person) -> Bool {
// if lastnames are the same, compare first names,
// otherwise we're comparing last names
var lhsString: String?
var rhsString: String?
if lhs.lastName == rhs.lastName {
lhsString = lhs.firstName
rhsString = rhs.firstName
} else {
lhsString = lhs.lastName
rhsString = rhs.lastName
}
// now compare two optional strings
return (lhsString ?? "") < (rhsString ?? "")
// or you could do
//
// switch (lhsString, rhsString) {
// case (nil, nil): return false
// case (nil, _): return true
// case (_, nil): return false
// default: return lhsString! < rhsString!
// }
}
}
The switch statement is more explicit regarding the handling of nil values (e.g. nil sorted before or after non-optional values) and will distinguish between a nil value and an empty string, should you need that. The nil coalescing operator is simpler (and IMHO, more intuitive for the end-user), but you can use switch approach if you want.
let descriptor: NSSortDescriptor = NSSortDescriptor.init(key: "YOUR KEY", ascending: true)
let sortedResults: NSArray = tempArray.sortedArray(using: [descriptor]) as NSArray

Extra argument 'method' in call in Swift 3.0

I'm getting this error : Extra arguement in 'method' in call. I am using XCode 8, Swift 3.0 and iOS 10.0.
I'm attaching the screenshots for the code.
try once below code
Alamofire.request("Your URL", method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(_):
break
case .failure(_):
break
}
}
Need to change in your code
encoding:URLEncoding.httpBody
to
encoding:URLEncoding(destination: .httpBody)
Try this in your code for New Alamofire 4.0
Swift 3
Alamofire.request(requestURL, method: .post, parameters: parameters, encoding:URLEncoding(destination: .httpBody), headers: headers)
.responseJSON { response in
switch response.result {
case .success:
self.successGetData(response.result.value! as AnyObject)
case .failure(let error):
print(error)
}
}
Source : - Alamofire 4.0