Is there a better way to do this switch? - swift

What I'm trying to do is: that based on the notification type, I'll change the image of the tableViewCell, and I know that maybe there is a better way of achieving this. I thought that maybe using enums would be a good way, but there is a lot of code in here, and it will do nothing but grow with time.
Is there a better way of achieving this?
enum NotificationIcons {
case newPayment
case newPaymentMethod
case newOffers
case userInfoUpdate
case supportChat
case newAnnouncement
case cardVerification
var strings: String {
switch self {
case .newPayment:
return "new_payment"
case .newPaymentMethod:
return "new_payment_method"
case .newOffers:
return "new_offers"
case .userInfoUpdate:
return "user_info_update"
case .supportChat:
return "support_chat"
case .newAnnouncement:
return "new_announcement"
case .cardVerification:
return "card_verification"
}
}
var image: UIImage {
switch self {
case .newPayment: return UIImage(named: "Icon-notification-service-pay")!
case .newPaymentMethod: return UIImage(named: "Icon-notification-add-paycard")!
case .newOffers: return UIImage(named: "Icon-notification-promos")!
case .userInfoUpdate: return UIImage(named: "Icon-notification-update-data")!
case .supportChat: return UIImage(named: "Icon-notification-support")!
case .newAnnouncement: return UIImage(named: "Icon-notification-advice")!
case .cardVerification: return UIImage(named: "Icon-notification-add-paycard")!
}
}
var detailImage: UIImage {
switch self {
case .newPayment: return UIImage(named: "Icon-notification-detail-service-pay")!
case .newPaymentMethod: return UIImage(named: "Icon-notification-detail-add-paycard")!
case .newOffers: return UIImage(named: "Icon-notification-detail-promos")!
case .userInfoUpdate: return UIImage(named: "Icon-notification-detail-update-data")!
case .supportChat: return UIImage(named: "Icon-notification-detail-support")!
case .newAnnouncement: return UIImage(named: "Icon-notification-detail-advice")!
case .cardVerification: return UIImage(named: "Icon-notification-detail-add-paycard")!
}
}
}
Inside my tableViewCell I have this variable notification which starts to set all values on didSet
switch notification.type {
case NotificationIcons.newPayment.strings:
notificationImageView.image = NotificationIcons.newPayment.image
break
case NotificationIcons.newPaymentMethod.strings:
notificationImageView.image = NotificationIcons.newPaymentMethod.image
break
case NotificationIcons.newOffers.strings:
notificationImageView.image = NotificationIcons.newPaymentMethod.image
break
case NotificationIcons.userInfoUpdate.strings:
notificationImageView.image = NotificationIcons.newPaymentMethod.image
break
case NotificationIcons.supportChat.strings:
notificationImageView.image = NotificationIcons.newPaymentMethod.image
break
case NotificationIcons.newAnnouncement.strings:
notificationImageView.image = NotificationIcons.newPaymentMethod.image
break
case NotificationIcons.cardVerification.strings:
notificationImageView.image = NotificationIcons.newPaymentMethod.image
break
default:
break
}

Firstly, assign rawValue of type String to the NotificationIcons enum, like this:
enum NotificationIcons: String {
case newPayment = "new_payment"
//...
}
Then, modify the switch statement with an initializer:
guard let type = NotificationIcons(rawValue: notification.type) else { return }
notinotificationImageView.image = type.image

Related

Is there a clean way of making an enum with associated value conform to rawRepresentable?

I have this in my code and it works, however if I have other enums (not necessarily color) with a long list it gets tiresome. Is there a better way of having an enum with an associated value that also conforms to RawRepresentable?
public enum IconColor {
case regular
case error
case warning
case success
case custom(String)
public var value: Color {
return loadColor(self.rawValue)
}
}
extension IconColor: RawRepresentable {
public var rawValue: String {
switch self {
case .regular: return "icon_regular"
case .error: return "icon_error"
case .warning: return "icon_warning"
case .success: return "icon_success"
case .custom(let value): return value
}
}
public init(rawValue: String) {
switch rawValue {
case "icon_regular": self = .regular
case "icon_error": self = .error
case "icon_warning": self = .warning
case "icon_success": self = .success
default: self = .custom(rawValue)
}
}
}
There's not a general solution.
public var rawValue: String {
switch self {
case .custom(let value): return value
default: return "icon_\(self)"
}
}
public init(rawValue: String) {
self =
[.regular, .error, .warning, .success]
.first { rawValue == $0.rawValue }
?? .custom(rawValue)
}

Value of type UIImage has no member cgImageOrientation

I am new coreml and I was reading a book I came across a code snippet and when I tried to run it I got the above error , I Googled but got no luck ,
here is my code
import UIKit
import Vision
extension UIImage {
func detectFaces(completion:#escaping ([VNFaceObservation]?)->()){
guard let image = self.cgImage else {
completion(nil)
return
}
let request = VNDetectFaceRectanglesRequest()
DispatchQueue.global().async {
let handler = VNImageRequestHandler(cgImage: image, orientation: self.cgImageOrientation)
//above error here I think cgImageOrientation is no longer available but what's the solution anyways
}
}
}
here I am using swiftUI and lifecycle methods are also selected as swiftui
,
The book name is "Practical Artificial intelligence with swift" by orielly
You need to continue working through the tutorial before you'll be able to compile it. The next section of the book talks about creating Views.swift, and step 8 of that section includes this extension:
extension UIImage {
func fixOrientation() -> UIImage? {
UIGraphicsBeginImageContext(self.size)
self.draw(at: .zero)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
var cgImageOrientation: CGImagePropertyOrientation {
switch self.imageOrientation {
case .up: return .up
case .down: return .down
case .left: return .left
case .right: return .right
case .upMirrored: return .upMirrored
case .downMirrored: return .downMirrored
case .leftMirrored: return .leftMirrored
case .rightMirrored: return .rightMirrored
}
}
}

Updating value of Enum

I have an enum that I use to populate a collection View and data changes based on API call and when this call is made, I want to change the subTitle value here is what I have currently
enum BalanceType: String, CaseIterable {
case ledger
case available
var backgroundImage: UIImage {
switch self {
case .ledger:
return R.image.walletBgNonWithdrawable()!
case .available:
return R.image.walletBgWithdrawable()!
}
}
var title: String {
switch self {
case .ledger:
return "Contribution Ledger Balance"
case .available:
return "My Available Balance"
}
}
var subTitle: String {
switch self {
case .ledger:
return Util.df2so(LedgerBalanceModel.currentWallet()?.totalLedgerBalance ?? 0)
case .available:
return Util.df2so((LedgerBalanceModel.currentWallet()?.totalLedgerBalance ?? 0) - (LedgerBalanceModel.currentWallet()?.totalMinBalance ?? 0))
}
}
}
my call that should update the value is
func showLedgerBal(_ data: LedgerBalanceModel) {
collectionView.reloadData()
}

How to archive enum with an associated value?

I'm trying to encode an object and i have some troubles. It work's fine with strings, booleans and else, but i don't know how to use it for enum.
I need to encode this:
enum Creature: Equatable {
enum UnicornColor {
case yellow, pink, white
}
case unicorn(UnicornColor)
case crusty
case shark
case dragon
I'm using this code for encode:
func saveFavCreature(creature: Dream.Creature) {
let filename = NSHomeDirectory().appending("/Documents/favCreature.bin")
NSKeyedArchiver.archiveRootObject(creature, toFile: filename)
}
func loadFavCreature() -> Dream.Creature {
let filename = NSHomeDirectory().appending("/Documents/favCreature.bin")
let unarchived = NSKeyedUnarchiver.unarchiveObject(withFile: filename)
return unarchived! as! Dream.Creature
}
Here is required functions (model.favoriteCreature == Dream.Creature)
override func encode(with aCoder: NSCoder) {
aCoder.encode(model.favoriteCreature, forKey: "FavoriteCreature")
aCoder.encode(String(), forKey: "CreatureName")
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let favoriteCreature = aDecoder.decodeObject(forKey: "FavoriteCreature")
let name = aDecoder.decodeObject(forKey: "CreatureName")
}
It works fine with "name", i think the problem is in aCoder.encode() coz i don't know what type to write there. I get next error when run:
-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance
-[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
I read some advices in comments and can assume that i have no rawValues in enum Creature, i made raw type "String" in that enum:
enum Creature: String, Equatable {
enum UnicornColor {
case yellow, pink, white
}
case unicorn(UnicornColor)
case crusty
case shark
case dragon
Now i have this error:
Enum with raw type cannot have cases with arguments.
Also i read that associated values and raw values can't coexist. Maybe there is some other way to archive enum without raw values?
Hope someone can help me, thank's
You are dealing with a problem that arises because Swift native features don't always play well with Objective-C. NSCoding has its roots in the Objective-C world, and Objective-C doesn't know anything about Swift Enums, so you can't simply archive an Enum.
Normally, you could just encode/decode the enumeration using raw values, but as you found, you can't combine associated types and raw values in a Swift enumeration.
Unfortunately this means that you will need to build your own 'raw' value methods and handle the cases explicitly in the Creature enumeration:
enum Creature {
enum UnicornColor: Int {
case yellow = 0, pink, white
}
case unicorn(UnicornColor)
case crusty
case shark
case dragon
init?(_ creatureType: Int, color: Int? = nil) {
switch creatureType {
case 0:
guard let rawColor = color,
let unicornColor = Creature.UnicornColor(rawValue:rawColor) else {
return nil
}
self = .unicorn(unicornColor)
case 1:
self = .crusty
case 2:
self = .shark
case 3:
self = .dragon
default:
return nil
}
}
func toRawValues() -> (creatureType:Int, unicornColor:Int?) {
switch self {
case .unicorn(let color):
let rawColor = color.rawValue
return(0,rawColor)
case .crusty:
return (1,nil)
case .shark:
return (2,nil)
case .dragon:
return (3,nil)
}
}
}
You can then encode/decode like this:
class SomeClass: NSObject, NSCoding {
var creature: Creature
init(_ creature: Creature) {
self.creature = creature
}
required init?(coder aDecoder: NSCoder) {
let creatureType = aDecoder.decodeInteger(forKey: "creatureType")
let unicornColor = aDecoder.decodeInteger(forKey: "unicornColor")
guard let creature = Creature(creatureType, color: unicornColor) else {
return nil
}
self.creature = creature
super.init()
}
func encode(with aCoder: NSCoder) {
let creatureValues = self.creature.toRawValues()
aCoder.encode(creatureValues.creatureType, forKey: "creatureType")
if let unicornColor = creatureValues.unicornColor {
aCoder.encode(unicornColor, forKey: "unicornColor")
}
}
}
Testing gives:
let a = SomeClass(.unicorn(.pink))
var data = NSMutableData()
let coder = NSKeyedArchiver(forWritingWith: data)
a.encode(with: coder)
coder.finishEncoding()
let decoder = NSKeyedUnarchiver(forReadingWith: data as Data)
if let b = SomeClass(coder: decoder) {
print(b.creature)
}
unicorn(Creature.UnicornColor.pink)
Personally, I would make Creature a class and use inheritance to deal with the variation between unicorns and other types
The main problem for your issue is that you cannot pass Swift enums to encode(_:forKey:).
This article shown by Paulw11 will help you solve this part. If the enum can easily have rawValue, it's not too difficult.
But, as you see, Enum with raw type cannot have cases with arguments.
Simple enums can easily have rawValue like this:
enum UnicornColor: Int {
case yellow, pink, white
}
But enums with associate values, cannot have rawValue in this way. You may need to manage by yourself.
For example, with having inner enum's rawValue as Int :
enum Creature: Equatable {
enum UnicornColor: Int {
case yellow, pink, white
}
case unicorn(UnicornColor)
case crusty
case shark
case dragon
static func == (lhs: Creature, rhs: Creature) -> Bool {
//...
}
}
You can write an extension for Dream.Creature as:
extension Dream.Creature: RawRepresentable {
var rawValue: Int {
switch self {
case .unicorn(let color):
return 0x0001_0000 + color.rawValue
case .crusty:
return 0x0002_0000
case .shark:
return 0x0003_0000
case .dragon:
return 0x0004_0000
}
}
init?(rawValue: Int) {
switch rawValue {
case 0x0001_0000...0x0001_FFFF:
if let color = UnicornColor(rawValue: rawValue & 0xFFFF) {
self = .unicorn(color)
} else {
return nil
}
case 0x0002_0000:
self = .crusty
case 0x0003_0000:
self = .shark
case 0x0004_0000:
self = .dragon
default:
return nil
}
}
}
(In fact, it is not an actual rawValue and you'd better rename it for a more appropriate name.)
With a definition like shown above, you can utilize the code shown in the link above.
To simplify the coding/decoding you could provide an initializer for Creature requiring a Data and a computed property named data. As Creature changes or as new associated values are added, the interface to NSCoding does not change.
class Foo: NSObject, NSCoding {
let creature: Creature
init(with creature: Creature = Creature.crusty) {
self.creature = creature
super.init()
}
required init?(coder aDecoder: NSCoder) {
guard let data = aDecoder.decodeObject(forKey: "creature") as? Data else { return nil }
guard let _creature = Creature(with: data) else { return nil }
self.creature = _creature
super.init()
}
func encode(with aCoder: NSCoder) {
aCoder.encode(creature.data, forKey: "creature")
}
}
A serialization of Creature into and out of Data could be accomplished like this.
enum Creature {
enum UnicornColor {
case yellow, pink, white
}
case unicorn(UnicornColor)
case crusty
case shark
case dragon
enum Index {
static fileprivate let ofEnum = 0 // data[0] holds enum value
static fileprivate let ofUnicornColor = 1 // data[1] holds unicorn color
}
init?(with data: Data) {
switch data[Index.ofEnum] {
case 1:
switch data[Index.ofUnicornColor] {
case 1: self = .unicorn(.yellow)
case 2: self = .unicorn(.pink)
case 3: self = .unicorn(.white)
default:
return nil
}
case 2: self = .crusty
case 3: self = .shark
case 4: self = .dragon
default:
return nil
}
}
var data: Data {
var data = Data(count: 2)
// the initializer above zero fills data, therefore serialize values starting at 1
switch self {
case .unicorn(let color):
data[Index.ofEnum] = 1
switch color {
case .yellow: data[Index.ofUnicornColor] = 1
case .pink: data[Index.ofUnicornColor] = 2
case .white: data[Index.ofUnicornColor] = 3
}
case .crusty: data[Index.ofEnum] = 2
case .shark: data[Index.ofEnum] = 3
case .dragon: data[Index.ofEnum] = 4
}
return data
}
}

Swift - Associated value or extension for an Enum

General question regarding swift enum.
I want to create an enum of "icon" and "associate" a value to the enum case
enum Icon {
case plane
case arrow
case logo
case flag
}
I want to create an associated image to the enum's value.
And also an associated color to the enum value
So for instance if it was possible to do something like:
extension Icon.plane {
var image = {
get {
return UIImage("plane.png")
}
}
var color = {
get {
return UIColor.greenColor()
}
}
}
var image = Icon.arrow.image // the image associated to the enum
var color = Icon.arrow.color // the color associated to the enum
Is this type of thing possible?
Unfortunately you cannot define static properties based on enum cases, but you can use computed properties and switch to return values for each case:
enum Icon {
case plane
case arrow
case logo
case flag
var image: UIImage {
switch self {
case .plane: return UIImage(named: "plane.png")!
case .arrow: return UIImage(named: "arrow.png")!
case .logo: return UIImage(named: "logo.png")!
case .flag: return UIImage(named: "flag.png")!
}
}
var color: UIColor {
switch self {
case .plane: return UIColor.greenColor()
case .arrow: return UIColor.greenColor()
case .logo: return UIColor.greenColor()
case .flag: return UIColor.greenColor()
}
}
}
// usage
Icon.plane.color
Using enums with associated values combined with switch statements you can be very flexible. A first example:
enum Icon {
case plane(img:UIImage, col:UIColor)
case arrow(img:UIImage, col:UIColor)
case logo(img:UIImage, col:UIColor)
case flag(img:UIImage, col:UIColor)
var values:(img:UIImage,col:UIColor) {
switch self {
case let .plane(image, color):
return (image,color)
case let .arrow(image, color):
return (image,color)
case let .logo(image, color):
return (image,color)
case let .flag(image, color):
return (image,color)
}
}
}
var a = Icon.plane(img: UIImage(named: "image.png")!, col: UIColor.blueColor())
a.values.col
a.values.img
and a second example:
enum Icon {
case plane(img:UIImage, col:UIColor)
case arrow(img:UIImage, col:UIColor)
case logo(img:UIImage, col:UIColor)
case flag(img:UIImage, col:UIColor)
var img:UIImage {
switch self {
case let .plane(image, color):
return image
case let .arrow(image, color):
return image
case let .logo(image, color):
return image
case let .flag(image, color):
return image
}
}
var col:UIColor {
switch self {
case let .plane(image, color):
return color
case let .arrow(image, color):
return color
case let .logo(image, color):
return color
case let .flag(image, color):
return color
}
}
}
var a = Icon.plane(img: UIImage(named: "image.png")!, col: UIColor.blueColor())
a.col
a.img
no need for extensions. And if you really do want static values, you could do this:
struct MyIcon {
static let plane = Icon.plane(img: UIImage(named: "image.png")!, col: UIColor.blueColor())
static let arrow = Icon.arrow(img: UIImage(named: "image.png")!, col: UIColor.blueColor())
static let logo = Icon.logo(img: UIImage(named: "image.png")!, col: UIColor.blueColor())
static let flag = Icon.flag(img: UIImage(named: "image.png")!, col: UIColor.blueColor())
}
MyIcon.arrow.col
which might be tidier than placing the fixed literal values inside a switch statement.
Shorter and safer code:
import UIKit
enum Icon: String {
case plane, arrow, logo, flag
var image: UIImage {
// If the image is not available, provide a default value so we dont force optional unwrapping
return UIImage(named: "\(self.rawValue).png") ?? UIImage()
}
var color: UIColor {
switch self {
case .plane: return UIColor.green
case .arrow: return UIColor.green
case .logo: return UIColor.green
case .flag: return UIColor.green
}
}
}
// Usage
Icon.plane.color
Icon.arrow.image
More cleaner and readable
enum Icon {
case plane
case arrow
case logo
case flag
var image: UIImage {
return value.image
}
var color: UIColor {
return value.color
}
private var value: (image: UIImage, color: UIColor) {
switch self {
case .plane: return (UIImage(named: "plane.png")!, UIColor.green)
case .arrow: return (UIImage(named: "arrow.png")!, UIColor.green)
case .logo: return (UIImage(named: "logo.png")!, UIColor.green)
case .flag: return (UIImage(named: "flag.png")!, UIColor.green)
}
}
}
// Use
Icon.plane.image
Icon.plane.color