App-iOS/EndpointPicker.swift
/* |
Copyright (C) 2018 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Runs the main view, letting the user configure an endpoint to connect to. |
*/ |
import UIKit |
/// Runs the main view, letting the user configure an endpoint to connect to. |
/// This no nothing about networking as such, it’s just a view that lets you |
/// input some networking parameters. The delegate is responsible for checking |
/// those parameters and actually starting a conversation based on them. |
class EndpointPicker : UITableViewController, UITextFieldDelegate { |
typealias Delegate = EndpointPickerDelegate |
weak var delegate: Delegate? = nil |
/// Set this before presenting the view controller to configure the initial display. |
/// Read this in delegate callbacks to get the current configuration. |
var endpoint: Endpoint = .empty |
// MARK: - View Controller Methods |
override func viewDidLoad() { |
super.viewDidLoad() |
self.hostNameField.text = self.endpoint.hostName |
self.portField.text = self.portNumberFormatter.string(from: self.endpoint.port as NSNumber) |
self.useTLSSwitch = UISwitch() |
self.useTLSSwitch.addTarget(self, action: #selector(didChange(useTLSSwitch:)), for: .valueChanged) |
self.useTLSCell.accessoryView = self.useTLSSwitch |
self.connectTextColor = self.connectCell.textLabel!.textColor! |
self.enableConnectCell() |
self.cell(for: self.endpoint.transport).accessoryType = .checkmark |
} |
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { |
if segue.identifier == "startConnection" { |
self.delegate?.prepareStartConnection(segue: segue, endpointPicker: self) |
} else { |
super.prepare(for: segue, sender: sender) |
} |
} |
// MARK: - Table View Controller Callbacks |
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { |
// Deselect by default. |
var shouldDeselect = true |
defer { |
tableView.deselectRow(at: indexPath, animated: true) |
} |
switch Section(rawValue: indexPath.section)! { |
case .general: |
if indexPath == tableView.indexPath(for: self.connectCell) && self.connectCell.selectionStyle == .default { |
if let message = self.delegate?.check(endpointPicker: self) { |
self.displayConfigurationUnsupportedAlert(message: message) |
} else { |
self.performSegue(withIdentifier: "startConnection", sender: self) |
// The table view deselects as we come back on screen. |
shouldDeselect = false |
} |
} |
case .transports: |
self.cell(for: self.endpoint.transport).accessoryType = .none |
self.endpoint.transport = Endpoint.Transport(rawValue: indexPath.row)! |
self.cell(for: self.endpoint.transport).accessoryType = .checkmark |
} |
} |
// MARK: - Utilities |
/// An enumeration that describes the layout of the table view. |
/// |
/// - general: The first section holds our general UI. Item in this section |
/// are accessed via various IBOutlets. |
/// - transports: The second section holds our transports. Items in this |
/// section are accessed by using the raw value of the `Transport` |
/// enumeration to index the `transportCells` IBOutletCollection. |
private enum Section : Int { |
case general |
case transports |
} |
/// Used for rendering and parsing port numbers. |
private var portNumberFormatter: NumberFormatter = { |
let nf = NumberFormatter() |
nf.numberStyle = .decimal |
nf.usesGroupingSeparator = false |
return nf |
}() |
/// Returns the cell for a given transport. |
private func cell(for transport: Endpoint.Transport) -> UITableViewCell { |
return self.transportCells[transport.rawValue] |
} |
/// Called when the UI changes to enable or disable the Connect cell. |
private func enableConnectCell() { |
let isEnabled = !self.endpoint.hostName.isEmpty && (1...65535).contains(self.endpoint.port) |
self.connectCell.selectionStyle = isEnabled ? .default : .none |
self.connectCell.textLabel!.textColor = isEnabled ? self.connectTextColor : .gray |
} |
/// Displays a "Configuration Unsupported" alert with the supplied message. |
private func displayConfigurationUnsupportedAlert(message: String) { |
let alert = UIAlertController( |
title: NSLocalizedString("_Unsupported_Configuration_", comment: "Title for alert view."), |
message: message, |
preferredStyle: .alert |
) |
alert.addAction(UIAlertAction(title: NSLocalizedString("_OK_", comment: "Default button in alert view."), style: .default, handler: { (_) in |
self.dismiss(animated: true, completion: nil) |
})) |
self.present(alert, animated: true, completion: nil) |
} |
// MARK: - Outlets, Actions and Text Field Delegate Callbacks |
@IBAction private func didChange(hostNameField: UITextField) { |
let newText = (self.hostNameField.text ?? "").trimmingCharacters(in: .whitespacesAndNewlines) |
self.endpoint.hostName = newText |
self.enableConnectCell() |
} |
@IBAction private func didChange(portField: UITextField) { |
let newText = self.portField.text ?? "" |
let newPort = self.portNumberFormatter.number(from: newText)?.intValue ?? 0 |
self.endpoint.port = newPort |
self.enableConnectCell() |
} |
@IBAction private func didChange(useTLSSwitch: UISwitch) { |
self.endpoint.useTLS = self.useTLSSwitch.isOn |
} |
func textFieldShouldReturn(_ textField: UITextField) -> Bool { |
textField.resignFirstResponder() |
return true |
} |
@IBOutlet private var hostNameField: UITextField! |
@IBOutlet private var portField: UITextField! |
@IBOutlet private var useTLSCell: UITableViewCell! |
private var useTLSSwitch: UISwitch! |
@IBOutlet private var connectCell: UITableViewCell! |
private var connectTextColor: UIColor! |
@IBOutlet private var transportCells: [UITableViewCell]! |
} |
/// Delegate protocol for `EndpointPicker`. |
protocol EndpointPickerDelegate : AnyObject { |
/// Called to check whether a particular endpoint configuration is viable. |
/// |
/// - Parameters: |
/// - endpoint: The endpoint configuration in question. |
/// - endpointPicker: The sender. |
/// - Returns: `nil` if the endpoint is viable; a localised message to |
/// display otherwise. |
func check(endpointPicker: EndpointPicker) -> String? |
/// Called to prepare `startConnection` segue. |
/// |
/// - Parameters: |
/// - segue: The `segue`, which holds the destination view controller. |
/// - endpointPicker: The sender. |
func prepareStartConnection(segue: UIStoryboardSegue, endpointPicker: EndpointPicker) |
} |
Copyright © 2018 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2018-05-10