LESSONS LEARNED. SWIFTDagna Bieda, 7th April 2016
SWIFT & XCODEBrief Intro
“ Language that essentially marries the readability of Python with the speed of C++.
@jeremyconkin
SWIFT
➤ mix of good practices from multiple languages, rooted in C,
➤ statically typed language,
➤ no nil as we know it, let n = nil // type of expression is ambiguous without more context
let i : Int? = nil // the result is actually Optional<Int>.None
➤ all variables must be initialized before using them,
➤ memory management done by Automatic Reference Counting,
➤ object-oriented, functional, generic, protocol-oriented,
SWIFT
➤ very large community,
➤ massive amount of free tutorials (www.raywenderlich.com),
➤ recently open-sourced,
➤ possible to create server backend using Kitura-IBM or Perfect
SWIFT. WHERE TO LEARN FROM
➤ stackoverflow.com
➤ youtube.com: Stanford - Developing iOS 8 Apps with Swift
➤ udacity.com:
A. Intro to iOS App Development with Swift
B. iOS Developer Nanodegree
➤ edx.com
➤ coursera.com
XCODE
➤ enforces type-safety, // var was never changed, consider changing to let
➤ integrated with control management systems - git and SVN,
➤ code coverage, profiling, memory usage, leaks,
➤ test recording,
➤ storyboards <3
HEALTH CAREapp case study
APP REQUIREMENTS
➤ Facilitate communication between patient and their nurse,
➤ Share health data of the patient on a daily basis,
➤ Share videos and/or documents,
➤ Simple management of multiple patients,
➤ Be easy to use (non tech-savvy users)
DEMO APP + TEST
CODE EXAMPLESimport GRDB
enum UserType: String, DatabaseValueConvertible { case Nurse = "Nurse", Patient = "Patient"
var databaseValue: DatabaseValue { get { return DatabaseValue(object: self.rawValue)! } }
static func fromDatabaseValue(databaseValue: DatabaseValue) -> UserType? { let value = databaseValue.value() as! String return UserType(rawValue: value) }
func toggled() -> UserType { switch self{ case .Nurse : return .Patient case .Patient : return .Nurse } } }
CODE EXAMPLESprotocol DashboardDrawable { func setupAt(origin: CGPoint, xOffset: CGFloat, size: CGSize, addToView masterView: UIScrollView) }
class MessageView: UIView, DashboardDrawable {
func setupAt(origin: CGPoint, xOffset: CGFloat, size: CGSize, addToView masterView: UIScrollView){ var frame = CGRect(origin: origin, size: size) frame.origin.x += xOffset self.frame = frame self.backgroundColor = UserSettings.messageViewColor self.addLabels() masterView.addSubview(self) } }
CODE EXAMPLESprotocol DashboardDrawable { func setupAt(origin: CGPoint, xOffset: CGFloat, size: CGSize, addToView masterView: UIScrollView) }
class MessageView: UIView, DashboardDrawable {
func setupAt(origin: CGPoint, xOffset: CGFloat, size: CGSize, addToView masterView: UIScrollView){ var frame = CGRect(origin: origin, size: size) frame.origin.x += xOffset self.frame = frame self.backgroundColor = UserSettings.messageViewColor self.addLabels() masterView.addSubview(self) } }
// Usage in DashboardDetailTableViewController private func preparePatientDataForCell(cell: PatientDataCell, patient: Patient, date: NSDate, viewSize: CGSize) {
let width = computeDashboardsElementWidth(viewSize.width) let size = CGSize(width: width, height: viewSize.height)
let matchPatientsIdAndSelectedDate = { (cp: ContactPointProtocol) -> Bool in return cp.patientId == patient.serverId && cp.date ~= date }
for message in dbWrapper.messages.filter(matchPatientsIdAndSelectedDate) { let view = MessageView(message: message)
let offset = timeline.calculateDateOffset(message.date) view.setupAt(CGPointZero, xOffset: offset, size: size, addToView: cell.patientDashboard) } }
CODE EXAMPLESprotocol DateDisplayButtonDelegate: class { func dateDidChange(newDate: NSDate) }
class DateDisplayButton: UIButton {
var date: NSDate! { // ! deferred initialization didSet{ if oldValue != date { let components = NSCalendar.currentCalendar().components([.Month, .Day, .Weekday], fromDate: date) let weekday = Weekday(rawValue: components.weekday)!.description let month = Month(rawValue: components.month)!.description let day = components.day let title = String("\(weekday), \(month) \(day)") self.setTitle(title, forState: .Normal)
if oldValue != nil { delegate?.dateDidChange(date) } } } } var delegate: DateDisplayButtonDelegate? }
! ?
CODE EXAMPLESprotocol DateDisplayButtonDelegate: class { func dateDidChange(newDate: NSDate) }
class DateDisplayButton: UIButton {
var date: NSDate! { didSet{ if oldValue != date { \\ ... if oldValue != nil { delegate?.dateDidChange(date) } } } } var delegate: DateDisplayButtonDelegate? }
class DashboardDetailTableViewController: UIViewController, UITableViewDelegate, UIScrollViewDelegate, UIPopoverDelegate, DateDisplayButtonDelegate {
@IBOutlet weak var dateDisplayButton: DateDisplayButton! { didSet { dateDisplayButton.delegate = self } }
// MARK: DateDisplayButtonDelegate func dateDidChange(newDate: NSDate) { preparePatientsData(newDate) } }
CODE EXAMPLESprivate extension UIColor { class var random: UIColor { switch arc4random()%5 { case 0: return UIColor.greenColor() case 1: return UIColor.blueColor() case 2: return UIColor.orangeColor() case 3: return UIColor.redColor() case 4: return UIColor.purpleColor() default: return UIColor.blackColor() } } }
// Tuples and _ placeholder class TimelineDashboardHeader: UIScrollView { var majorTics = [NSDate : (String, (UILabel, UIBezierPath))]() var minorTics = [NSDate : (String, UIBezierPath)]()
func calculateDateOffset(date: NSDate) -> CGFloat{ let roundedDate = roundedDownToNearestHalfHour(date)
var tic: UIBezierPath? = UIBezierPath() if let (_, (_ , tic)) = majorTics[roundedDate] { let roundedX = tic.currentPoint.x let timeDistance = CGFloat(date.timeIntervalSinceNow - roundedDate.timeIntervalSinceNow) let xOffset = roundedX + (timeDistance/majorTicsTimespan * intervalSize) return xOffset } else { return 0.0 }
}
NOT SO FUN PART➤ casting Int or Double to CGFloat
func frameForMenuButton(indexPath: NSIndexPath) -> CGRect { let row = indexPath.row / numberOfColumns let column = indexPath.row % numberOfColumns
let originX: CGFloat = floor((itemSize.width + horizontalSpace) * CGFloat(column)) let originY: CGFloat = floor((itemSize.height + verticalSpace) * CGFloat(row))
return CGRectMake(originX, originY, itemSize.width, itemSize.height) }
➤ mysterious UIScrollView ➤ “in” to denote end of a closure’s type signature ➤ protocol with a settable variable has to be restricted to classes (it not a struct with a mutating setter which should be allowed)
THANK YOU!
#ifdef greatPresentation
#include ”standing-ovation.hpp”
#endif