Giter Club home page Giter Club logo

ios-handbook's Introduction

logo

This playbook consists of a set of guidelines that may be useful to you. It is a living document that contains good advice on how to style code, communicate, use git, and more. As a guideline, it is not a rule of law. Being consistent with the repo and your fellow developers takes precedence over this playbook. Also, the playbook can be ignored if a developer believes that adhering to the guidelines would give a worse result. At which point - you may want to consider updating the playbook ๐Ÿ˜€

Don't forget to check out our favorite suite of components and helper methods and the other B&B iOS repos to see if we've already built something to help with what you're trying to do.

Index

Maintainers

License

License: CC BY-SA 4.0

ios-handbook's People

Contributors

3lvis avatar attila-bardos avatar bcapps avatar brow avatar csdodd avatar designatednerd avatar dlo avatar eerwitt avatar elland avatar etigerstudio avatar heumn avatar jacobvanorder avatar jasonm23 avatar jawwad avatar jeffleeismyhero avatar jfiore avatar jgorset avatar kostiakoval avatar marijnschilling avatar mattbischoff avatar maxgabriel avatar murdocdv avatar nselvis avatar paulbruneau avatar peteog avatar phelps avatar ramongilabert avatar robrix avatar twigz avatar zenangst avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ios-handbook's Issues

Declare constants in headers as extern variables

Declare constants in headers as extern variables which are defined in the corresponding implementation file.

extern NSString * const kCHRDeclerativeNameOfString;

Constants should be declared like this if they are available across files. And how do you make it available? Like we do with everything else. We create a decleration in the header file, and we define it in the implementation file.

http://ashfurrow.com/blog/structuring-modern-objective-c/

View configuration + Auto-layout

We agreed on using auto-layout as much as possible now, but in the interest of decluttering controllers and compartmentalisation, we also think we should avoid configuring the views inside the controller as well. So as much configuration code as possible should come from the view. That means that the view would become more aware of its subviews as well. So the focus would be view composition, instead of a view controller managing everything.

Master Detail class name convention

For a Recipes app right now we are using RecipesController and RecipeController, even though this does the job, I think it would be better if we have a keyword that can extend between multiple projects to name this classes. I'm proposing that we use: RecipeListController and RecipeDetailController.

What do you think?

BEFORE_PROJECT_CHECKLIST.md

  • iOS Versions
  • Platforms (iOS - iPad)
  • Portrait - Landscape (be very clear, iOS doesn't cut it)
  • Change effective hours from 7.5 to 7, it's more realistic (deadlines)
  • Move from days to hours for deadlines
  • Localization: How many languages should be supported?
  • Advanced Accessibility

- Every app has a testing time of 10% of the development time

Document lazy loading

When creating variables that require setup, it's better to do lazy loading instead of adding the logic to the viewDidLoad or init methods.

Wrong

class RecipeCell: UITableViewCell {

init() {
    self.label = UILabel(....)
    self.label.textColor = .red  

    super.init()
}

Right

class RecipeCell: UITableViewCell {

lazy var label: UILabel = {
    let label = UILabel(....)
    label.textColor = .red  

    return label
}()

init() {
    self.contentView.addSubview(self.label)

    super.init()
}

When using Property Observers make sure to not over-reload the UI

It's common that when using property observers any change will update some part of the UI.

For example:

// PhotoViewerController

var photo: Photo? {
    didSet {
        guard let viewerItem = self.viewerItem else { return }
        // Do something with photo, maybe download and so on
    }
}

The problem with this logic is that if for some reason the user of the PhotoViewerController is required to update multiple times his PhotoViewerController instance with the same photo, this will have awful performance issues. And since this user doesn't know the kind of logic processed inside this controller, bugs can occur. So it's better that if the PhotoViewerController is handling property observation, it should also handle the cases where the new introduced value is the same as the one that currently exists:

There are two ways to solve this issue:

Version A:

One solution would be that the API user (the abuser) makes sure that he doesn't set a photo item if it has already being set.

// ViewerController (A horizontal array of PhotoViewerController)

let photoViewerController = self.cachedPhotoViewerControllers.objectForKey(photo.remoteID)
if photoViewerController.photo?.remoteID != photo.remoteID {
    photoViewerController.photo = photo
}

Version B (Recommended):

The other solution would be that the PhotoViewerController takes care of this since it's the one that decided to do property observing instead of having a separate method to trigger this side effect (downloading photo).

// PhotoViewerController

var changed = false
var photo: Photo? {
    willSet {
        if self.photo?.remoteID != newValue?.remoteID {
            self.changed = true
        }
    }

    didSet {
        guard let photo = self.photo else { return }

        if self.changed {
            // Do something with photo, maybe download and so on
            self.changed = false
        }
    }
}

Commenting code

One-line:

// Workaround: This is the comment

Multiple-lines:

/*
   Workaround: Selected cell only gets deselected when pressing the back button
   dragging the screen to go back doesn't deselect the selected cell.
   So, `self.clearsSelectionOnViewWillAppear = YES;` only works sometimes.
 */

Guideline for static tables

Introduction

Let's say you have a static table view, where each cell needs a different treatment. One way to differentiate them is by assigning a type to the model so you can use a switch later. The problem is that initializing an enum for this Type later causes warnings in Xcode and doesn't compile.

In my project, I have a table with Album settings. This table has a few sections, one of this sections is "sharing".

screen shot 2016-11-22 at 10 07 25

Here we have 3 rows. We need to detect taps in the first and third row, taps in the second row are ignored. We need to detect toggle switching in the second row.

These are static cells, so we could use indexPath comparison to see if they are the first, the second or the third. But comparing to indexes can be difficult to read, that's why we have an enum to differentiate which type of row they are.

struct ShareSettingRow {
    enum `Type`: Int {
        case copySharingURL
        case anyoneCanAddPhotos
        case unshare

        var title: String {
            switch self {
            case .copySharingURL: return NSLocalizedString("Copy sharing URL", comment: "")
            case .anyoneCanAddPhotos: return NSLocalizedString("Anyone can add photos", comment: "")
            case .unshare: return NSLocalizedString("Unshare album", comment: "")
            }
        }
    }
}

These cells use the class AlbumSettingCell. Here's how we style them.

func update(albumSection: AlbumSettingsSection, album: Album, indexPath: IndexPath) {
    let sectionKind = albumSection.kind
    self.textLabel?.textColor = UIColor(hex: "6A6970")
    self.textLabel?.font = .light(16)
    self.detailTextLabel?.text = nil
    self.accessoryView = nil

    switch sectionKind {
    // ... more sections
    case .sharing:
        let sharingRowKind = ShareSettingRow.`Type`(rawValue: indexPath.row)!
        switch sharingRowKind {
        case .copySharingURL:
            self.textLabel?.textColor = UIColor(hex: "CC6EF1")
            self.textLabel?.font = .regular(16)
            self.accessoryView = UIImageView(image: UIImage(named: "get-link-gray")!)
        case .anyoneCanAddPhotos:
            self.accessoryView = self.switch
            break
        case .unshare:
            self.textLabel?.textColor = UIColor(hex: "EF3138")
            self.textLabel?.font = .regular(16)
        }

        self.textLabel?.text = sharingRowKind.title
    //... more sections
    }
}

Problem

Swift doesn't let you do this:

let sharingRowKind = ShareSettingRow.`Type`(rawValue: indexPath.row)!

Proposed solutions

Use Kind

Use Kind instead of Type for this cases since it's not a reserved word.

Use models

Create a struct for each row and assign a type to them.

Document why external testers vs internal testers on TestFlight

  • Internal requires that the user is an iTunes Connect member, so they can't reuse the same email for different accounts.
  • Internal sends emails about every little issue regarding your builds, you don't want this for your testers.
  • Releasing to external testers takes the same time as doing it for internal testers if you only increment the build number

Add best practices for error handling

For example this is how sharing an album would be done.

func shareAlbum() {
    // 1.- It can be any other type of progress indicator.
    let progressAlert = UIAlertController.progressAlert("Sharing album...")
    self.presentViewController(progressAlert, animated: true, completion: nil)
    self.fetcher.share(album) { url, error in
        progressAlert.dismissViewControllerAnimated(true) {
            // 2.- First handle error
            if let error = error {
                let errorAlert = UIAlertController.errorAlert(error)
                self.presentViewController(errorAlert, animated: true, completion: nil)
            } else {
                // 3.- Add guard to check for expectation
                guard let url = url else { fatalError("Couldn't find url") }
                UIPasteboard.generalPasteboard().string = url
                let progressAlert = UIAlertController.dismissableAlert(title: "Link copied to the clipboard")
                progressAlert.addAction(UIAlertAction(title: "Open", style: .Default, handler: { action in
                    UIApplication.sharedApplication().openURL(NSURL(string: url)!)
                }))
                self.presentViewController(progressAlert, animated: true, completion: nil)
            }
        }
    }
}

iOS-team workshop

We'll be working together for four days to streamline the iOS team and make sure we're all on the same page.

The goal is to optimise the way we work together by:

  • Making guidelines on how we write code so that we can share and reuse it.
  • Building a simple app together so we cover all the points of architecture and process. (It'll be a photo app to demo our open source libraries.)
  • Discussing and tackling common problems in code and process.

Discussing personal points:

  • Discussing common problems in code, and tackle them either in the Unsplash app or in a separate session
  • Discussing common problems in the work process so we know each others pitfalls and strengths!
  • Discussing toolchain and process. How can we all benefit from better, smarter, faster tools. (Ideally, we all learn a bit about the tools some of us use that the others don't.)

Discussing general things in the B&B process:

  • Discussing and making guidelines for how and what to contribute to the SweetUIKit (tuesday)
  • Talking about the process of working together (wednesday)
  • Discussing how we handle clients (thursday)
  • Going over and adding things to the iOS playbook so we're all on the same page and new employees will be too (friday)

Create a small app to showcase our open source libraries based on the open unsplash api, where we'll pay extra attention to:

  • Define (and redefine) standards in architecture
  • Revisit iOS tests
  • Look at how we can make away with the boilerplate from CollectionView and TableView
  • Look at Autolayout + StackView

Have fun together!

  • Go to dinner on Friday ๐ŸŽ‰
  • Make at least one joke p.p once a day
  • Create a secret handshake for the iOS-team

Discourage usage of #imageLiteral and #colorLiteral

Why? For images there's no easy way to see which file name they are pointing to, so if you want to search and replace, nope, not happening. For colors is similar, if you want to check if you're using the same color somewhere else you can't there's only a block of color and you can't see the contents of it.

screen shot 2017-01-19 at 16 55 17

New project checklist

We should create this checklist in the playbook.

  • what iOS version
  • ipad iphone?
  • does it need to work offline?
  • will it require extensions, watch os today widget extensions
  • what are we selling to the client. Is it a product or a prototype? Just implement the idea or experiment.

Hiring

Questions
Feedback
Preferred candidates

How do we do things

Table of Contents

  • Goals
    • Company
    • Personal
    • Community
  • Code Review
  • Hiring
  • Weekly meetings
  • Playbook
  • Components & Open Source

Goals

Company

  • Build reliable and user-friendly iOS apps
  • Provide value and solves people's problems
  • Deliver software on time
  • Don't reinvent the wheel and share between projects
  • Build a set of tools that can help us achieve these goals faster
  • Build good tools with good documentation

Personal

  • Self-improvement, get MUCH better every year
  • Be famous
  • Be happy
  • Enjoy all the projects
  • Have ownership of what I'm doing
  • Be able to feel like I can count on and collaborate with anyone on my team

Community

  • Go from ranking 1651 to top 100

screen shot 2017-01-26 at 10 46 30

  • 1 blog post per month
  • Become more visible

How do we agree on things?

We should have a framework for evaluating ideas and proposals. Steps to follow in each phase and ways to find a resolution when we don't agree on something unanimously. Finally, we should discuss what to do when someone from the team decides not to follow the playbook.

Challenges

  • Most efficient way to compare large files starting with MD5
    • Stream data through a pipe generation a MD5 from the stream
  • Play slow motion videos
  • It's hard to get started on a new project
    • Improve template
    • Checklist of things before starting a new project
    • Plan ahead: Dependencies, what can be open sourced, structureย 

Conditionals

This

1:

if (....) {
  // ....
} 

else {
  // ....
}

vs

2:

if (....) {
  // ....
} else {
  // ....
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.