We'll tie together a variety of Swift language techniques, giving you additional practice to create structs, functions, data types, and optionals, and use flow control structures.

To solidify your understanding of basic struct and class usage by practicing the creation of each type of object.

  1. Launch Xcode, and create a new playground, then save it to your desktop with the name Activity 5 - Final Activity.playground.
  2. Add the following enum, which will be used to classify customers by gold, silver, and platinum levels. Note that this enum has a rawValue of type String, which we will use while printing customer information:
    enum CustomerType:String {
        case silver = "SILVER"
        case gold = "GOLD"
        case platinum = "PLATINUM"
    }
  3. Create a new Customer struct with a set of String variables, including an optional for country and the variable type to classify the customer into one of the CustomerType categories:
    struct Customer {
        var name: String
        var address: String
        var city: String
        var state: String
        var country: String?
        var type: CustomerType
    }
  4. Within the Customer struct, add an enum OutputType to control customer printing output style as either a formatted label, a debug output, or both. This enum has no rawValue:
    enum OutputType {
       case label, debug, both
    }
  5. Add a function printAddress to the Customer struct that can be called to print customer address information in a variety of styles. This function returns a result, but includes the @discardableResult annotation so that callers who do not store its return value won't generate a compiler warning. This function also allows (but does not require) additional text lines to be appended to the end of the address label output via a variadic parameter:
    @discardableResult func printAddress(outputType: OutputType = .label, additionalLines: String?...) -> OutputType {
    
        switch outputType {
            case .both:
                printDebug()
                fallthrough
            case .label:
                printLabel(additionalLines)
            case .debug:
                printDebug()
         }
    
        return outputType
    }
  6. Add a function printLabel to the Customer struct that creates a formatted string and prints it to the console. Note that this function is declared as private so that it can be called only from other functions in the Customer class (forcing callers to go through the printAddress function to print label data). This function also accepts an array of optional strings:
    private func printLabel(_ additionalLines: [String?]) {
        var addressString = """
            \(type.rawValue)
            \(name)
            \(address)
            \(city), \(state)
            """
    
        if let countryText = country {
            addressString += "\n\(countryText)"
        }
        for line in additionalLines {
            if let line = line {
                // "line" and "line" have the same name, but exist in different scopes.
                // The inner 'line' variable is a non-Optional, scoped within this block,
                //and is created only when the Optional 'line' variable created by the for statement is not nil
                addressString += "\n\(line)"
            }
        }
    
        print(addressString)
    }
  7. Add a function printDebug to the Customer class to print a simple output string to the console. This function will be called when the printAddress function is called with either the .debug or .both style parameters:
    private func printDebug() {
        print(self)
    }
  8. Add a function customerTuple to return customer information as a tuple containing six unnamed members:
    func customerTuple() -> (String, String, String, String, String?, String) {
        return (name, address, city, state, country, type.rawValue)
    }
  9. Now create two Customer objects, customer1 and customer2, with different address information:
    let customer1 = Customer(name: "John Doe", address: "100 First Street", city: "Springfield", state: "Indiana", country: "USA", type: .platinum)
    
    let customer2 = Customer(name: "Jane Doe", address: "57 Morgan Circle", city: "Las Vegas", state: "Nevada", country: "USA", type: .silver)
  10. Create a constant variable tuple, and assign it the return of the customerTuple function:
    let tuple = customer1.customerTuple()
  11. Print the first and third members of the tuple (customer name and address):
    print("Customer named ", tuple.0, " lives in ", tuple.2)
  12. Call the printAddress function on the customer2 object, directing the function to print a formatted label with two additional lines under the address:
    customer2.printAddress(outputType: .label, additionalLines: "C/O Sam Johnson", "Forwarding Requested")
  13. Call the printAddress function on the customer2 object, this time passing the .debug style parameter, and no additional lines:
    customer2.printAddress(outputType: .debug)
  14. Finally, call the printAddress function on the customer1 object, this time passing the .both style parameter. The printAddress function's switch statement will use the fallthrough instruction to print both versions of the address output:
    customer1.printAddress(outputType: .both)