Thursday, September 1, 2016

Struct and Enum used together in Swift 3

Sample code in swift 3 (Xcode 8 beta 6)

This demonstrates
(1) the use of enum initialization method for default value
(2) the use of struct initialization method for default variable value
(3) the use of multiple guard statements in Swift 3
(4) struct and enum used together for data model that has different associated values based on enum type
(5) internal var for mutating associated values
playgroundcode.swift   Select all
//: Playground - noun: a place where people can play import Foundation struct Instrument { enum CurrencyType:String { case USD case EUR case GBP case JPY case EURUSD case USDEUR case GBPUSD case USDGBP case USDJPY case JPYUSD case USDCNY case USDHKD // default initialization methods for currencyType enum init() { self = .USD } } enum InstrumentType { case FloatingRateBond(ccy:String, faceamount: Double?, redemption: Double?, issueDate:String, maturityDate:String, floatingRate:String) case FixedRateBond(ccy:String, faceamount: Double?, redemption: Double?, issueDate:String, maturityDate:String, fixedRate:String) case ZeroCouponBond(ccy:String, faceamount: Double?, redemption: Double?, issueDate:String, maturityDate:String) case Unknown // default initialization methods for different instrumentType enum init() { self = .Unknown } init(ccy: String, issueDate:String, maturityDate:String, floatingRate:String) { self = .FloatingRateBond(ccy: ccy, faceamount: 100.0, redemption: 100.0, issueDate: issueDate, maturityDate: maturityDate, floatingRate: floatingRate) } init(ccy: String, issueDate:String, maturityDate:String, fixedRate:String) { self = .FixedRateBond(ccy: ccy, faceamount: 100.0, redemption: 100.0, issueDate: issueDate, maturityDate: maturityDate, fixedRate: fixedRate) } init(ccy: String, redemption:Double?, issueDate:String, maturityDate:String) { self = .ZeroCouponBond(ccy: ccy, faceamount: 100.0, redemption: redemption, issueDate: issueDate, maturityDate: maturityDate) } } var title:String? = nil var name:String var type:InstrumentType var typeName: String { switch self.type { case .FloatingRateBond : return "FloatingRateBond" case .FixedRateBond : return "FixedRateBond" case .ZeroCouponBond : return "ZeroCouponBond" default : return "Unknown" } } // provide mutating internal var internal var _ccy : String? = nil var ccy : String? { get { guard let ccy: String = { switch self.type { // use case let here case let .FloatingRateBond(tuple) : return self._ccy ?? tuple.ccy case let .FixedRateBond(tuple) : return self._ccy ?? tuple.ccy case let .ZeroCouponBond(tuple) : return self._ccy ?? tuple.ccy default : return nil } }() else { return nil } return ccy } set { // cannot throws error here yet switch self.type { case .FloatingRateBond, .FixedRateBond, .ZeroCouponBond : _ccy = newValue! default : () } } } var currencyType : CurrencyType? { // multiple guard statements Swift 3 guard let ccy = self.ccy, let ccytype:CurrencyType = CurrencyType(rawValue: ccy.uppercased()) else { return nil } return ccytype } // no mutating internal var var faceAmount : Double? { guard let faceAmount: Double = { switch self.type { // use pattern matching here case .FloatingRateBond(_, let faceAmount, _, _, _, _) : return faceAmount case .FixedRateBond(_, let faceAmount, _, _, _, _) : return faceAmount case .ZeroCouponBond(_, let faceAmount, _, _, _) : return faceAmount default : return nil } }() else { return nil } return faceAmount } var redemption : Double? { guard let redemption: Double = { switch self.type { case .FloatingRateBond(_, _, let redemption, _, _, _) : return redemption case .FixedRateBond(_, _, let redemption, _, _, _) : return redemption case .ZeroCouponBond(_, _, let redemption, _, _) : return redemption default : return nil } }() else { return nil } return redemption } var issueDate : String? { guard let issueDate: String = { switch self.type { case .FloatingRateBond(_, _, _, let issueDate, _, _) : return issueDate case .FixedRateBond(_, _, _, let issueDate, _, _) : return issueDate case .ZeroCouponBond(_, _, _, let issueDate, _) : return issueDate default : return nil } }() else { return nil } return issueDate.isEmpty ? nil : issueDate } var maturityDate : String? { guard let maturityDate: String = { switch self.type { case .FloatingRateBond(_, _, _, _, let maturityDate, _) : return maturityDate case .FixedRateBond(_, _, _, _, let maturityDate, _) : return maturityDate case .ZeroCouponBond(_, _, _, _, let maturityDate) : return maturityDate default : return nil } }() else { return nil } return maturityDate.isEmpty ? nil : maturityDate } var description: String { switch self.type { case .FloatingRateBond(let ccy, _, _, let issueDate, let maturityDate, let floatingRate) : return "\(self.name) FloatingRateBond \(ccy) \(issueDate) \(maturityDate) \(floatingRate)" case .FixedRateBond(let ccy, _, _, let issueDate, let maturityDate, let fixedRate) : return "\(self.name) FixedRateBond \(ccy) \(issueDate) \(maturityDate) \(fixedRate)" case .ZeroCouponBond : return "ZeroCouponBond" default : return "Unknown Description" } } var marketData:AnyObject? = nil var pricingEngine:AnyObject? = nil // default initialization methods for Instrument struct init(name: String, type: InstrumentType) { self.name = name self.type = type } init(name: String) { self.name = name self.type = InstrumentType() } func NPV() -> Double? { // multiple guard statements Swift 3 guard let data = self.marketData, let engine = self.pricingEngine else { return nil } // TODO: marketData and pricingEngine print("\(data) \(engine)") return 0.0 } } var portfolio = [ Instrument(name:"B00001USDLIBOR", type: Instrument.InstrumentType(ccy: "USD", issueDate: "20100101", maturityDate: "20101231", floatingRate: "LIBOR+1")), Instrument(name:"B0002JPY0.5%", type: .FixedRateBond(ccy: "JPY", faceamount: 100.0, redemption: 100.0, issueDate: "20110101", maturityDate: "20291231", fixedRate: "0.5%")), Instrument(name:"ZC0001", type: Instrument.InstrumentType(ccy: "USD", redemption: 120.0, issueDate: "20110101", maturityDate: "20291231")), Instrument(name:"U0001")] portfolio.forEach{ ( a : Instrument) -> () in print(a.typeName) print(a) if let b = a.currencyType { print(b) } else { print("missing / invalid currency info \(a.ccy)") } print(a.issueDate ?? "No issueDate") print(a.maturityDate ?? "No maturityDate") print(a.description) print(a.NPV() ?? "No NPV") } print(portfolio[2].ccy) portfolio[2].ccy = "JPY" print(portfolio[2].currencyType) print(portfolio[2].ccy)



Differences between struct Instrument and class Instrument
(1) class is reference type and struct is value type.
(2) open class can be subclassed in other modules, but not for struct.
(3) class func is not allowed, use static func instead in struct.
(4) reference and value types can be modified in the forEach or other iteration methods, as below.
(5) modification inside iteration is local copy only for struct due to value type nature.
playgroundcode.swift   Select all
open class Instrument { .... } .... // cannot set var a here in function parameter for Swift 3 (SE-0003) portfolio.forEach{ ( a : Instrument) -> () in var a = a // using shadow variable switch a.type { case .Unknown : a.type = Instrument.InstrumentType(ccy: "GBP", redemption: 110.0, issueDate: "20010101", maturityDate: "20291231") // modification is local copy only inside iteration for struct a.name = "MODIFIED NAME" default: () // do nothing } print(a.typeName) print(a) if let t = a.currencyType { print(t) } else { print("missing / invalid currency info \(a.ccy)") } print(a.issueDate ?? "No issueDate") print(a.maturityDate ?? "No maturityDate") print(a.description) print(a.NPV() ?? "No NPV") } print(portfolio[3].name) // instance can be modified inside print(portfolio[3].type) // modification is local copy only inside iteration for struct



No comments: