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