Skip to content

Instantly share code, notes, and snippets.

@zacwolff
Forked from mikesparr/Constants.swift
Created December 15, 2015 10:10
Show Gist options
  • Save zacwolff/f991895c5bbe25bccf26 to your computer and use it in GitHub Desktop.
Save zacwolff/f991895c5bbe25bccf26 to your computer and use it in GitHub Desktop.
Swift IOS Architecture Quick Start
//
// Constants.swift
// Goomzee
//
// Created by Michael Sparr on 11/11/15.
//
import Foundation
// MARK: - Environment
struct Environment
{
static let Development = "development"
static let Test = "test"
static let Stage = "stage"
static let Production = "production"
static let Default = "development"
}
// MARK: - Errors & Logging
struct LogLevel
{
static let Trace = "trace"
static let Debug = "debug"
static let Info = "info"
static let Warn = "warn"
static let Error = "error"
static let Critical = "critical"
}
enum InputError: ErrorType
{
case InputMissing
case PasswordMismatch
case InvalidEmail
...
}
enum APIError: ErrorType
{
case ConnectionLost
case RequestTimeout
...
}
enum GeneralError: ErrorType
{
case IncompleteFeature
case Unknown
}
// MARK: - Storyboard
struct Storyboard
{
// storyboard names
static let Main = "Main"
// storyboard IDs
static let WelcomeViewIdentifier = "welcomeViewController"
static let LoginViewIdentifier = "loginViewController"
static let RegisterViewIdentifier = "registerViewController"
static let ResetPasswordViewIdentifier = "resetPasswordViewController"
static let HomeViewIdentifier = "homeViewController"
...
// segues
static let ShowHomeIdentifier = "Show Home"
...
// collection views
...
// table views
static let BasicCellIdentifier = "Basic Cell" // generic cell (built-in)
...
// generic
static let EmptyString = ""
}
// MARK: - NSUserDefaults
struct Defaults
{
static let CurrentUserKey = "myAppUser" // NSUserDefaults object
...
}
// MARK: - Images & Files
struct Images
{
static let DefaultBackground = "DefaultBackground"
...
}
struct FileType
{
static let PNG = "image/png"
static let JPEG = "image/jpeg"
static let PJPEG = "image/pjpeg" // photoshop JPeg
static let GIF = "image/gif"
static let TIFF = "image/tiff"
static let BMP = "image/bmp" // bitmap
static let SVG = "image/svg"
...
}
let MimeType = FileType() // alias
// MARK: - Localization
struct Currency
{
static let USD = "USD"
...
}
struct Country
{
static let UnitedStates = "US" // ISO standard abbreviation
...
}
struct Language
{
static let EnglishUS = "en_US"
static let EnglishUK = "en_UK"
static let Spanish = "es_ES"
static let French = "fr_FR"
static let German = "de_DE"
static let Greek = "gr_GR"
static let Italian = "it_IT"
...
}
struct TimeZone
{
// TODO: determine time zone (name or GMT offsets)
}
// MARK: - AppData & Picklists (dynamic lists)
// MARK: - API
struct API
{
static let URLS = [
Environment.Development: "http://localhost:8085/api/v1",
Environment.Test: "https://dev.yourdomain.com/api/v1",
Environment.Stage: "https://stage.yourdomain.com/api/v1",
Environment.Production: "https://yourdomain.com/api/v1"
]
static let AllowedFileTypes = [
FileType.PNG,
FileType.JPEG,
FileType.PJPEG,
FileType.TIFF,
FileType.BMP,
FileType.SVG,
FileType.GIF,
FileType.SVG,
FileType.PDF,
FileType.WAV,
FileType.MOV
]
// constraints
static let MaxRequestWaitSeconds = 30 // seconds
static let MaxUploadSizeMegabytes = 25 // megabytes
static let MaxUploadFiles = 20 // file count
}
//
// Helpers.swift
// Goomzee
//
// Created by Michael Sparr on 11/22/15.
//
import Founation
import UIKit
class Helpers
{
// MARK: - UIAlertViewController (called from UIViewController)
/**
* Usage: Helpers.showOKAlert("Alert", message: "Something happened", target: self)
*/
static func showOKAlert(title: String, message: String, target: UIViewController)
{
let alertController: UIAlertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let okAction: UIAlertAction = UIAlertAction(title: Keys.ButtonOK.localized, style: .Default, handler: nil)
alertController.addAction(okAction)
target.presentViewController(alertController, animated: true, completion: nil)
}
/**
* Usage: Helpers.showOKHelpAlert("Notice", message: "Something happened.", target: self, handler: { (UIAlertAction) -> Void in
* // perform help option code here
* })
*/
static func showOKHelpAlert(title: String, message: String, target: UIViewController, handler: ((UIAlertAction) -> Void)?)
{
let alertController: UIAlertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let helpAction: UIAlertAction = UIAlertAction(title: Keys.ButtonHelp.localized, style: .Default, handler: handler)
let okAction: UIAlertAction = UIAlertAction(title: Keys.ButtonOK.localized, style: .Default, handler: nil)
alertController.addAction(helpAction)
alertController.addAction(okAction)
target.presentViewController(alertController, animated: true, completion: nil)
}
/**
* Usage: Helpers.showContinueAlert("Log Out", message: "Are you sure you want to log out?", target: self, handler: { (UIAlertAction) -> Void in
* // perform log out code here
* })
*/
static func showContinueAlert(title: String, message: String, target: UIViewController, handler: ((UIAlertAction) -> Void)?)
{
let alertController: UIAlertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let continueAction: UIAlertAction = UIAlertAction(title: Keys.ButtonContinue.localized, style: .Default, handler: handler)
let cancelAction: UIAlertAction = UIAlertAction(title: Keys.ButtonCancel.localized, style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
alertController.addAction(continueAction)
target.presentViewController(alertController, animated: true, completion: nil)
}
// MARK: - UIActionSheet (TODO)
// MARK: - Localization
static func getFormattedStringFromNumber(number: Double) -> String
{
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = .DecimalStyle
return numberFormatter.stringFromNumber(number)!
}
static func getFormattedStringFromDate(aDate: NSDate) -> String
{
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .MediumStyle
return dateFormatter.stringFromDate(aDate)
}
}
// MARK: - Extensions
extension String
{
var localized: String {
return NSLocalizedString(self, tableName: nil, bundle: NSBundle.mainBundle(), value: "", comment: "")
}
}
extension String
{
func localizedWithComment(comment:String) -> String {
return NSLocalizedString(self, tableName: nil, bundle: NSBundle.mainBundle(), value: "", comment: comment)
}
}
/*
Localizable.strings
ListIt
Created by Michael Sparr on 11/22/15.
Copyright © 2015 Goomzee Corporation. All rights reserved.
*/
// buttons
"button.login" = "Login";
"button.logout" = "Log Out";
"button.reset" = "Reset";
"button.ok" = "OK";
"button.help" = "Help";
"button.cancel" = "Cancel";
"button.continue" = "Continue";
// labels
"label.welcome_back" = "Welcome back,";
"label.password_reset" = "Password Reset";
// placeholders
"placeholder.email" = "Email";
"placeholder.password" = "Password";
"placeholder.confirm" = "Confirm Password";
// titles
"title.alert" = "Alert";
"title.notice" = "Notice";
"title.input_error" = "Input Error";
"title.missing_fields" = "Missing Fields";
"title.logout_confirm" = "Confirm Log Out";
"title.select_option" = "Select Option";
"title.header" = "Header";
"title.my_profile" = "My Profile";
"title.about" = "About"; // info about the app
"title.help" = "Help"; // support or help link
"title.logout" = "Log Out";
// instructions
"info.settings" = "Welcome back!";
// options
// text
"text.logout_confirm" = "Are you sure you want to log out?";
"text.fields_required" = "All fields are required";
"text.password_mismatch" = "Passwords did not match. Please retry";
"text.unknown_error" = "An unknown error occurred. Please try again";
//
// LoginViewController.swift
// Goomzee
//
// Created by Michael Sparr on 11/11/15.
//
import UIKit
class LoginViewController: UIViewController {
// MARK: - Properties
var user: User!
var userManager: UserManager!
// MARK: - IBOutlet
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
// MARK: - VC Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
userManager = UserManager.sharedInstance // singleton
usernameTextField.placeholder = Keys.PlaceholderUsername.localized
passwordTextField.placeholder = Keys.PlaceholderPassword.localized
}
// MARK: - IBAction
@IBAction func loginAction(sender: AnyObject) {
// perform action
do {
let user = try userManager.login(usernameTextField.text!, password: passwordTextField.text!)
self.performSegueWithIdentifier(Storyboard.ShowHomeIdentifier, sender: user)
} catch InputError.InputMissing {
Helpers.showOKAlert(Keys.TitleMissingFields.localized, message: Keys.FieldsRequired.localized, target: self)
} catch {
debugPrint(error)
Helpers.showOKAlert(Keys.TitleAlert.localized, message: Keys.UnknownError.localized, target: self)
}
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier
{
case Storyboard.ShowHomeIdentifier:
// NOTE: - HomeViewController is just custom class for another view after login
let destinationViewController = segue.destinationViewController as! HomeViewController
destinationViewController.user = self.user
default:
break
}
}
}
}
//
// SettingsTableViewController.swift
// Goomzee
//
// Created by Michael Sparr on 11/16/15.
//
import UIKit
class SettingsTableViewController: UITableViewController {
// MARK: - Public API
var user: User!
var userManager: UserManager!
// MARK: - Private
private let settingsNav = [
[Keys.TitleHeader.localized], // profile image
[Keys.TitleMyProfile.localized, ...],
[Keys.TitleAbout.localized, Keys.TitleHelp.localized, Keys.TitleLogout.localized]
]
// MARK: - IBOutlet
// MARK: - VC Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
userManager = UserManager.sharedInstance
user = userManager.getCurrentUser()
}
// MARK: - UITableViewDataSource
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return settingsNav.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return settingsNav[section].count
}
// MARK: - UITableViewDelegate
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return CGFloat.min
} else {
return 20.0
}
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return nil
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let section = indexPath.section
let sectionRows = settingsNav[section]
let row = sectionRows[indexPath.row]
switch row
{
case Keys.ButtonLogout.localized:
Helpers.showContinueAlert(Keys.TitleLogoutConfirm.localized, message: Keys.LogoutConfirm.localized, target: self, handler: { (UIAlertAction) -> Void in
// perform log out
debugPrint("User logged out at \(NSDate())")
self.userManager.logout()
let storyboard: UIStoryboard = UIStoryboard(name: Storyboard.Main, bundle: nil)
let viewController: UIViewController = storyboard.instantiateViewControllerWithIdentifier(Storyboard.LoginViewIdentifier) as! LoginViewController
self.presentViewController(viewController, animated: true, completion: nil)
})
default:
break
}
}
// MARK: - IBAction
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
debugPrint("Identifier from settings was \(identifier)")
switch identifier {
case Storyboard.ShowProfileIdentifier:
debugPrint("Sending user \(user.displayName) to viewController")
let destinationNavigationController = segue.destinationViewController as! UINavigationController
let viewController = destinationNavigationController.topViewController as! UserProfileTableViewController
viewController.user = user
default:
break
}
}
}
}
//
// Strings.swift
// Goomzee
//
// Created by Michael Sparr on 11/22/15.
//
import Foundation
struct Keys
{
// buttons
static let ButtonOK = "button.ok"
static let ButtonHelp = "button.help"
static let ButtonCancel = "button.cancel"
static let ButtonContinue = "button.continue"
static let ButtonLogin = "button.login"
static let ButtonLogout = "button.logout"
// labels
static let LabelWecomeBack = "label.welcome_back"
static let LabelPasswordReset = "label.password_reset"
// placeholders
static let PlaceholderEmail = "placeholder.email"
static let PlaceholderPassword = "placeholder.password"
static let PlaceholderConfirm = "placeholder.confirm"
// titles
static let TitleAlert = "title.alert"
static let TitleNotice = "title.notice"
static let TitleInputError = "title.input_error"
static let TitleMissingFields = "title.missing_fields"
static let TitleLogoutConfirm = "title.logout_confirm"
static let TitleHeader = "title.header"
static let TitleMyProfile = "title.my_profile"
static let TitleAbout = "title.about"
static let TitleHelp = "title.help"
static let TitleLogout = "title.logout"
// instructions
static let InfoSettings = "info.settings"
// options
// text
static let LogoutConfirm = "text.logout_confirm"
static let FieldsRequired = "text.fields_required"
static let PasswordMismatch = "text.password_mismatch"
static let UnknownError = "text.unknown_error"
}
//
// UserManager.swift
// Goomzee
//
// Created by Michael Sparr on 11/12/15.
//
import Foundation
struct User
{
var id: String!
var displayName: String?
var emailAddress: String!
}
class UserManager
{
// MARK: - Singleton
static let sharedInstance = UserManager()
private init() {
}
// MARK: - Properties
var user: User!
// MARK: - Public API
func login(username: String?, password: String?) throws -> User
{
guard let username = username, let password = password
where !username.isEmpty && !password.isEmpty else {
throw InputError.InputMissing
}
// perform user login functions here
return self.user
}
func registerUser(username: String?, password: String?, confirm: String?) throws -> User
{
guard let username = username, let password = password
where !username.isEmpty && !password.isEmpty else {
throw InputError.InputMissing
}
guard let confirm = confirm
where confirm == password else {
throw InputError.PasswordMismatch
}
// perform user register functionality here
return self.user
}
func resetPassword(email: String?) throws
{
guard let email = email
where !email.isEmpty else {
throw InputError.InputMissing
}
// perform password reset functionality here
}
// MARK: - Private
...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment