Classes for representing teleconferencing numbers

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
2
down vote

favorite












I'm developing an app that joins tele-conference calls so we use conference numbers a lot. I've got this basic structure to encapsulate the information regarding a conference number.



///A model that represents a teleconference number.
struct ConferenceNumber

let iso : String ///Interntational Standardised 2 letter Abbreviation for country.
let conferenceProvider: ConferenceProvider ///The company that hosts the conference call the number is for.
let number : String ///The phone number
let toll : Bool ///Whether the number is a paid or free line.
let country: String

init(ISO: String, country: String, number:String, provider: ConferenceProvider, toll: Bool)
self.iso = ISO
self.country = ConferenceNumber.localeName(from: ISO) ?? country
self.number = number
self.conferenceProvider = provider
self.toll = toll




I'm running in to two problems:



  1. This class if often bypassed in favour of storing the numbers in [String] arrays. I can't think of a nice way to put this in to an object as ConferenceNumber.number doesn't sound right.


  2. The conferenceProvider property has resulted in over a dozen switch statement in our code base which all look similar to the code below. Is it possible to reduce repeating myself this often with protocols or subclasses?


ConferenceProvider enum



///Defines the type of conference call service the meeting uses if any.
public enum ConferenceProvider : String
case unknown = "",
zoom = "Zoom",
att = "AT&T",
webex = "WebEx",
arkadin = "Arkadin"



Class with ConferenceProvider switch statements example



import Foundation

///Builds, stores, retrieves and queries conference number directories.
class ConferenceNumberDirectory

static let att: [ConferenceNumber] =
return buildDirectory(for: .att, from: jsonArray)
()

static let arkadinMeetings: [ConferenceNumber] =
return buildDirectory(for: .arkadinMeetings, from: jsonArray)
()

static let webex: [ConferenceNumber] =
return buildDirectory(for: .webex, from: jsonArray)
()

static let zoom: [ConferenceNumber] =
return buildDirectory(for: .zoom, from: jsonArray)
()

static let allProviders: [ConferenceNumber] =
return att + arkadinMeetings + webex + zoom
()

static let attPhoneNumbers: [String] =
return ConferenceNumberDirectory.att.map( $0.number )
()

static let arkadinMeetingsPhoneNumbers: [String] =
return ConferenceNumberDirectory.arkadinMeetings.map( $0.number )
()

static let webexPhoneNumbers: [String] =
return ConferenceNumberDirectory.webex.map( $0.number )
()

static let zoomPhoneNumbers: [String] =
return ConferenceNumberDirectory.zoom.map( $0.number )
()

static let allPhoneNumber: [String] =
return ConferenceNumberDirectory.allProviders.map $0.number
()

private static let jsonArray: [Any]? =
guard let numbersFilePath = Bundle.main.path(forResource: "numbers", ofType:"json") else
return nil


let data = try! Data(contentsOf: URL(fileURLWithPath:numbersFilePath), options: .uncached)

return try! JSONSerialization.jsonObject(with: data) as? [Any]
()

///Takes the outputted [Any]? from a JSON file and builds an Array of ConferenceNumber objects suitable for Call In to use.
static func buildDirectory(for conferenceProvider: ConferenceProvider, from jsonArray: [Any]?) -> [ConferenceNumber]
var directory = [ConferenceNumber]()
do
guard let rootJSONArray = jsonArray else
throw SerializationError.missing("JSON Root")


for entry in rootJSONArray
guard let dictionary = entry as? [String: Any] else
throw SerializationError.missing("JSON Root")

guard let isoCode = dictionary["ISO Code"] as? String else
throw SerializationError.missing("isoCode")

guard let country = dictionary["Country"] as? String else
throw SerializationError.missing("country")


if let key = getTollKey(for: conferenceProvider), let tollNumbers = try self.extractNumbers(from: dictionary, forKey: key)
directory.append(contentsOf: tollNumbers.mapConferenceNumber(ISO: isoCode, country: country, number: $0, provider: conferenceProvider, toll: true) )

if let key = getTollFreeKey(for: conferenceProvider), let tollFreeNumbers = try self.extractNumbers(from: dictionary, forKey: key)
directory.append(contentsOf: tollFreeNumbers.mapConferenceNumber(ISO: isoCode, country: country, number: $0, provider: conferenceProvider, toll: false))



catch let error
print(error)

return directory


///Returns the directory for a given conference provider. Returns all if no numbers exist for this provider.
static func getDirectory(for conferenceProvider: ConferenceProvider) -> [ConferenceNumber]?
switch conferenceProvider
case .arkadinMeetings:
return ConferenceNumberDirectory.arkadinMeetings
case .att:
return ConferenceNumberDirectory.att
case .webex:
return ConferenceNumberDirectory.webex
case .zoom:
return ConferenceNumberDirectory.zoom
default:
return ConferenceNumberDirectory.att + ConferenceNumberDirectory.arkadinMeetings + ConferenceNumberDirectory.webex + ConferenceNumberDirectory.zoom



/**
Returns a ConferenceNumber that matches the phone number and the country ISO code provided. Returns nil if no number is found.
- parameter number: The phone number to try and find a match for
- parameter isoCode: The international standard organizations 2 digit code that represents a country for all known languages

At present we do not store ConferenceNumber Objects in the settings so we often need to convert the Strings we store back in to their original conference number. Where we to save the object we would still need this as user entered numbers will not contain all ConferenceNumber properties.
*/
static func findConferenceNumber(usingNumber number: String, andISOCode isoCode: String?, fromDirectory conferenceProvider: ConferenceProvider = .unknown) -> ConferenceNumber?

if number.isEmpty


/**
Returns all conference numbers that share the same number.
Similarly as the previous one, findConferenceNumber() but getting all possibilities because we know that some countries share the same number, example: 8884266840
*/
static func findConferenceNumbers(usingNumber number: String, fromDirectory conferenceProvider: ConferenceProvider? = .unknown) -> [ConferenceNumber]

if number.isEmpty
return


switch conferenceProvider
case .att?:
return ConferenceNumberDirectory.att.filter( $0.number == number)
case .arkadinMeetings?:
return ConferenceNumberDirectory.arkadinMeetings.filter($0.number == number)
case .webex?:
return ConferenceNumberDirectory.webex.filter( $0.number == number)
case .zoom?:
return ConferenceNumberDirectory.zoom.filter($0.number == number)
default:
let allConferenceNumbers = ConferenceNumberDirectory.att + ConferenceNumberDirectory.arkadinMeetings + ConferenceNumberDirectory.webex + ConferenceNumberDirectory.zoom
return allConferenceNumbers.filter($0.number == number)



///Find the provider for a given number, return unknown if the number does not exist
///- warning: This will return the first provider that has the current number seeing as providers don't share numbers.
static func findProvider(forNumber queriedNumber: String) -> ConferenceProvider
return ConferenceNumberDirectory.allProviders.first $0.number == queriedNumber ?.conferenceProvider ?? .unknown


///Returns an array of all country iso codes a queriedNumber is used by.
static func findCountryISOCodes(forNumber queriedNumber: String) -> [String]
return ConferenceNumberDirectory.allProviders.filter $0.number == queriedNumber .map( $0.iso )



///Extracts telephone numbers from a given dictionary and key by taking the value as a string then returning an array of each comma seperated component of that string.
private static func extractNumbers(from dictionary: [String: Any], forKey key: String?) throws -> [String]?
guard let jsonKey = key ,let tollNumbersString = dictionary[jsonKey] as? String else
throw SerializationError.missing(key ?? "key is nil")

if tollNumbersString.isEmpty
return nil

return tollNumbersString.components(separatedBy: ",")


///Returns the JSON key to access a given ConferenceProviders toll numbers.
private static func getTollKey(for provider: ConferenceProvider) -> String?
switch provider
case .att:
return "AT&T toll"
case .arkadinMeetings:
return "arkadin Meetings toll"
case .webex:
return "Webex toll"
case .zoom:
return "Zoom toll"
default:
return nil



///Returns the JSON key to access a given ConferenceProviders toll-free numbers.
private static func getTollFreeKey(for provider: ConferenceProvider) -> String?
switch provider
case .att:
return "AT&T toll-free"
case .arkadinMeetings:
return "arkadin Meetings toll-free"
case .webex:
return "Webex toll-free"
default:
return nil





Class with ConferenceProvider switch statements Example 2



/// Data class, responsible for storing and retrieving of user settings.

import Foundation

class Settings
/// init user defaults with the app group suite to be able to use the same values in the app and in the widget
static let defaults = SharedGlobals.appGroupDefaults

static var id: String?
get return self.defaults.string(forKey: "id")
set self.defaults.set(newValue, forKey: "id")


static var attNumber: String?
get return self.defaults.string(forKey: "attNumber")
set self.defaults.set(newValue, forKey: "attNumber")


static var attCountry: String?
get return self.defaults.string(forKey: "attCountry")
set self.defaults.set(newValue, forKey: "attCountry")


static var attISO: String?
get return self.defaults.string(forKey: "attISO")
set self.defaults.set(newValue, forKey: "attISO")


static var attHostCodes: [String]
get if let codes = self.defaults.stringArray(forKey: "hostCodes")
return codes
else
return


set self.defaults.set(newValue, forKey: "hostCodes")


///AT&T participant codes, these are paired to hostcodes (by index) so we can determine which host code to use via parsed participant codes.
static var attParticipantCodes: [String]
get if let codes = self.defaults.stringArray(forKey: "participantCodes")
return codes
else
return


set self.defaults.set(newValue, forKey: "participantCodes")


///arkadinMeetings (Smartcloud)
static var arkadinMeetingsCountry: String?
get return self.defaults.string(forKey: "arkadinMeetingsCountry")
set self.defaults.set(newValue, forKey: "arkadinMeetingsCountry")



static var arkadinMeetingsNumber: String?
get return self.defaults.string(forKey: "arkadinMeetingsNumber")
set self.defaults.set(newValue, forKey: "arkadinMeetingsNumber")


static var arkadinMeetingsISO: String?
get return self.defaults.string(forKey: "arkadinMeetingsISO")
set self.defaults.set(newValue, forKey: "arkadinMeetingsISO")



static var arkadinMeetingsModeratorCode: String?
get return self.defaults.string(forKey: "arkadinMeetingsModeratorCode")
set self.defaults.set(newValue, forKey: "arkadinMeetingsModeratorCode")



///WEBEX
static var webexNumber: String?
get return self.defaults.string(forKey: "webexNumber")
set self.defaults.set(newValue, forKey: "webexNumber")


static var webexCountry: String?
get return self.defaults.string(forKey: "webexCountry")
set self.defaults.set(newValue, forKey: "webexCountry")


static var webexISO: String?
get return self.defaults.string(forKey: "webexISO")
set self.defaults.set(newValue, forKey: "webexISO")


//Webex participant code, this is stored as the participant code is required to dial in even when the user is hosting.
static var webexAccessCode: String?
get return self.defaults.string(forKey: "webexAccessCode")
set self.defaults.set(newValue, forKey: "webexAccessCode")


static var webexHostPin: String?
get return self.defaults.string(forKey: "webexHostPin")
set self.defaults.set(newValue, forKey: "webexHostPin")



///ZOOM
static var zoomNumber: String?
get return self.defaults.string(forKey: "zoomNumber")
set self.defaults.set(newValue, forKey: "zoomNumber")


static var zoomCountry: String?
get return self.defaults.string(forKey: "zoomCountry")
set self.defaults.set(newValue, forKey: "zoomCountry")


static var zoomISO: String?
get return self.defaults.string(forKey: "zoomISO")
set self.defaults.set(newValue, forKey: "zoomISO")


static var zoomMeetingId: String?
get return self.defaults.string(forKey: "zoomMeetingId")
set self.defaults.set(newValue, forKey: "zoomMeetingId")



// MARK: User

///Country
static var country: String?
get return self.defaults.string(forKey: "country")
set self.defaults.set(newValue, forKey: "country")


static var email: String?
get return self.defaults.string(forKey: "email")
set self.defaults.set(newValue, forKey: "email")


///Return the user's name. If this hasn't been saved extract their name from their arkadin calendar and save it.
static var usersName: String?

if let savedName = defaults.string(forKey: "name")
return savedName


if let calendar = MeetingsFetcher().getarkadinCalendar().first,
let parsedName = calendar.title.parseFirstMatch(from: Regex.organizerNameFromEKCalendar)

defaults.set(parsedName, forKey: "name")
return parsedName

return AppConfigInteractor.getUserName()


static var callMethod: String
get
if let method = self.defaults.string(forKey: "callMethod")
return method
else
return CallMethod.methods.device.rawValue


set self.defaults.set(newValue, forKey: "callMethod")


///VIPS: email + relation for a max of 9 VIPs
static var VIPemails: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPemails")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPemails")


static var VIPnames: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPnames")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPnames")


static var VIPrelations: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPrelations")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPrelations")



///notification
static var notificationsAllowed: Bool
get return self.defaults.bool(forKey: "notification")
set self.defaults.set(newValue, forKey: "notification")


///location awareness
static var locationAwarenessAllowed: Bool
get return self.defaults.bool(forKey: "locationAwareness")
set self.defaults.set(newValue, forKey: "locationAwareness")


///promt for authentication awareness
static var promptForAuthentication: Bool
get return self.defaults.bool(forKey: "promptForAuthentication")
set self.defaults.set(newValue, forKey: "promptForAuthentication")


///last time the user was prompt for authentication
static var lastAuthenticationPromptDate: Date?
get return self.defaults.object(forKey: "lastAuthenticationPromptDate") as? Date
set self.defaults.set(newValue, forKey: "lastAuthenticationPromptDate")


///audiosummary
static var audioSummary: Bool
get return self.defaults.bool(forKey: "audioSummary")
set self.defaults.set(newValue, forKey: "audioSummary")



/// Local settings (not stored in the cloud)

static var notifyNumberOfMinutesBeforeEvent: Double
get return self.defaults.double(forKey: "notifyNumberOfMinutesBeforeEvent")
set self.defaults.set(newValue, forKey: "notifyNumberOfMinutesBeforeEvent")


static var landingPageWasReadToday: Bool
get return self.defaults.bool(forKey: "landingPageWasReadToday")
set self.defaults.set(newValue, forKey: "landingPageWasReadToday")


static var lastDayAudioWasRead: Int
get return self.defaults.integer(forKey: "lastDayAudioWasRead")
set self.defaults.set(newValue, forKey: "lastDayAudioWasRead")


static var lastDayPopUpWasDismissed: Int
get return self.defaults.integer(forKey: "lastDayPopUpWasDismissed")
set self.defaults.set(newValue, forKey: "lastDayPopUpWasDismissed")


static var meetingsDismissed: [String]
get if let meetings = self.defaults.stringArray(forKey: "meetingsDismissed")
return meetings
else
return


set self.defaults.set(newValue, forKey: "meetingsDismissed")


static var promptToModifyNumber: Bool
get return self.defaults.bool(forKey: "promptToModifyNumber")
set self.defaults.set(newValue, forKey: "promptToModifyNumber")


///returns true if the user has already gone though the onboarding process
static var didOnboarding: Bool
get return self.defaults.bool(forKey: "didOnboarding")
set self.defaults.set(newValue, forKey: "didOnboarding")


static var advancedParticipantCode: String
get
if let code = self.defaults.string(forKey:"advancedParticipantCode")
return code
else
return SharedGlobals.Call.DEFAULT_PARTICIPANT_CODE


set self.defaults.set(newValue, forKey: "advancedParticipantCode")


static var advancedHostCode: String
get
if let code = self.defaults.string(forKey:"advancedHostCode")
return code
else
return SharedGlobals.Call.DEFAULT_HOST_CODE


set self.defaults.set(newValue, forKey: "advancedHostCode")


static var advancedNoCode: String
get
if let code = self.defaults.string(forKey: "advancedNoCode")
return code
else
return SharedGlobals.Call.DEFAULT_NO_CODE


set self.defaults.set(newValue, forKey: "advancedNoCode")


static var versionID: String?
get return self.defaults.string(forKey: "versionID")
set self.defaults.set(newValue, forKey: "versionID")


static var buildID: String?
get return self.defaults.string(forKey:"buildID")
set self.defaults.set(newValue, forKey: "buildID")


///always use conference number and ignore number in invitation
static var prioritizeSettingsNumbers: Bool
get return self.defaults.bool(forKey: "prioritizeSettingsNumbers")
set self.defaults.set(newValue, forKey: "prioritizeSettingsNumbers")


///Returns the settings number for a given provider or the AT&T number if provider is unknown.
static func phoneNumber(for provider: ConferenceProvider) -> String?
switch provider
case .unknown:
return attNumber ?? webexNumber ?? arkadinMeetingsNumber ?? zoomNumber
case .att:
return attNumber
case .arkadinMeetings:
return arkadinMeetingsNumber
case .webex:
return webexNumber
case .zoom:
return zoomNumber



///Returns the iso code for the current Settings number of a given provider, returns nil if no settings number is set.
static func isoCodeForSettingsNumber(with provider: ConferenceProvider) -> String?
switch provider
case .unknown:
return nil
case .att:
return attISO
case .arkadinMeetings:
return arkadinMeetingsISO
case .webex:
return webexISO
case .zoom:
return zoomISO



/**
Returns the participant codes for a given provider

- Note: Returns an empty array for arkadinMeetings as we don't store these participant codes.
- Note: For unknown providers this returns the participant codes for all meetings other than AT&T
as the user is unlikely to ever dial in with their own AT&T participant code (they'd use their host code).
*/
static func participantCodes(for provider: ConferenceProvider) -> [String]
switch provider
case .unknown:
return [webexAccessCode, zoomMeetingId].flatMap$0
case .att:
return attParticipantCodes
case .arkadinMeetings:
return
case .webex:
return [webexAccessCode].flatMap$0
case .zoom:
return [zoomMeetingId].flatMap$0



/**
Returns the host codes for a given provider

- Note: Returns an empty array for zoom conference calls as they have no host codes.
- Note: For unknown providers this returns all hostcodes
*/
static func hostCodes(for provider: ConferenceProvider) -> [String]
switch provider
case .unknown:
return attHostCodes + [arkadinMeetingsModeratorCode ,webexHostPin].flatMap$0
case .att:
return attHostCodes
case .arkadinMeetings:
return [arkadinMeetingsModeratorCode].flatMap$0
case .webex:
return [webexHostPin].flatMap$0
case .zoom:
return









share|improve this question





















  • What is the significance of knowing the provider? Why would a user care if AT&T provider the conferencing vs. IBM?
    – user1118321
    Mar 4 at 21:34










  • @user1118321 We store phone numbers for these different providers which we will want to put in to the meeting if we detect text or URLs specific to that provider. The user can also store settings for each providers where they can select or put their own number in as well as add participant codes/ host codes for that number. Different providers also use different dial codes.
    – Deco
    Mar 5 at 14:38






  • 1




    let number : String ///The phone number Imagine if you just named the variable let phoneNumber: String. You wouldn't need a comment. Same with toll -> isTollFree (although that has the inverted meaning)
    – Alexander
    Mar 21 at 18:09
















up vote
2
down vote

favorite












I'm developing an app that joins tele-conference calls so we use conference numbers a lot. I've got this basic structure to encapsulate the information regarding a conference number.



///A model that represents a teleconference number.
struct ConferenceNumber

let iso : String ///Interntational Standardised 2 letter Abbreviation for country.
let conferenceProvider: ConferenceProvider ///The company that hosts the conference call the number is for.
let number : String ///The phone number
let toll : Bool ///Whether the number is a paid or free line.
let country: String

init(ISO: String, country: String, number:String, provider: ConferenceProvider, toll: Bool)
self.iso = ISO
self.country = ConferenceNumber.localeName(from: ISO) ?? country
self.number = number
self.conferenceProvider = provider
self.toll = toll




I'm running in to two problems:



  1. This class if often bypassed in favour of storing the numbers in [String] arrays. I can't think of a nice way to put this in to an object as ConferenceNumber.number doesn't sound right.


  2. The conferenceProvider property has resulted in over a dozen switch statement in our code base which all look similar to the code below. Is it possible to reduce repeating myself this often with protocols or subclasses?


ConferenceProvider enum



///Defines the type of conference call service the meeting uses if any.
public enum ConferenceProvider : String
case unknown = "",
zoom = "Zoom",
att = "AT&T",
webex = "WebEx",
arkadin = "Arkadin"



Class with ConferenceProvider switch statements example



import Foundation

///Builds, stores, retrieves and queries conference number directories.
class ConferenceNumberDirectory

static let att: [ConferenceNumber] =
return buildDirectory(for: .att, from: jsonArray)
()

static let arkadinMeetings: [ConferenceNumber] =
return buildDirectory(for: .arkadinMeetings, from: jsonArray)
()

static let webex: [ConferenceNumber] =
return buildDirectory(for: .webex, from: jsonArray)
()

static let zoom: [ConferenceNumber] =
return buildDirectory(for: .zoom, from: jsonArray)
()

static let allProviders: [ConferenceNumber] =
return att + arkadinMeetings + webex + zoom
()

static let attPhoneNumbers: [String] =
return ConferenceNumberDirectory.att.map( $0.number )
()

static let arkadinMeetingsPhoneNumbers: [String] =
return ConferenceNumberDirectory.arkadinMeetings.map( $0.number )
()

static let webexPhoneNumbers: [String] =
return ConferenceNumberDirectory.webex.map( $0.number )
()

static let zoomPhoneNumbers: [String] =
return ConferenceNumberDirectory.zoom.map( $0.number )
()

static let allPhoneNumber: [String] =
return ConferenceNumberDirectory.allProviders.map $0.number
()

private static let jsonArray: [Any]? =
guard let numbersFilePath = Bundle.main.path(forResource: "numbers", ofType:"json") else
return nil


let data = try! Data(contentsOf: URL(fileURLWithPath:numbersFilePath), options: .uncached)

return try! JSONSerialization.jsonObject(with: data) as? [Any]
()

///Takes the outputted [Any]? from a JSON file and builds an Array of ConferenceNumber objects suitable for Call In to use.
static func buildDirectory(for conferenceProvider: ConferenceProvider, from jsonArray: [Any]?) -> [ConferenceNumber]
var directory = [ConferenceNumber]()
do
guard let rootJSONArray = jsonArray else
throw SerializationError.missing("JSON Root")


for entry in rootJSONArray
guard let dictionary = entry as? [String: Any] else
throw SerializationError.missing("JSON Root")

guard let isoCode = dictionary["ISO Code"] as? String else
throw SerializationError.missing("isoCode")

guard let country = dictionary["Country"] as? String else
throw SerializationError.missing("country")


if let key = getTollKey(for: conferenceProvider), let tollNumbers = try self.extractNumbers(from: dictionary, forKey: key)
directory.append(contentsOf: tollNumbers.mapConferenceNumber(ISO: isoCode, country: country, number: $0, provider: conferenceProvider, toll: true) )

if let key = getTollFreeKey(for: conferenceProvider), let tollFreeNumbers = try self.extractNumbers(from: dictionary, forKey: key)
directory.append(contentsOf: tollFreeNumbers.mapConferenceNumber(ISO: isoCode, country: country, number: $0, provider: conferenceProvider, toll: false))



catch let error
print(error)

return directory


///Returns the directory for a given conference provider. Returns all if no numbers exist for this provider.
static func getDirectory(for conferenceProvider: ConferenceProvider) -> [ConferenceNumber]?
switch conferenceProvider
case .arkadinMeetings:
return ConferenceNumberDirectory.arkadinMeetings
case .att:
return ConferenceNumberDirectory.att
case .webex:
return ConferenceNumberDirectory.webex
case .zoom:
return ConferenceNumberDirectory.zoom
default:
return ConferenceNumberDirectory.att + ConferenceNumberDirectory.arkadinMeetings + ConferenceNumberDirectory.webex + ConferenceNumberDirectory.zoom



/**
Returns a ConferenceNumber that matches the phone number and the country ISO code provided. Returns nil if no number is found.
- parameter number: The phone number to try and find a match for
- parameter isoCode: The international standard organizations 2 digit code that represents a country for all known languages

At present we do not store ConferenceNumber Objects in the settings so we often need to convert the Strings we store back in to their original conference number. Where we to save the object we would still need this as user entered numbers will not contain all ConferenceNumber properties.
*/
static func findConferenceNumber(usingNumber number: String, andISOCode isoCode: String?, fromDirectory conferenceProvider: ConferenceProvider = .unknown) -> ConferenceNumber?

if number.isEmpty


/**
Returns all conference numbers that share the same number.
Similarly as the previous one, findConferenceNumber() but getting all possibilities because we know that some countries share the same number, example: 8884266840
*/
static func findConferenceNumbers(usingNumber number: String, fromDirectory conferenceProvider: ConferenceProvider? = .unknown) -> [ConferenceNumber]

if number.isEmpty
return


switch conferenceProvider
case .att?:
return ConferenceNumberDirectory.att.filter( $0.number == number)
case .arkadinMeetings?:
return ConferenceNumberDirectory.arkadinMeetings.filter($0.number == number)
case .webex?:
return ConferenceNumberDirectory.webex.filter( $0.number == number)
case .zoom?:
return ConferenceNumberDirectory.zoom.filter($0.number == number)
default:
let allConferenceNumbers = ConferenceNumberDirectory.att + ConferenceNumberDirectory.arkadinMeetings + ConferenceNumberDirectory.webex + ConferenceNumberDirectory.zoom
return allConferenceNumbers.filter($0.number == number)



///Find the provider for a given number, return unknown if the number does not exist
///- warning: This will return the first provider that has the current number seeing as providers don't share numbers.
static func findProvider(forNumber queriedNumber: String) -> ConferenceProvider
return ConferenceNumberDirectory.allProviders.first $0.number == queriedNumber ?.conferenceProvider ?? .unknown


///Returns an array of all country iso codes a queriedNumber is used by.
static func findCountryISOCodes(forNumber queriedNumber: String) -> [String]
return ConferenceNumberDirectory.allProviders.filter $0.number == queriedNumber .map( $0.iso )



///Extracts telephone numbers from a given dictionary and key by taking the value as a string then returning an array of each comma seperated component of that string.
private static func extractNumbers(from dictionary: [String: Any], forKey key: String?) throws -> [String]?
guard let jsonKey = key ,let tollNumbersString = dictionary[jsonKey] as? String else
throw SerializationError.missing(key ?? "key is nil")

if tollNumbersString.isEmpty
return nil

return tollNumbersString.components(separatedBy: ",")


///Returns the JSON key to access a given ConferenceProviders toll numbers.
private static func getTollKey(for provider: ConferenceProvider) -> String?
switch provider
case .att:
return "AT&T toll"
case .arkadinMeetings:
return "arkadin Meetings toll"
case .webex:
return "Webex toll"
case .zoom:
return "Zoom toll"
default:
return nil



///Returns the JSON key to access a given ConferenceProviders toll-free numbers.
private static func getTollFreeKey(for provider: ConferenceProvider) -> String?
switch provider
case .att:
return "AT&T toll-free"
case .arkadinMeetings:
return "arkadin Meetings toll-free"
case .webex:
return "Webex toll-free"
default:
return nil





Class with ConferenceProvider switch statements Example 2



/// Data class, responsible for storing and retrieving of user settings.

import Foundation

class Settings
/// init user defaults with the app group suite to be able to use the same values in the app and in the widget
static let defaults = SharedGlobals.appGroupDefaults

static var id: String?
get return self.defaults.string(forKey: "id")
set self.defaults.set(newValue, forKey: "id")


static var attNumber: String?
get return self.defaults.string(forKey: "attNumber")
set self.defaults.set(newValue, forKey: "attNumber")


static var attCountry: String?
get return self.defaults.string(forKey: "attCountry")
set self.defaults.set(newValue, forKey: "attCountry")


static var attISO: String?
get return self.defaults.string(forKey: "attISO")
set self.defaults.set(newValue, forKey: "attISO")


static var attHostCodes: [String]
get if let codes = self.defaults.stringArray(forKey: "hostCodes")
return codes
else
return


set self.defaults.set(newValue, forKey: "hostCodes")


///AT&T participant codes, these are paired to hostcodes (by index) so we can determine which host code to use via parsed participant codes.
static var attParticipantCodes: [String]
get if let codes = self.defaults.stringArray(forKey: "participantCodes")
return codes
else
return


set self.defaults.set(newValue, forKey: "participantCodes")


///arkadinMeetings (Smartcloud)
static var arkadinMeetingsCountry: String?
get return self.defaults.string(forKey: "arkadinMeetingsCountry")
set self.defaults.set(newValue, forKey: "arkadinMeetingsCountry")



static var arkadinMeetingsNumber: String?
get return self.defaults.string(forKey: "arkadinMeetingsNumber")
set self.defaults.set(newValue, forKey: "arkadinMeetingsNumber")


static var arkadinMeetingsISO: String?
get return self.defaults.string(forKey: "arkadinMeetingsISO")
set self.defaults.set(newValue, forKey: "arkadinMeetingsISO")



static var arkadinMeetingsModeratorCode: String?
get return self.defaults.string(forKey: "arkadinMeetingsModeratorCode")
set self.defaults.set(newValue, forKey: "arkadinMeetingsModeratorCode")



///WEBEX
static var webexNumber: String?
get return self.defaults.string(forKey: "webexNumber")
set self.defaults.set(newValue, forKey: "webexNumber")


static var webexCountry: String?
get return self.defaults.string(forKey: "webexCountry")
set self.defaults.set(newValue, forKey: "webexCountry")


static var webexISO: String?
get return self.defaults.string(forKey: "webexISO")
set self.defaults.set(newValue, forKey: "webexISO")


//Webex participant code, this is stored as the participant code is required to dial in even when the user is hosting.
static var webexAccessCode: String?
get return self.defaults.string(forKey: "webexAccessCode")
set self.defaults.set(newValue, forKey: "webexAccessCode")


static var webexHostPin: String?
get return self.defaults.string(forKey: "webexHostPin")
set self.defaults.set(newValue, forKey: "webexHostPin")



///ZOOM
static var zoomNumber: String?
get return self.defaults.string(forKey: "zoomNumber")
set self.defaults.set(newValue, forKey: "zoomNumber")


static var zoomCountry: String?
get return self.defaults.string(forKey: "zoomCountry")
set self.defaults.set(newValue, forKey: "zoomCountry")


static var zoomISO: String?
get return self.defaults.string(forKey: "zoomISO")
set self.defaults.set(newValue, forKey: "zoomISO")


static var zoomMeetingId: String?
get return self.defaults.string(forKey: "zoomMeetingId")
set self.defaults.set(newValue, forKey: "zoomMeetingId")



// MARK: User

///Country
static var country: String?
get return self.defaults.string(forKey: "country")
set self.defaults.set(newValue, forKey: "country")


static var email: String?
get return self.defaults.string(forKey: "email")
set self.defaults.set(newValue, forKey: "email")


///Return the user's name. If this hasn't been saved extract their name from their arkadin calendar and save it.
static var usersName: String?

if let savedName = defaults.string(forKey: "name")
return savedName


if let calendar = MeetingsFetcher().getarkadinCalendar().first,
let parsedName = calendar.title.parseFirstMatch(from: Regex.organizerNameFromEKCalendar)

defaults.set(parsedName, forKey: "name")
return parsedName

return AppConfigInteractor.getUserName()


static var callMethod: String
get
if let method = self.defaults.string(forKey: "callMethod")
return method
else
return CallMethod.methods.device.rawValue


set self.defaults.set(newValue, forKey: "callMethod")


///VIPS: email + relation for a max of 9 VIPs
static var VIPemails: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPemails")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPemails")


static var VIPnames: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPnames")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPnames")


static var VIPrelations: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPrelations")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPrelations")



///notification
static var notificationsAllowed: Bool
get return self.defaults.bool(forKey: "notification")
set self.defaults.set(newValue, forKey: "notification")


///location awareness
static var locationAwarenessAllowed: Bool
get return self.defaults.bool(forKey: "locationAwareness")
set self.defaults.set(newValue, forKey: "locationAwareness")


///promt for authentication awareness
static var promptForAuthentication: Bool
get return self.defaults.bool(forKey: "promptForAuthentication")
set self.defaults.set(newValue, forKey: "promptForAuthentication")


///last time the user was prompt for authentication
static var lastAuthenticationPromptDate: Date?
get return self.defaults.object(forKey: "lastAuthenticationPromptDate") as? Date
set self.defaults.set(newValue, forKey: "lastAuthenticationPromptDate")


///audiosummary
static var audioSummary: Bool
get return self.defaults.bool(forKey: "audioSummary")
set self.defaults.set(newValue, forKey: "audioSummary")



/// Local settings (not stored in the cloud)

static var notifyNumberOfMinutesBeforeEvent: Double
get return self.defaults.double(forKey: "notifyNumberOfMinutesBeforeEvent")
set self.defaults.set(newValue, forKey: "notifyNumberOfMinutesBeforeEvent")


static var landingPageWasReadToday: Bool
get return self.defaults.bool(forKey: "landingPageWasReadToday")
set self.defaults.set(newValue, forKey: "landingPageWasReadToday")


static var lastDayAudioWasRead: Int
get return self.defaults.integer(forKey: "lastDayAudioWasRead")
set self.defaults.set(newValue, forKey: "lastDayAudioWasRead")


static var lastDayPopUpWasDismissed: Int
get return self.defaults.integer(forKey: "lastDayPopUpWasDismissed")
set self.defaults.set(newValue, forKey: "lastDayPopUpWasDismissed")


static var meetingsDismissed: [String]
get if let meetings = self.defaults.stringArray(forKey: "meetingsDismissed")
return meetings
else
return


set self.defaults.set(newValue, forKey: "meetingsDismissed")


static var promptToModifyNumber: Bool
get return self.defaults.bool(forKey: "promptToModifyNumber")
set self.defaults.set(newValue, forKey: "promptToModifyNumber")


///returns true if the user has already gone though the onboarding process
static var didOnboarding: Bool
get return self.defaults.bool(forKey: "didOnboarding")
set self.defaults.set(newValue, forKey: "didOnboarding")


static var advancedParticipantCode: String
get
if let code = self.defaults.string(forKey:"advancedParticipantCode")
return code
else
return SharedGlobals.Call.DEFAULT_PARTICIPANT_CODE


set self.defaults.set(newValue, forKey: "advancedParticipantCode")


static var advancedHostCode: String
get
if let code = self.defaults.string(forKey:"advancedHostCode")
return code
else
return SharedGlobals.Call.DEFAULT_HOST_CODE


set self.defaults.set(newValue, forKey: "advancedHostCode")


static var advancedNoCode: String
get
if let code = self.defaults.string(forKey: "advancedNoCode")
return code
else
return SharedGlobals.Call.DEFAULT_NO_CODE


set self.defaults.set(newValue, forKey: "advancedNoCode")


static var versionID: String?
get return self.defaults.string(forKey: "versionID")
set self.defaults.set(newValue, forKey: "versionID")


static var buildID: String?
get return self.defaults.string(forKey:"buildID")
set self.defaults.set(newValue, forKey: "buildID")


///always use conference number and ignore number in invitation
static var prioritizeSettingsNumbers: Bool
get return self.defaults.bool(forKey: "prioritizeSettingsNumbers")
set self.defaults.set(newValue, forKey: "prioritizeSettingsNumbers")


///Returns the settings number for a given provider or the AT&T number if provider is unknown.
static func phoneNumber(for provider: ConferenceProvider) -> String?
switch provider
case .unknown:
return attNumber ?? webexNumber ?? arkadinMeetingsNumber ?? zoomNumber
case .att:
return attNumber
case .arkadinMeetings:
return arkadinMeetingsNumber
case .webex:
return webexNumber
case .zoom:
return zoomNumber



///Returns the iso code for the current Settings number of a given provider, returns nil if no settings number is set.
static func isoCodeForSettingsNumber(with provider: ConferenceProvider) -> String?
switch provider
case .unknown:
return nil
case .att:
return attISO
case .arkadinMeetings:
return arkadinMeetingsISO
case .webex:
return webexISO
case .zoom:
return zoomISO



/**
Returns the participant codes for a given provider

- Note: Returns an empty array for arkadinMeetings as we don't store these participant codes.
- Note: For unknown providers this returns the participant codes for all meetings other than AT&T
as the user is unlikely to ever dial in with their own AT&T participant code (they'd use their host code).
*/
static func participantCodes(for provider: ConferenceProvider) -> [String]
switch provider
case .unknown:
return [webexAccessCode, zoomMeetingId].flatMap$0
case .att:
return attParticipantCodes
case .arkadinMeetings:
return
case .webex:
return [webexAccessCode].flatMap$0
case .zoom:
return [zoomMeetingId].flatMap$0



/**
Returns the host codes for a given provider

- Note: Returns an empty array for zoom conference calls as they have no host codes.
- Note: For unknown providers this returns all hostcodes
*/
static func hostCodes(for provider: ConferenceProvider) -> [String]
switch provider
case .unknown:
return attHostCodes + [arkadinMeetingsModeratorCode ,webexHostPin].flatMap$0
case .att:
return attHostCodes
case .arkadinMeetings:
return [arkadinMeetingsModeratorCode].flatMap$0
case .webex:
return [webexHostPin].flatMap$0
case .zoom:
return









share|improve this question





















  • What is the significance of knowing the provider? Why would a user care if AT&T provider the conferencing vs. IBM?
    – user1118321
    Mar 4 at 21:34










  • @user1118321 We store phone numbers for these different providers which we will want to put in to the meeting if we detect text or URLs specific to that provider. The user can also store settings for each providers where they can select or put their own number in as well as add participant codes/ host codes for that number. Different providers also use different dial codes.
    – Deco
    Mar 5 at 14:38






  • 1




    let number : String ///The phone number Imagine if you just named the variable let phoneNumber: String. You wouldn't need a comment. Same with toll -> isTollFree (although that has the inverted meaning)
    – Alexander
    Mar 21 at 18:09












up vote
2
down vote

favorite









up vote
2
down vote

favorite











I'm developing an app that joins tele-conference calls so we use conference numbers a lot. I've got this basic structure to encapsulate the information regarding a conference number.



///A model that represents a teleconference number.
struct ConferenceNumber

let iso : String ///Interntational Standardised 2 letter Abbreviation for country.
let conferenceProvider: ConferenceProvider ///The company that hosts the conference call the number is for.
let number : String ///The phone number
let toll : Bool ///Whether the number is a paid or free line.
let country: String

init(ISO: String, country: String, number:String, provider: ConferenceProvider, toll: Bool)
self.iso = ISO
self.country = ConferenceNumber.localeName(from: ISO) ?? country
self.number = number
self.conferenceProvider = provider
self.toll = toll




I'm running in to two problems:



  1. This class if often bypassed in favour of storing the numbers in [String] arrays. I can't think of a nice way to put this in to an object as ConferenceNumber.number doesn't sound right.


  2. The conferenceProvider property has resulted in over a dozen switch statement in our code base which all look similar to the code below. Is it possible to reduce repeating myself this often with protocols or subclasses?


ConferenceProvider enum



///Defines the type of conference call service the meeting uses if any.
public enum ConferenceProvider : String
case unknown = "",
zoom = "Zoom",
att = "AT&T",
webex = "WebEx",
arkadin = "Arkadin"



Class with ConferenceProvider switch statements example



import Foundation

///Builds, stores, retrieves and queries conference number directories.
class ConferenceNumberDirectory

static let att: [ConferenceNumber] =
return buildDirectory(for: .att, from: jsonArray)
()

static let arkadinMeetings: [ConferenceNumber] =
return buildDirectory(for: .arkadinMeetings, from: jsonArray)
()

static let webex: [ConferenceNumber] =
return buildDirectory(for: .webex, from: jsonArray)
()

static let zoom: [ConferenceNumber] =
return buildDirectory(for: .zoom, from: jsonArray)
()

static let allProviders: [ConferenceNumber] =
return att + arkadinMeetings + webex + zoom
()

static let attPhoneNumbers: [String] =
return ConferenceNumberDirectory.att.map( $0.number )
()

static let arkadinMeetingsPhoneNumbers: [String] =
return ConferenceNumberDirectory.arkadinMeetings.map( $0.number )
()

static let webexPhoneNumbers: [String] =
return ConferenceNumberDirectory.webex.map( $0.number )
()

static let zoomPhoneNumbers: [String] =
return ConferenceNumberDirectory.zoom.map( $0.number )
()

static let allPhoneNumber: [String] =
return ConferenceNumberDirectory.allProviders.map $0.number
()

private static let jsonArray: [Any]? =
guard let numbersFilePath = Bundle.main.path(forResource: "numbers", ofType:"json") else
return nil


let data = try! Data(contentsOf: URL(fileURLWithPath:numbersFilePath), options: .uncached)

return try! JSONSerialization.jsonObject(with: data) as? [Any]
()

///Takes the outputted [Any]? from a JSON file and builds an Array of ConferenceNumber objects suitable for Call In to use.
static func buildDirectory(for conferenceProvider: ConferenceProvider, from jsonArray: [Any]?) -> [ConferenceNumber]
var directory = [ConferenceNumber]()
do
guard let rootJSONArray = jsonArray else
throw SerializationError.missing("JSON Root")


for entry in rootJSONArray
guard let dictionary = entry as? [String: Any] else
throw SerializationError.missing("JSON Root")

guard let isoCode = dictionary["ISO Code"] as? String else
throw SerializationError.missing("isoCode")

guard let country = dictionary["Country"] as? String else
throw SerializationError.missing("country")


if let key = getTollKey(for: conferenceProvider), let tollNumbers = try self.extractNumbers(from: dictionary, forKey: key)
directory.append(contentsOf: tollNumbers.mapConferenceNumber(ISO: isoCode, country: country, number: $0, provider: conferenceProvider, toll: true) )

if let key = getTollFreeKey(for: conferenceProvider), let tollFreeNumbers = try self.extractNumbers(from: dictionary, forKey: key)
directory.append(contentsOf: tollFreeNumbers.mapConferenceNumber(ISO: isoCode, country: country, number: $0, provider: conferenceProvider, toll: false))



catch let error
print(error)

return directory


///Returns the directory for a given conference provider. Returns all if no numbers exist for this provider.
static func getDirectory(for conferenceProvider: ConferenceProvider) -> [ConferenceNumber]?
switch conferenceProvider
case .arkadinMeetings:
return ConferenceNumberDirectory.arkadinMeetings
case .att:
return ConferenceNumberDirectory.att
case .webex:
return ConferenceNumberDirectory.webex
case .zoom:
return ConferenceNumberDirectory.zoom
default:
return ConferenceNumberDirectory.att + ConferenceNumberDirectory.arkadinMeetings + ConferenceNumberDirectory.webex + ConferenceNumberDirectory.zoom



/**
Returns a ConferenceNumber that matches the phone number and the country ISO code provided. Returns nil if no number is found.
- parameter number: The phone number to try and find a match for
- parameter isoCode: The international standard organizations 2 digit code that represents a country for all known languages

At present we do not store ConferenceNumber Objects in the settings so we often need to convert the Strings we store back in to their original conference number. Where we to save the object we would still need this as user entered numbers will not contain all ConferenceNumber properties.
*/
static func findConferenceNumber(usingNumber number: String, andISOCode isoCode: String?, fromDirectory conferenceProvider: ConferenceProvider = .unknown) -> ConferenceNumber?

if number.isEmpty


/**
Returns all conference numbers that share the same number.
Similarly as the previous one, findConferenceNumber() but getting all possibilities because we know that some countries share the same number, example: 8884266840
*/
static func findConferenceNumbers(usingNumber number: String, fromDirectory conferenceProvider: ConferenceProvider? = .unknown) -> [ConferenceNumber]

if number.isEmpty
return


switch conferenceProvider
case .att?:
return ConferenceNumberDirectory.att.filter( $0.number == number)
case .arkadinMeetings?:
return ConferenceNumberDirectory.arkadinMeetings.filter($0.number == number)
case .webex?:
return ConferenceNumberDirectory.webex.filter( $0.number == number)
case .zoom?:
return ConferenceNumberDirectory.zoom.filter($0.number == number)
default:
let allConferenceNumbers = ConferenceNumberDirectory.att + ConferenceNumberDirectory.arkadinMeetings + ConferenceNumberDirectory.webex + ConferenceNumberDirectory.zoom
return allConferenceNumbers.filter($0.number == number)



///Find the provider for a given number, return unknown if the number does not exist
///- warning: This will return the first provider that has the current number seeing as providers don't share numbers.
static func findProvider(forNumber queriedNumber: String) -> ConferenceProvider
return ConferenceNumberDirectory.allProviders.first $0.number == queriedNumber ?.conferenceProvider ?? .unknown


///Returns an array of all country iso codes a queriedNumber is used by.
static func findCountryISOCodes(forNumber queriedNumber: String) -> [String]
return ConferenceNumberDirectory.allProviders.filter $0.number == queriedNumber .map( $0.iso )



///Extracts telephone numbers from a given dictionary and key by taking the value as a string then returning an array of each comma seperated component of that string.
private static func extractNumbers(from dictionary: [String: Any], forKey key: String?) throws -> [String]?
guard let jsonKey = key ,let tollNumbersString = dictionary[jsonKey] as? String else
throw SerializationError.missing(key ?? "key is nil")

if tollNumbersString.isEmpty
return nil

return tollNumbersString.components(separatedBy: ",")


///Returns the JSON key to access a given ConferenceProviders toll numbers.
private static func getTollKey(for provider: ConferenceProvider) -> String?
switch provider
case .att:
return "AT&T toll"
case .arkadinMeetings:
return "arkadin Meetings toll"
case .webex:
return "Webex toll"
case .zoom:
return "Zoom toll"
default:
return nil



///Returns the JSON key to access a given ConferenceProviders toll-free numbers.
private static func getTollFreeKey(for provider: ConferenceProvider) -> String?
switch provider
case .att:
return "AT&T toll-free"
case .arkadinMeetings:
return "arkadin Meetings toll-free"
case .webex:
return "Webex toll-free"
default:
return nil





Class with ConferenceProvider switch statements Example 2



/// Data class, responsible for storing and retrieving of user settings.

import Foundation

class Settings
/// init user defaults with the app group suite to be able to use the same values in the app and in the widget
static let defaults = SharedGlobals.appGroupDefaults

static var id: String?
get return self.defaults.string(forKey: "id")
set self.defaults.set(newValue, forKey: "id")


static var attNumber: String?
get return self.defaults.string(forKey: "attNumber")
set self.defaults.set(newValue, forKey: "attNumber")


static var attCountry: String?
get return self.defaults.string(forKey: "attCountry")
set self.defaults.set(newValue, forKey: "attCountry")


static var attISO: String?
get return self.defaults.string(forKey: "attISO")
set self.defaults.set(newValue, forKey: "attISO")


static var attHostCodes: [String]
get if let codes = self.defaults.stringArray(forKey: "hostCodes")
return codes
else
return


set self.defaults.set(newValue, forKey: "hostCodes")


///AT&T participant codes, these are paired to hostcodes (by index) so we can determine which host code to use via parsed participant codes.
static var attParticipantCodes: [String]
get if let codes = self.defaults.stringArray(forKey: "participantCodes")
return codes
else
return


set self.defaults.set(newValue, forKey: "participantCodes")


///arkadinMeetings (Smartcloud)
static var arkadinMeetingsCountry: String?
get return self.defaults.string(forKey: "arkadinMeetingsCountry")
set self.defaults.set(newValue, forKey: "arkadinMeetingsCountry")



static var arkadinMeetingsNumber: String?
get return self.defaults.string(forKey: "arkadinMeetingsNumber")
set self.defaults.set(newValue, forKey: "arkadinMeetingsNumber")


static var arkadinMeetingsISO: String?
get return self.defaults.string(forKey: "arkadinMeetingsISO")
set self.defaults.set(newValue, forKey: "arkadinMeetingsISO")



static var arkadinMeetingsModeratorCode: String?
get return self.defaults.string(forKey: "arkadinMeetingsModeratorCode")
set self.defaults.set(newValue, forKey: "arkadinMeetingsModeratorCode")



///WEBEX
static var webexNumber: String?
get return self.defaults.string(forKey: "webexNumber")
set self.defaults.set(newValue, forKey: "webexNumber")


static var webexCountry: String?
get return self.defaults.string(forKey: "webexCountry")
set self.defaults.set(newValue, forKey: "webexCountry")


static var webexISO: String?
get return self.defaults.string(forKey: "webexISO")
set self.defaults.set(newValue, forKey: "webexISO")


//Webex participant code, this is stored as the participant code is required to dial in even when the user is hosting.
static var webexAccessCode: String?
get return self.defaults.string(forKey: "webexAccessCode")
set self.defaults.set(newValue, forKey: "webexAccessCode")


static var webexHostPin: String?
get return self.defaults.string(forKey: "webexHostPin")
set self.defaults.set(newValue, forKey: "webexHostPin")



///ZOOM
static var zoomNumber: String?
get return self.defaults.string(forKey: "zoomNumber")
set self.defaults.set(newValue, forKey: "zoomNumber")


static var zoomCountry: String?
get return self.defaults.string(forKey: "zoomCountry")
set self.defaults.set(newValue, forKey: "zoomCountry")


static var zoomISO: String?
get return self.defaults.string(forKey: "zoomISO")
set self.defaults.set(newValue, forKey: "zoomISO")


static var zoomMeetingId: String?
get return self.defaults.string(forKey: "zoomMeetingId")
set self.defaults.set(newValue, forKey: "zoomMeetingId")



// MARK: User

///Country
static var country: String?
get return self.defaults.string(forKey: "country")
set self.defaults.set(newValue, forKey: "country")


static var email: String?
get return self.defaults.string(forKey: "email")
set self.defaults.set(newValue, forKey: "email")


///Return the user's name. If this hasn't been saved extract their name from their arkadin calendar and save it.
static var usersName: String?

if let savedName = defaults.string(forKey: "name")
return savedName


if let calendar = MeetingsFetcher().getarkadinCalendar().first,
let parsedName = calendar.title.parseFirstMatch(from: Regex.organizerNameFromEKCalendar)

defaults.set(parsedName, forKey: "name")
return parsedName

return AppConfigInteractor.getUserName()


static var callMethod: String
get
if let method = self.defaults.string(forKey: "callMethod")
return method
else
return CallMethod.methods.device.rawValue


set self.defaults.set(newValue, forKey: "callMethod")


///VIPS: email + relation for a max of 9 VIPs
static var VIPemails: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPemails")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPemails")


static var VIPnames: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPnames")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPnames")


static var VIPrelations: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPrelations")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPrelations")



///notification
static var notificationsAllowed: Bool
get return self.defaults.bool(forKey: "notification")
set self.defaults.set(newValue, forKey: "notification")


///location awareness
static var locationAwarenessAllowed: Bool
get return self.defaults.bool(forKey: "locationAwareness")
set self.defaults.set(newValue, forKey: "locationAwareness")


///promt for authentication awareness
static var promptForAuthentication: Bool
get return self.defaults.bool(forKey: "promptForAuthentication")
set self.defaults.set(newValue, forKey: "promptForAuthentication")


///last time the user was prompt for authentication
static var lastAuthenticationPromptDate: Date?
get return self.defaults.object(forKey: "lastAuthenticationPromptDate") as? Date
set self.defaults.set(newValue, forKey: "lastAuthenticationPromptDate")


///audiosummary
static var audioSummary: Bool
get return self.defaults.bool(forKey: "audioSummary")
set self.defaults.set(newValue, forKey: "audioSummary")



/// Local settings (not stored in the cloud)

static var notifyNumberOfMinutesBeforeEvent: Double
get return self.defaults.double(forKey: "notifyNumberOfMinutesBeforeEvent")
set self.defaults.set(newValue, forKey: "notifyNumberOfMinutesBeforeEvent")


static var landingPageWasReadToday: Bool
get return self.defaults.bool(forKey: "landingPageWasReadToday")
set self.defaults.set(newValue, forKey: "landingPageWasReadToday")


static var lastDayAudioWasRead: Int
get return self.defaults.integer(forKey: "lastDayAudioWasRead")
set self.defaults.set(newValue, forKey: "lastDayAudioWasRead")


static var lastDayPopUpWasDismissed: Int
get return self.defaults.integer(forKey: "lastDayPopUpWasDismissed")
set self.defaults.set(newValue, forKey: "lastDayPopUpWasDismissed")


static var meetingsDismissed: [String]
get if let meetings = self.defaults.stringArray(forKey: "meetingsDismissed")
return meetings
else
return


set self.defaults.set(newValue, forKey: "meetingsDismissed")


static var promptToModifyNumber: Bool
get return self.defaults.bool(forKey: "promptToModifyNumber")
set self.defaults.set(newValue, forKey: "promptToModifyNumber")


///returns true if the user has already gone though the onboarding process
static var didOnboarding: Bool
get return self.defaults.bool(forKey: "didOnboarding")
set self.defaults.set(newValue, forKey: "didOnboarding")


static var advancedParticipantCode: String
get
if let code = self.defaults.string(forKey:"advancedParticipantCode")
return code
else
return SharedGlobals.Call.DEFAULT_PARTICIPANT_CODE


set self.defaults.set(newValue, forKey: "advancedParticipantCode")


static var advancedHostCode: String
get
if let code = self.defaults.string(forKey:"advancedHostCode")
return code
else
return SharedGlobals.Call.DEFAULT_HOST_CODE


set self.defaults.set(newValue, forKey: "advancedHostCode")


static var advancedNoCode: String
get
if let code = self.defaults.string(forKey: "advancedNoCode")
return code
else
return SharedGlobals.Call.DEFAULT_NO_CODE


set self.defaults.set(newValue, forKey: "advancedNoCode")


static var versionID: String?
get return self.defaults.string(forKey: "versionID")
set self.defaults.set(newValue, forKey: "versionID")


static var buildID: String?
get return self.defaults.string(forKey:"buildID")
set self.defaults.set(newValue, forKey: "buildID")


///always use conference number and ignore number in invitation
static var prioritizeSettingsNumbers: Bool
get return self.defaults.bool(forKey: "prioritizeSettingsNumbers")
set self.defaults.set(newValue, forKey: "prioritizeSettingsNumbers")


///Returns the settings number for a given provider or the AT&T number if provider is unknown.
static func phoneNumber(for provider: ConferenceProvider) -> String?
switch provider
case .unknown:
return attNumber ?? webexNumber ?? arkadinMeetingsNumber ?? zoomNumber
case .att:
return attNumber
case .arkadinMeetings:
return arkadinMeetingsNumber
case .webex:
return webexNumber
case .zoom:
return zoomNumber



///Returns the iso code for the current Settings number of a given provider, returns nil if no settings number is set.
static func isoCodeForSettingsNumber(with provider: ConferenceProvider) -> String?
switch provider
case .unknown:
return nil
case .att:
return attISO
case .arkadinMeetings:
return arkadinMeetingsISO
case .webex:
return webexISO
case .zoom:
return zoomISO



/**
Returns the participant codes for a given provider

- Note: Returns an empty array for arkadinMeetings as we don't store these participant codes.
- Note: For unknown providers this returns the participant codes for all meetings other than AT&T
as the user is unlikely to ever dial in with their own AT&T participant code (they'd use their host code).
*/
static func participantCodes(for provider: ConferenceProvider) -> [String]
switch provider
case .unknown:
return [webexAccessCode, zoomMeetingId].flatMap$0
case .att:
return attParticipantCodes
case .arkadinMeetings:
return
case .webex:
return [webexAccessCode].flatMap$0
case .zoom:
return [zoomMeetingId].flatMap$0



/**
Returns the host codes for a given provider

- Note: Returns an empty array for zoom conference calls as they have no host codes.
- Note: For unknown providers this returns all hostcodes
*/
static func hostCodes(for provider: ConferenceProvider) -> [String]
switch provider
case .unknown:
return attHostCodes + [arkadinMeetingsModeratorCode ,webexHostPin].flatMap$0
case .att:
return attHostCodes
case .arkadinMeetings:
return [arkadinMeetingsModeratorCode].flatMap$0
case .webex:
return [webexHostPin].flatMap$0
case .zoom:
return









share|improve this question













I'm developing an app that joins tele-conference calls so we use conference numbers a lot. I've got this basic structure to encapsulate the information regarding a conference number.



///A model that represents a teleconference number.
struct ConferenceNumber

let iso : String ///Interntational Standardised 2 letter Abbreviation for country.
let conferenceProvider: ConferenceProvider ///The company that hosts the conference call the number is for.
let number : String ///The phone number
let toll : Bool ///Whether the number is a paid or free line.
let country: String

init(ISO: String, country: String, number:String, provider: ConferenceProvider, toll: Bool)
self.iso = ISO
self.country = ConferenceNumber.localeName(from: ISO) ?? country
self.number = number
self.conferenceProvider = provider
self.toll = toll




I'm running in to two problems:



  1. This class if often bypassed in favour of storing the numbers in [String] arrays. I can't think of a nice way to put this in to an object as ConferenceNumber.number doesn't sound right.


  2. The conferenceProvider property has resulted in over a dozen switch statement in our code base which all look similar to the code below. Is it possible to reduce repeating myself this often with protocols or subclasses?


ConferenceProvider enum



///Defines the type of conference call service the meeting uses if any.
public enum ConferenceProvider : String
case unknown = "",
zoom = "Zoom",
att = "AT&T",
webex = "WebEx",
arkadin = "Arkadin"



Class with ConferenceProvider switch statements example



import Foundation

///Builds, stores, retrieves and queries conference number directories.
class ConferenceNumberDirectory

static let att: [ConferenceNumber] =
return buildDirectory(for: .att, from: jsonArray)
()

static let arkadinMeetings: [ConferenceNumber] =
return buildDirectory(for: .arkadinMeetings, from: jsonArray)
()

static let webex: [ConferenceNumber] =
return buildDirectory(for: .webex, from: jsonArray)
()

static let zoom: [ConferenceNumber] =
return buildDirectory(for: .zoom, from: jsonArray)
()

static let allProviders: [ConferenceNumber] =
return att + arkadinMeetings + webex + zoom
()

static let attPhoneNumbers: [String] =
return ConferenceNumberDirectory.att.map( $0.number )
()

static let arkadinMeetingsPhoneNumbers: [String] =
return ConferenceNumberDirectory.arkadinMeetings.map( $0.number )
()

static let webexPhoneNumbers: [String] =
return ConferenceNumberDirectory.webex.map( $0.number )
()

static let zoomPhoneNumbers: [String] =
return ConferenceNumberDirectory.zoom.map( $0.number )
()

static let allPhoneNumber: [String] =
return ConferenceNumberDirectory.allProviders.map $0.number
()

private static let jsonArray: [Any]? =
guard let numbersFilePath = Bundle.main.path(forResource: "numbers", ofType:"json") else
return nil


let data = try! Data(contentsOf: URL(fileURLWithPath:numbersFilePath), options: .uncached)

return try! JSONSerialization.jsonObject(with: data) as? [Any]
()

///Takes the outputted [Any]? from a JSON file and builds an Array of ConferenceNumber objects suitable for Call In to use.
static func buildDirectory(for conferenceProvider: ConferenceProvider, from jsonArray: [Any]?) -> [ConferenceNumber]
var directory = [ConferenceNumber]()
do
guard let rootJSONArray = jsonArray else
throw SerializationError.missing("JSON Root")


for entry in rootJSONArray
guard let dictionary = entry as? [String: Any] else
throw SerializationError.missing("JSON Root")

guard let isoCode = dictionary["ISO Code"] as? String else
throw SerializationError.missing("isoCode")

guard let country = dictionary["Country"] as? String else
throw SerializationError.missing("country")


if let key = getTollKey(for: conferenceProvider), let tollNumbers = try self.extractNumbers(from: dictionary, forKey: key)
directory.append(contentsOf: tollNumbers.mapConferenceNumber(ISO: isoCode, country: country, number: $0, provider: conferenceProvider, toll: true) )

if let key = getTollFreeKey(for: conferenceProvider), let tollFreeNumbers = try self.extractNumbers(from: dictionary, forKey: key)
directory.append(contentsOf: tollFreeNumbers.mapConferenceNumber(ISO: isoCode, country: country, number: $0, provider: conferenceProvider, toll: false))



catch let error
print(error)

return directory


///Returns the directory for a given conference provider. Returns all if no numbers exist for this provider.
static func getDirectory(for conferenceProvider: ConferenceProvider) -> [ConferenceNumber]?
switch conferenceProvider
case .arkadinMeetings:
return ConferenceNumberDirectory.arkadinMeetings
case .att:
return ConferenceNumberDirectory.att
case .webex:
return ConferenceNumberDirectory.webex
case .zoom:
return ConferenceNumberDirectory.zoom
default:
return ConferenceNumberDirectory.att + ConferenceNumberDirectory.arkadinMeetings + ConferenceNumberDirectory.webex + ConferenceNumberDirectory.zoom



/**
Returns a ConferenceNumber that matches the phone number and the country ISO code provided. Returns nil if no number is found.
- parameter number: The phone number to try and find a match for
- parameter isoCode: The international standard organizations 2 digit code that represents a country for all known languages

At present we do not store ConferenceNumber Objects in the settings so we often need to convert the Strings we store back in to their original conference number. Where we to save the object we would still need this as user entered numbers will not contain all ConferenceNumber properties.
*/
static func findConferenceNumber(usingNumber number: String, andISOCode isoCode: String?, fromDirectory conferenceProvider: ConferenceProvider = .unknown) -> ConferenceNumber?

if number.isEmpty


/**
Returns all conference numbers that share the same number.
Similarly as the previous one, findConferenceNumber() but getting all possibilities because we know that some countries share the same number, example: 8884266840
*/
static func findConferenceNumbers(usingNumber number: String, fromDirectory conferenceProvider: ConferenceProvider? = .unknown) -> [ConferenceNumber]

if number.isEmpty
return


switch conferenceProvider
case .att?:
return ConferenceNumberDirectory.att.filter( $0.number == number)
case .arkadinMeetings?:
return ConferenceNumberDirectory.arkadinMeetings.filter($0.number == number)
case .webex?:
return ConferenceNumberDirectory.webex.filter( $0.number == number)
case .zoom?:
return ConferenceNumberDirectory.zoom.filter($0.number == number)
default:
let allConferenceNumbers = ConferenceNumberDirectory.att + ConferenceNumberDirectory.arkadinMeetings + ConferenceNumberDirectory.webex + ConferenceNumberDirectory.zoom
return allConferenceNumbers.filter($0.number == number)



///Find the provider for a given number, return unknown if the number does not exist
///- warning: This will return the first provider that has the current number seeing as providers don't share numbers.
static func findProvider(forNumber queriedNumber: String) -> ConferenceProvider
return ConferenceNumberDirectory.allProviders.first $0.number == queriedNumber ?.conferenceProvider ?? .unknown


///Returns an array of all country iso codes a queriedNumber is used by.
static func findCountryISOCodes(forNumber queriedNumber: String) -> [String]
return ConferenceNumberDirectory.allProviders.filter $0.number == queriedNumber .map( $0.iso )



///Extracts telephone numbers from a given dictionary and key by taking the value as a string then returning an array of each comma seperated component of that string.
private static func extractNumbers(from dictionary: [String: Any], forKey key: String?) throws -> [String]?
guard let jsonKey = key ,let tollNumbersString = dictionary[jsonKey] as? String else
throw SerializationError.missing(key ?? "key is nil")

if tollNumbersString.isEmpty
return nil

return tollNumbersString.components(separatedBy: ",")


///Returns the JSON key to access a given ConferenceProviders toll numbers.
private static func getTollKey(for provider: ConferenceProvider) -> String?
switch provider
case .att:
return "AT&T toll"
case .arkadinMeetings:
return "arkadin Meetings toll"
case .webex:
return "Webex toll"
case .zoom:
return "Zoom toll"
default:
return nil



///Returns the JSON key to access a given ConferenceProviders toll-free numbers.
private static func getTollFreeKey(for provider: ConferenceProvider) -> String?
switch provider
case .att:
return "AT&T toll-free"
case .arkadinMeetings:
return "arkadin Meetings toll-free"
case .webex:
return "Webex toll-free"
default:
return nil





Class with ConferenceProvider switch statements Example 2



/// Data class, responsible for storing and retrieving of user settings.

import Foundation

class Settings
/// init user defaults with the app group suite to be able to use the same values in the app and in the widget
static let defaults = SharedGlobals.appGroupDefaults

static var id: String?
get return self.defaults.string(forKey: "id")
set self.defaults.set(newValue, forKey: "id")


static var attNumber: String?
get return self.defaults.string(forKey: "attNumber")
set self.defaults.set(newValue, forKey: "attNumber")


static var attCountry: String?
get return self.defaults.string(forKey: "attCountry")
set self.defaults.set(newValue, forKey: "attCountry")


static var attISO: String?
get return self.defaults.string(forKey: "attISO")
set self.defaults.set(newValue, forKey: "attISO")


static var attHostCodes: [String]
get if let codes = self.defaults.stringArray(forKey: "hostCodes")
return codes
else
return


set self.defaults.set(newValue, forKey: "hostCodes")


///AT&T participant codes, these are paired to hostcodes (by index) so we can determine which host code to use via parsed participant codes.
static var attParticipantCodes: [String]
get if let codes = self.defaults.stringArray(forKey: "participantCodes")
return codes
else
return


set self.defaults.set(newValue, forKey: "participantCodes")


///arkadinMeetings (Smartcloud)
static var arkadinMeetingsCountry: String?
get return self.defaults.string(forKey: "arkadinMeetingsCountry")
set self.defaults.set(newValue, forKey: "arkadinMeetingsCountry")



static var arkadinMeetingsNumber: String?
get return self.defaults.string(forKey: "arkadinMeetingsNumber")
set self.defaults.set(newValue, forKey: "arkadinMeetingsNumber")


static var arkadinMeetingsISO: String?
get return self.defaults.string(forKey: "arkadinMeetingsISO")
set self.defaults.set(newValue, forKey: "arkadinMeetingsISO")



static var arkadinMeetingsModeratorCode: String?
get return self.defaults.string(forKey: "arkadinMeetingsModeratorCode")
set self.defaults.set(newValue, forKey: "arkadinMeetingsModeratorCode")



///WEBEX
static var webexNumber: String?
get return self.defaults.string(forKey: "webexNumber")
set self.defaults.set(newValue, forKey: "webexNumber")


static var webexCountry: String?
get return self.defaults.string(forKey: "webexCountry")
set self.defaults.set(newValue, forKey: "webexCountry")


static var webexISO: String?
get return self.defaults.string(forKey: "webexISO")
set self.defaults.set(newValue, forKey: "webexISO")


//Webex participant code, this is stored as the participant code is required to dial in even when the user is hosting.
static var webexAccessCode: String?
get return self.defaults.string(forKey: "webexAccessCode")
set self.defaults.set(newValue, forKey: "webexAccessCode")


static var webexHostPin: String?
get return self.defaults.string(forKey: "webexHostPin")
set self.defaults.set(newValue, forKey: "webexHostPin")



///ZOOM
static var zoomNumber: String?
get return self.defaults.string(forKey: "zoomNumber")
set self.defaults.set(newValue, forKey: "zoomNumber")


static var zoomCountry: String?
get return self.defaults.string(forKey: "zoomCountry")
set self.defaults.set(newValue, forKey: "zoomCountry")


static var zoomISO: String?
get return self.defaults.string(forKey: "zoomISO")
set self.defaults.set(newValue, forKey: "zoomISO")


static var zoomMeetingId: String?
get return self.defaults.string(forKey: "zoomMeetingId")
set self.defaults.set(newValue, forKey: "zoomMeetingId")



// MARK: User

///Country
static var country: String?
get return self.defaults.string(forKey: "country")
set self.defaults.set(newValue, forKey: "country")


static var email: String?
get return self.defaults.string(forKey: "email")
set self.defaults.set(newValue, forKey: "email")


///Return the user's name. If this hasn't been saved extract their name from their arkadin calendar and save it.
static var usersName: String?

if let savedName = defaults.string(forKey: "name")
return savedName


if let calendar = MeetingsFetcher().getarkadinCalendar().first,
let parsedName = calendar.title.parseFirstMatch(from: Regex.organizerNameFromEKCalendar)

defaults.set(parsedName, forKey: "name")
return parsedName

return AppConfigInteractor.getUserName()


static var callMethod: String
get
if let method = self.defaults.string(forKey: "callMethod")
return method
else
return CallMethod.methods.device.rawValue


set self.defaults.set(newValue, forKey: "callMethod")


///VIPS: email + relation for a max of 9 VIPs
static var VIPemails: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPemails")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPemails")


static var VIPnames: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPnames")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPnames")


static var VIPrelations: [String]
get if let vips = self.defaults.stringArray(forKey: "VIPrelations")
return vips
else
return


set self.defaults.set(newValue, forKey: "VIPrelations")



///notification
static var notificationsAllowed: Bool
get return self.defaults.bool(forKey: "notification")
set self.defaults.set(newValue, forKey: "notification")


///location awareness
static var locationAwarenessAllowed: Bool
get return self.defaults.bool(forKey: "locationAwareness")
set self.defaults.set(newValue, forKey: "locationAwareness")


///promt for authentication awareness
static var promptForAuthentication: Bool
get return self.defaults.bool(forKey: "promptForAuthentication")
set self.defaults.set(newValue, forKey: "promptForAuthentication")


///last time the user was prompt for authentication
static var lastAuthenticationPromptDate: Date?
get return self.defaults.object(forKey: "lastAuthenticationPromptDate") as? Date
set self.defaults.set(newValue, forKey: "lastAuthenticationPromptDate")


///audiosummary
static var audioSummary: Bool
get return self.defaults.bool(forKey: "audioSummary")
set self.defaults.set(newValue, forKey: "audioSummary")



/// Local settings (not stored in the cloud)

static var notifyNumberOfMinutesBeforeEvent: Double
get return self.defaults.double(forKey: "notifyNumberOfMinutesBeforeEvent")
set self.defaults.set(newValue, forKey: "notifyNumberOfMinutesBeforeEvent")


static var landingPageWasReadToday: Bool
get return self.defaults.bool(forKey: "landingPageWasReadToday")
set self.defaults.set(newValue, forKey: "landingPageWasReadToday")


static var lastDayAudioWasRead: Int
get return self.defaults.integer(forKey: "lastDayAudioWasRead")
set self.defaults.set(newValue, forKey: "lastDayAudioWasRead")


static var lastDayPopUpWasDismissed: Int
get return self.defaults.integer(forKey: "lastDayPopUpWasDismissed")
set self.defaults.set(newValue, forKey: "lastDayPopUpWasDismissed")


static var meetingsDismissed: [String]
get if let meetings = self.defaults.stringArray(forKey: "meetingsDismissed")
return meetings
else
return


set self.defaults.set(newValue, forKey: "meetingsDismissed")


static var promptToModifyNumber: Bool
get return self.defaults.bool(forKey: "promptToModifyNumber")
set self.defaults.set(newValue, forKey: "promptToModifyNumber")


///returns true if the user has already gone though the onboarding process
static var didOnboarding: Bool
get return self.defaults.bool(forKey: "didOnboarding")
set self.defaults.set(newValue, forKey: "didOnboarding")


static var advancedParticipantCode: String
get
if let code = self.defaults.string(forKey:"advancedParticipantCode")
return code
else
return SharedGlobals.Call.DEFAULT_PARTICIPANT_CODE


set self.defaults.set(newValue, forKey: "advancedParticipantCode")


static var advancedHostCode: String
get
if let code = self.defaults.string(forKey:"advancedHostCode")
return code
else
return SharedGlobals.Call.DEFAULT_HOST_CODE


set self.defaults.set(newValue, forKey: "advancedHostCode")


static var advancedNoCode: String
get
if let code = self.defaults.string(forKey: "advancedNoCode")
return code
else
return SharedGlobals.Call.DEFAULT_NO_CODE


set self.defaults.set(newValue, forKey: "advancedNoCode")


static var versionID: String?
get return self.defaults.string(forKey: "versionID")
set self.defaults.set(newValue, forKey: "versionID")


static var buildID: String?
get return self.defaults.string(forKey:"buildID")
set self.defaults.set(newValue, forKey: "buildID")


///always use conference number and ignore number in invitation
static var prioritizeSettingsNumbers: Bool
get return self.defaults.bool(forKey: "prioritizeSettingsNumbers")
set self.defaults.set(newValue, forKey: "prioritizeSettingsNumbers")


///Returns the settings number for a given provider or the AT&T number if provider is unknown.
static func phoneNumber(for provider: ConferenceProvider) -> String?
switch provider
case .unknown:
return attNumber ?? webexNumber ?? arkadinMeetingsNumber ?? zoomNumber
case .att:
return attNumber
case .arkadinMeetings:
return arkadinMeetingsNumber
case .webex:
return webexNumber
case .zoom:
return zoomNumber



///Returns the iso code for the current Settings number of a given provider, returns nil if no settings number is set.
static func isoCodeForSettingsNumber(with provider: ConferenceProvider) -> String?
switch provider
case .unknown:
return nil
case .att:
return attISO
case .arkadinMeetings:
return arkadinMeetingsISO
case .webex:
return webexISO
case .zoom:
return zoomISO



/**
Returns the participant codes for a given provider

- Note: Returns an empty array for arkadinMeetings as we don't store these participant codes.
- Note: For unknown providers this returns the participant codes for all meetings other than AT&T
as the user is unlikely to ever dial in with their own AT&T participant code (they'd use their host code).
*/
static func participantCodes(for provider: ConferenceProvider) -> [String]
switch provider
case .unknown:
return [webexAccessCode, zoomMeetingId].flatMap$0
case .att:
return attParticipantCodes
case .arkadinMeetings:
return
case .webex:
return [webexAccessCode].flatMap$0
case .zoom:
return [zoomMeetingId].flatMap$0



/**
Returns the host codes for a given provider

- Note: Returns an empty array for zoom conference calls as they have no host codes.
- Note: For unknown providers this returns all hostcodes
*/
static func hostCodes(for provider: ConferenceProvider) -> [String]
switch provider
case .unknown:
return attHostCodes + [arkadinMeetingsModeratorCode ,webexHostPin].flatMap$0
case .att:
return attHostCodes
case .arkadinMeetings:
return [arkadinMeetingsModeratorCode].flatMap$0
case .webex:
return [webexHostPin].flatMap$0
case .zoom:
return











share|improve this question












share|improve this question




share|improve this question








edited Mar 5 at 14:35
























asked Mar 3 at 21:55









Deco

11310




11310











  • What is the significance of knowing the provider? Why would a user care if AT&T provider the conferencing vs. IBM?
    – user1118321
    Mar 4 at 21:34










  • @user1118321 We store phone numbers for these different providers which we will want to put in to the meeting if we detect text or URLs specific to that provider. The user can also store settings for each providers where they can select or put their own number in as well as add participant codes/ host codes for that number. Different providers also use different dial codes.
    – Deco
    Mar 5 at 14:38






  • 1




    let number : String ///The phone number Imagine if you just named the variable let phoneNumber: String. You wouldn't need a comment. Same with toll -> isTollFree (although that has the inverted meaning)
    – Alexander
    Mar 21 at 18:09
















  • What is the significance of knowing the provider? Why would a user care if AT&T provider the conferencing vs. IBM?
    – user1118321
    Mar 4 at 21:34










  • @user1118321 We store phone numbers for these different providers which we will want to put in to the meeting if we detect text or URLs specific to that provider. The user can also store settings for each providers where they can select or put their own number in as well as add participant codes/ host codes for that number. Different providers also use different dial codes.
    – Deco
    Mar 5 at 14:38






  • 1




    let number : String ///The phone number Imagine if you just named the variable let phoneNumber: String. You wouldn't need a comment. Same with toll -> isTollFree (although that has the inverted meaning)
    – Alexander
    Mar 21 at 18:09















What is the significance of knowing the provider? Why would a user care if AT&T provider the conferencing vs. IBM?
– user1118321
Mar 4 at 21:34




What is the significance of knowing the provider? Why would a user care if AT&T provider the conferencing vs. IBM?
– user1118321
Mar 4 at 21:34












@user1118321 We store phone numbers for these different providers which we will want to put in to the meeting if we detect text or URLs specific to that provider. The user can also store settings for each providers where they can select or put their own number in as well as add participant codes/ host codes for that number. Different providers also use different dial codes.
– Deco
Mar 5 at 14:38




@user1118321 We store phone numbers for these different providers which we will want to put in to the meeting if we detect text or URLs specific to that provider. The user can also store settings for each providers where they can select or put their own number in as well as add participant codes/ host codes for that number. Different providers also use different dial codes.
– Deco
Mar 5 at 14:38




1




1




let number : String ///The phone number Imagine if you just named the variable let phoneNumber: String. You wouldn't need a comment. Same with toll -> isTollFree (although that has the inverted meaning)
– Alexander
Mar 21 at 18:09




let number : String ///The phone number Imagine if you just named the variable let phoneNumber: String. You wouldn't need a comment. Same with toll -> isTollFree (although that has the inverted meaning)
– Alexander
Mar 21 at 18:09










1 Answer
1






active

oldest

votes

















up vote
0
down vote



accepted










I think you've used the wrong abstraction here. I think that's the cause of all these switch statements.



Use Objects or structs



One thing I notice is that it seems like a ConferenceProvider should be an object, not an enum. If it were an object you could hold the provider's name, a link to the ConferenceNumberDirectory entry for that provider, the toll key and the toll free key. Then most of your switch statements in the first example go away, and it just becomes extracting the information from the passed-in provider. It could look something like this:



struct ConferenceProvider 
let providerName : String
let providerNumbers : [ConferenceNumber]
let tollKey : String
let tollFreeKey : String

init(providerName: String, providerNumbers: [ConferenceNumber], tollKey: String, tollFreeKey: String)
self.providerName = providerName
self.providerNumbers = providerNumbers
self.tollKey = tollKey
self.tollFreeKey = tollFreeKey




You'd then have to change getDirectory(for conferenceProvider: ) to do the lookup by name, or some other method. You'd probably want a Dictionary that mapped provider name to the above ConferenceProvider struct.






share|improve this answer





















  • How would we apply this to something like number identification? At present we look for URLs and keywords then use something like meeting.conferenceProvider = .att once we identify the meeting uses AT&T teleconference calls.
    – Deco
    Mar 6 at 13:40










  • You would still have that information in the struct I mention above. It would just be meeting.conferenceProvider.providerName. Or if you wanted it to still be an enum I suppose it could be an enum instead of a String.
    – user1118321
    Mar 6 at 17:07










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188752%2fclasses-for-representing-teleconferencing-numbers%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
0
down vote



accepted










I think you've used the wrong abstraction here. I think that's the cause of all these switch statements.



Use Objects or structs



One thing I notice is that it seems like a ConferenceProvider should be an object, not an enum. If it were an object you could hold the provider's name, a link to the ConferenceNumberDirectory entry for that provider, the toll key and the toll free key. Then most of your switch statements in the first example go away, and it just becomes extracting the information from the passed-in provider. It could look something like this:



struct ConferenceProvider 
let providerName : String
let providerNumbers : [ConferenceNumber]
let tollKey : String
let tollFreeKey : String

init(providerName: String, providerNumbers: [ConferenceNumber], tollKey: String, tollFreeKey: String)
self.providerName = providerName
self.providerNumbers = providerNumbers
self.tollKey = tollKey
self.tollFreeKey = tollFreeKey




You'd then have to change getDirectory(for conferenceProvider: ) to do the lookup by name, or some other method. You'd probably want a Dictionary that mapped provider name to the above ConferenceProvider struct.






share|improve this answer





















  • How would we apply this to something like number identification? At present we look for URLs and keywords then use something like meeting.conferenceProvider = .att once we identify the meeting uses AT&T teleconference calls.
    – Deco
    Mar 6 at 13:40










  • You would still have that information in the struct I mention above. It would just be meeting.conferenceProvider.providerName. Or if you wanted it to still be an enum I suppose it could be an enum instead of a String.
    – user1118321
    Mar 6 at 17:07














up vote
0
down vote



accepted










I think you've used the wrong abstraction here. I think that's the cause of all these switch statements.



Use Objects or structs



One thing I notice is that it seems like a ConferenceProvider should be an object, not an enum. If it were an object you could hold the provider's name, a link to the ConferenceNumberDirectory entry for that provider, the toll key and the toll free key. Then most of your switch statements in the first example go away, and it just becomes extracting the information from the passed-in provider. It could look something like this:



struct ConferenceProvider 
let providerName : String
let providerNumbers : [ConferenceNumber]
let tollKey : String
let tollFreeKey : String

init(providerName: String, providerNumbers: [ConferenceNumber], tollKey: String, tollFreeKey: String)
self.providerName = providerName
self.providerNumbers = providerNumbers
self.tollKey = tollKey
self.tollFreeKey = tollFreeKey




You'd then have to change getDirectory(for conferenceProvider: ) to do the lookup by name, or some other method. You'd probably want a Dictionary that mapped provider name to the above ConferenceProvider struct.






share|improve this answer





















  • How would we apply this to something like number identification? At present we look for URLs and keywords then use something like meeting.conferenceProvider = .att once we identify the meeting uses AT&T teleconference calls.
    – Deco
    Mar 6 at 13:40










  • You would still have that information in the struct I mention above. It would just be meeting.conferenceProvider.providerName. Or if you wanted it to still be an enum I suppose it could be an enum instead of a String.
    – user1118321
    Mar 6 at 17:07












up vote
0
down vote



accepted







up vote
0
down vote



accepted






I think you've used the wrong abstraction here. I think that's the cause of all these switch statements.



Use Objects or structs



One thing I notice is that it seems like a ConferenceProvider should be an object, not an enum. If it were an object you could hold the provider's name, a link to the ConferenceNumberDirectory entry for that provider, the toll key and the toll free key. Then most of your switch statements in the first example go away, and it just becomes extracting the information from the passed-in provider. It could look something like this:



struct ConferenceProvider 
let providerName : String
let providerNumbers : [ConferenceNumber]
let tollKey : String
let tollFreeKey : String

init(providerName: String, providerNumbers: [ConferenceNumber], tollKey: String, tollFreeKey: String)
self.providerName = providerName
self.providerNumbers = providerNumbers
self.tollKey = tollKey
self.tollFreeKey = tollFreeKey




You'd then have to change getDirectory(for conferenceProvider: ) to do the lookup by name, or some other method. You'd probably want a Dictionary that mapped provider name to the above ConferenceProvider struct.






share|improve this answer













I think you've used the wrong abstraction here. I think that's the cause of all these switch statements.



Use Objects or structs



One thing I notice is that it seems like a ConferenceProvider should be an object, not an enum. If it were an object you could hold the provider's name, a link to the ConferenceNumberDirectory entry for that provider, the toll key and the toll free key. Then most of your switch statements in the first example go away, and it just becomes extracting the information from the passed-in provider. It could look something like this:



struct ConferenceProvider 
let providerName : String
let providerNumbers : [ConferenceNumber]
let tollKey : String
let tollFreeKey : String

init(providerName: String, providerNumbers: [ConferenceNumber], tollKey: String, tollFreeKey: String)
self.providerName = providerName
self.providerNumbers = providerNumbers
self.tollKey = tollKey
self.tollFreeKey = tollFreeKey




You'd then have to change getDirectory(for conferenceProvider: ) to do the lookup by name, or some other method. You'd probably want a Dictionary that mapped provider name to the above ConferenceProvider struct.







share|improve this answer













share|improve this answer



share|improve this answer











answered Mar 6 at 5:11









user1118321

10.2k11144




10.2k11144











  • How would we apply this to something like number identification? At present we look for URLs and keywords then use something like meeting.conferenceProvider = .att once we identify the meeting uses AT&T teleconference calls.
    – Deco
    Mar 6 at 13:40










  • You would still have that information in the struct I mention above. It would just be meeting.conferenceProvider.providerName. Or if you wanted it to still be an enum I suppose it could be an enum instead of a String.
    – user1118321
    Mar 6 at 17:07
















  • How would we apply this to something like number identification? At present we look for URLs and keywords then use something like meeting.conferenceProvider = .att once we identify the meeting uses AT&T teleconference calls.
    – Deco
    Mar 6 at 13:40










  • You would still have that information in the struct I mention above. It would just be meeting.conferenceProvider.providerName. Or if you wanted it to still be an enum I suppose it could be an enum instead of a String.
    – user1118321
    Mar 6 at 17:07















How would we apply this to something like number identification? At present we look for URLs and keywords then use something like meeting.conferenceProvider = .att once we identify the meeting uses AT&T teleconference calls.
– Deco
Mar 6 at 13:40




How would we apply this to something like number identification? At present we look for URLs and keywords then use something like meeting.conferenceProvider = .att once we identify the meeting uses AT&T teleconference calls.
– Deco
Mar 6 at 13:40












You would still have that information in the struct I mention above. It would just be meeting.conferenceProvider.providerName. Or if you wanted it to still be an enum I suppose it could be an enum instead of a String.
– user1118321
Mar 6 at 17:07




You would still have that information in the struct I mention above. It would just be meeting.conferenceProvider.providerName. Or if you wanted it to still be an enum I suppose it could be an enum instead of a String.
– user1118321
Mar 6 at 17:07












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188752%2fclasses-for-representing-teleconferencing-numbers%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Python Lists

Aion

JavaScript Array Iteration Methods