Last active
March 18, 2018 00:20
-
-
Save mikesparr/ef611fee6cc1865ba0b7 to your computer and use it in GitHub Desktop.
Swift IOS Architecture Quick Start
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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" | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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
Localization (with i18n)
Updated the files to use localized strings along with helper extension of the
String
object so you can useString.localized
where the string is the key to theLocalizable.strings
item. Instead of hand-writing each key as used throughout app, instead use aStrings.swift
file with Keys struct so you can auto-complete through app.Adding local strings file:
File > New > Resource (strings)
Localizable.strings
Usage:
Localizable.strings
file with respective key (don't forget semicolon at end in this file)Strings.swift
file in appropriate sectionKeys.SomeConstantName.localized
Note:
XCode has built-in localization of the storyboards, etc. that can generate for each locale and get translated. Minimize the hard-coded strings in your storyboards, however, or override them in code when instantiating your views and leverage this technique wherever possible.