ABOUT
TEAM
WORK
CONTACT
|
BLOG

GirAppe blog

2018/12/30

iOS Launch Screen Tips & Tricks

When the basic launch screen is actually too basic.

Creating a good launch screen is always a challenge. Creating a good launch screen while being compliant to Apple guidelines is a challenge even more. Since the approach Apple recommends actually makes sense (at least for me), it surprised me how hard it is to make it right, and how uncommon it is to see it done.

In this article, I will present briefly a few minor things I struggled with while trying to balance between custom UI and recommended approach. To be fair, I have to say that the majority of apps just slide over the topic and just place logo as a splash screen. I've done that as well, more than once. But this time I wanted to do it as close to "by the book" as possible. I will try to sum this up in a few points as possible.

1. Human Interface Guidelines - Launch Screen

I don't want to rewrite what is already done, as Apple already has a section in guidelines for Launch Screen.

Human Interface Guidelines - Launch Screen

TL;DR

The guidelines for Launch Screen are not long and can be summarised in just few bullet points:

From my experience, the last point is kind of dead, a lot of apps does exactly that and I never heard about any review issues because of that.

2. Idea

The outlined points are what I actually wanted to achieve. I wanted to make a very lightweight launch screen, that actually reassembles the first screen of the app.

Ok, that seems fairly easy, right? Let's see how does it look:

Pass4Wallet first screen

To have something similar to what Apple shows as an example, I should then:

Also, I decided to not to keep any "interactive" elements on the screen while the app is loading so all buttons would be gone. It would look something like one of the following:

Pass4Wallet launch1 screen   Pass4Wallet launch2 screen

I actually went for the second option, as I did not want to give up branding completely. I left screen title as well; it is not localized, so it would not be a problem, and it will actually reduce the number of differences between launch and first screen of the app.

3. Challenges

The first and biggest challenge is that we cannot use any custom classes. That means that no actual code would be executed! So all we do has to be possible to setup through IB. Another limitation (that was not obvious for me at the beginning) is no custom runtime attributes.

Navigation Bar:

Another challenge was with the navigation bar. Fortunately, we can use UINavigationController, UITabBarController etc., as long as we are fine with its default look (or the level of customization available from IB).

In my case, I was extending navigation bar with this top menu thing (noticed this small line on screenshots from storyboard?). Fortunately, I was able to set empty image (1 transparent pixel) as navigation bar shadow image. Then I can extend it with plain UIView:

Pass4Wallet nav bar extend

Shadows:

There is no way to use built-in shadows - no shadow layer available, nor runtime attributes. Which means that I had to generate shadow beforehand, and use it as an image. I've just used an UIImageView with "scale to fill" mode, and a thin slice of vertical shadow:

Pass4Wallet shadow view

Pass4Wallet shadow image

Borders:

In my case, I actually skipped borders. The actual problem here is, again, no runtime attributes to set it on the layer level. If you need borders, you will have to make them from separate UIView's.

Rounded corners:

I have to admit, that this gave me a lot of troubles. There are no custom runtime attributes so we cannot change any of the layer properties. The approach is the same as last time, we can just pre-render rounded corners and use images instead of drawing.

As a first approach, I just generated 4 assets, one for every corner (@1x @2x @3x), giving in total 12 images. I made them white, hoping that setting image mode to "always template" will let me control corners color using tint.

Pass4Wallet corners

Unfortunately, for some reason, my corners were ignoring tint color. The problem I had with this approach was the actual number of assets I had to generate. Maintaining separate images for every corner, scale, and color (I needed white and light grey) was giving quite a big number of assets.

The solution proved to be straightforward (at least when looking at it now). When you look at it in a more general way, four corners (with radius 10pt in my case) could be actually combined into one image.

Pass4Wallet corners

Then, the one last thing to resolve is to how to control, which part of the combined image is used (and so which actual corner is drawn). It could be done by setting image view "content mode" to one of the following:

I have to admit, that this is actually the first time, I used this content mode.

This approach to combining several images into one asset or atlas is quite common, but I've never seen it in iOS (at least in a non-game context). Even if it's so straightforward, I was very happy to find it out :)

Final effect below:

Pass4Wallet corners

2017/12/03

Using SwiftyMocky to generate mocks and simplify unit testing in Swift

Simple 5 points guide to leverage your unit testing into completely new level.

There are a lot going on lately in Swift world in general, and the trend did not miss testing part of development. While good testing practices are spreading, the need for tools designed to aid that process also grows. I have a pleasure to be a part of a team working on one of the solutions called SwiftyMocky.

1. Mock is Test Double

When talking of unit testing and testing in general, there is a whole set of special purpose objects, called Test Doubles. In this article I will only briefly go through them, as it is needed to understand whole concept behind SwiftyMocky.

The whole story behind Test Doubles is quite simple. The, for lack of better word, classic way of testing often relies on state verification. We create initial state for sut and its dependencies, and then verify, whether state after performing test matches our expectations. Still, not in every test case we are testing sut, we can use real implementations as its dependencies.

Good example are database operations, or network calls. We don't really want to make real calls there, the knowledge that sut tried to do them is usually enough to assure validity. Another good example (from Martin Fowler post) is sending email - hard to verify from test perspective.

That's where our special case objects come into picture. Based on the vocabulary proposed by Gerard Meszaros (which i personally found pretty useful) I would split them into following categories:

For the rest of article we will focus on Mock, as it handles most of the stuff needed for proper testing.

Please note, that while vocabulary above strictly distinct between test doubles types, in most tools Mock is extended, with combined functionality of Stub and Spy. In SwiftyMocky we do the same, so whenever we refer to Mock, we mean object that also provides both of Stub and Spy features.

While idea of having Mock objects is quite clear, and benefits cannot be overstated, that leaves us with one problem. The mock implementations have to be written, which can become quite an overhead in big projects, with proper layer separation.

In languages that have proper reflections, there are numbers of libraries and frameworks that can create Mock in runtime, allowing you to choose one, fitting best your style of testing.

2. SwiftyMocky - the idea

As Swift does not support reflections (yet?), other approaches are needed. There are bunch of solutions that are partially manual (when you have to implement part of the mock manual), and a few that uses meta-programming to generate complete mock implementation.

SwiftyMocky falls into second category (originally proposed by Przemysław Wośko). Whole concept is based on Sourcery (written by Krzysztof Zabłocki) and utilizes meta-programming concept.

First step is scanning sources, and checking for types, that could be subject of mocking. In SwiftyMocky it is done by either annotations, or adopting AutoMockable protocol.

Second step is generation of swift file, containing all mock's implementations, adopting from variety of Mock protocols (like Mock and StaticMock to name few). That file can be added to test target, allowing to use all generated classes.

In step three we could write tests, using whole set of handy methods for classes adopting Mock protocols, allowing:

Worth to notice is that with SwiftyMocky you can focus on step 3. Just write the tests and let the library handle rest of boilerplate stuff :)

3. Why SwiftyMocky? Best features overview

Automatic mocks generation:

This greatly reduces time needed to setup tests. And its much easier to adopt to changes, as the mock implementations could be updated on the go.

Let's consider simple protocol.

//sourcery: AutoMockable
protocol UserStorageType {
    func surname(for name: String) -> String
    func storeUser(name: String, surname: String)
}

It will result in something like:

// MARK: - UserStorageType
class UserStorageTypeMock: UserStorageType, Mock {
    func surname(for name: String) -> String {
      // ...
    }

    func storeUser(name: String, surname: String) {
      // ...
    }

    // ... mock internals

Please note, that for now only protocols are subject of Mock generation. We believe that interfacing is the proper way of layer separation and setting up dependencies in Swift. However, mocking classes are possible in the future (but not without limitations)

Easy stubbing:

One of the most important things for our mock implementation, if it's gonna be anything more than dummy, is a way to specify method return values. In typical plain stub we just hardcode value, but its not very flexible.

SwiftyMocky provides nice way to specify return values, both with easy syntax and flexibility. Please consider mock from above, that declares method surname(for name: String) -> String:

SwiftyMocky allows us to do something like:

let mock = UserStorageTypeMock()

// For all calls with name Johny, we should return Bravo
Given(mock, .surname(for: .value("Johny"), willReturn: "Bravo"))
// For all other calls, regardless of value, we return Kowalsky
Given(mock, .surname(for: .any, willReturn: "Kowalsky"))

XCTAssertEqual(mock.surname(for: "Johny"), "Bravo")
XCTAssertEqual(mock.surname(for: "Mathew"), "Kowalsky")
XCTAssertEqual(mock.surname(for: "Joanna"), "Kowalsky")

Please note . at the beginning of the method, as it strictly refers to:

Autocomplete:

We utilize as much power of autocomplete as possible, to aid process of writing tests. When performing Given, Verify, Perform on mock, type . to get list of all methods declared by mocked protocol, that fits particular case. All of that is type-safe.

That is the situation, where one image is worth thousand words:

Given example

We don't force user into remembering additional stuff. And there is no longer a need for error prone "String" identifiers (like OCMock does).

Easy spying:

Stubbing is often not enough, and so we prepared a way to verify, whether a method was called (and how many times). Syntax is consistent with the one proposed in Given:

// inject mock to sut. Every time sut saves user data, it should trigger storage storeUser method
sut.usersStorage = mockStorage
sut.saveUser(name: "Johny", surname: "Bravo")
sut.saveUser(name: "Johny", surname: "Cage")
sut.saveUser(name: "Jon", surname: "Snow")

// check if Jon Snow was stored at least one time
Verify(mockStorage, .storeUser(name: .value("Jon"), surname: .value("Snow")))
// storeUser method should be triggered 3 times in total, regardless of attributes values
Verify(mockStorage, 3, .storeUser(name: .any, surname: .any))
// storeUser method should be triggered 2 times with name Johny
Verify(mockStorage, 2, .storeUser(name: .value("Johny"), surname: .any))

Flexibility:

Wherever it makes sense, we wrap method attributes into Parameter enum. It allows to specify if we care about explicit value, or not:

All the cases could be mixed together, giving quite nice flexibility for stubbing and spying.

In the moment I'm writing that, version 2.0 is on its way, adding .matching(Type -> Bool) case to Parameter, providing even more flexibility.

Generics:

When I'm writing that, it seems that SwiftyMocky is the only Swift tool that supports generics. (Or at least both generics and mock generation).

//sourcery: AutoMockable
protocol ProtocolWithGenericMethods {
    func methodWithGeneric<T>(lhs: T, rhs: T) -> Bool
}

We can use it in tests like following:

let mock = ProtocolWithGenericMethodsMock()

// For generics - you have to use .any(ValueType.Type) to avoid ambiguity
Given(mock, .methodWithGeneric(lhs: .any(Int.self), rhs: .any(Int.self), willReturn: false))
Given(mock, .methodWithGeneric(lhs: .any(String.self), rhs: .any(String.self), willReturn: true))
// In that case it is enough to specify type for only one element, so the type inference could do the rest
Given(mock, .methodWithGeneric(lhs: .value(1), rhs: .any, willReturn: true))

XCTAssertEqual(mock.methodWithGeneric(lhs: 1, rhs: 0), true)
XCTAssertEqual(mock.methodWithGeneric(lhs: 0, rhs: 1), false)
XCTAssertEqual(mock.methodWithGeneric(lhs: "a", rhs: "b"), true)

// Same applies to verify - specify type to avoid ambiguity
Verify(mock, 2, .methodWithGeneric(lhs: .any(Int.self), rhs: .any(Int.self)))
Verify(mock, 1, .methodWithGeneric(lhs: .any(String.self), rhs: .any(String.self)))

4. Installation and Setup

SwiftyMocky is available both by CocoaPods and Carthage, and additional steps required to run it differs a bit, based on dependency manager used. Full instructions are available here.

The simplest approach would be to use CocoaPods, as it handles getting Sourcery as well (in case of carthage you would have to do it yourself).

While I'm writing that, Sourcery is at version 0.9.0, compiled for Swift 4.0.0. It often leads to problems for users of Xcode 9.1, so along with SwiftyMocky comes script to override Sourcery in such a cases (check here)

Whole setup is done in yml config file. We could specify multiple locations for source files, both as list of files or folders. It usually looks something like below:

sources: # locations to scan
  - ./ExampleApp
  - ./ExampleAppTests
templates: # location of SwiftyMocky templates in Pods directory
  - ./Pods/SwiftyMocky/Sources/Templates
output:
  ./ExampleApp # here Mock.generated.swift will be placed
args: # additional arguments
  testable: # assure @testable imports added
    - ExampleApp
  import: # assure all external imports for mocks
    - RxSwift
    - RxBlocking
  excludedSwiftLintRules: # for lint users
    - force_cast
    - function_body_length
    - line_length
    - vertical_whitespace

Then, to trigger generation, Sourcery is invoked with specified config. While it often looks something like that:

Pods/Sourcery/bin/Sourcery.app/Contents/MacOS/Sourcery --config mocky.yml

It could be wrapped in Rakefile, allowing to do just rake mock:

# Rakefile
task :mock do
  sh "Pods/Sourcery/bin/Sourcery.app/Contents/MacOS/Sourcery --config mocky.yml"
end

As mocks generation takes some time, we see no value in adding it to build run script phases, but we found it quite convenient to use Xcode behaviors and key bindings to regenerate mocks in project.

5. Summary

While there were several good tools for obj-c, Swift was still lacking some proper solutions. SwiftyMocky tries to address that, providing automatic mock generation and easy to get and use syntax, utilizing as much power of auto-complete as possible.

It is still a new thing, getting more mature overtime, yet bigger changes are still possible. Good thing about that is it still have velocity required to grow, providing new features and fixing current.

If I have to name why you should give it a try, I would say:

2017/09/21

POP - Swift protocols with stored properties, and where to use them

Protocol oriented programming is recently on the crest of a wave. Composition has many advantages over inheritance, allowing to create more flexible and transparent code.

I will spare most of arguments in favour of POP, and focus on one particular example, I found quite interesting.

But first, I'll refer to todays post topic - how's with that protocol properties?

By the book, Swift protocols being a blueprints for real implementation, don't seem to have much flexibility here. But then, Swift 2.0 (yeach, I know, it's prehistory) introduced default implementations for Swift protocol extension, allowing something like this:

protocol SomeProtocol {
    func getSomeValue() -> Int
}

extension SomeProtocol {
    func getSomeValue() -> Int {
      return 0 // default behaviour
    }
}

class SomeClass: SomeProtocol { } // already adopting SomeProtocol

It works very same for properties, but the downside is that you can't have stored properties, and so implementing custom getters and setters is the only option.

protocol SomeProtocol {
    var someValue: Int? { get set }
}

extension SomeProtocol {
    var someValue: Int? {
      get { return nil } // not always possible to specify good default value
      set { } // empty setter is not really elegant
    }
}

class SomeClass: SomeProtocol { } // already adopting SomeProtocol

Before I get into how to workaround that, let's briefly focus on why do we even want to have them.

Please note, that in most cases this kind of requirement indicates some sort of design or architectural flaw.

In general, when we want to adopt protocol with property requirement, we add it in main class body (or we define computed property in class extension). It has some downsides though:

Points 1 and 2 are not very problematic - if you intend to fight with Swift just because of that- better stop reading now :)

As for point 3, in most cases you create some base class you do inherit from now on, which usually is perfectly fine. But during my work, I've stumbled upon two situations, where it was insufficient (or at least not fitting well).

Whole concept of adding stored properties is based on Obj-C runtime, which allows to actually do something like:

// MARK: - Helpers - Association
internal func associatedObject<ValueType: AnyObject>(base: AnyObject, key: UnsafePointer<UInt8>, initialiser: () -> ValueType) -> ValueType {
    guard let associated = objc_getAssociatedObject(base, key) as? ValueType else {
        let associated = initialiser()
        objc_setAssociatedObject(base, key, associated,
                                 .OBJC_ASSOCIATION_RETAIN)
        return associated
    }

    return associated
}

internal func optionalAssociatedObject<ValueType: AnyObject>(base: AnyObject, key: UnsafePointer<UInt8>, initialiser: () -> ValueType?) -> ValueType? {
    guard let associated = objc_getAssociatedObject(base, key) as? ValueType else {
        let associated = initialiser()
        if associated != nil {
            objc_setAssociatedObject(base, key, associated!,.OBJC_ASSOCIATION_RETAIN)
        }

        return associated
    }

    return associated
}

internal func associateObject<ValueType: AnyObject>(base: AnyObject, key: UnsafePointer<UInt8>, value: ValueType){
    objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN)
}

internal func associateOptionalObject<ValueType: AnyObject>(base: AnyObject, key: UnsafePointer<UInt8>, value: ValueType?){
    objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN)
}

Case study 1 - tracking ids for all UI controls

For one of the projects, client insisted on registering every ui action in analytics tool. That have lead to a problem, how (and where) to define tracking identifiers for every ui component. The most problematic were system components (text fields, buttons, segmented control etc). The solution had to be generic enough, to not to require to produce too much additional code and logic, just for tracking.

For that particular case, following protocol was proposed:

fileprivate var referenceKey: UInt8 = 10

public extension UIView {
    @IBInspectable public var trackingID: String {
        get {
            return associatedObject(base: self, key: &referenceKey, initialiser: { () -> String in
                return ""
            })
        }
        set {
            associateObject(base: self, key: &referenceKey, value: newValue)
        }
    }
}

That allows to set trackingID for all elements directly on storyboard, allowing to produce more generic analytics code.

Case study 2 - handling selection in SpriteKit and SceneKit

When preparing SpriteKit scene, one of the things you want to handle is responding to user selection. In general, you can do something like:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  guard let touch = touches.first else {
      return
  }

  let location = touch.location(in: self)
  let touchedNode = self.atPoint(location)
  // ... resolve domain action based on what type / what instance you've got
}  

Please note, that while it is quite sufficient for simple scenes, it flaws in more complex ones.

First of all, there is not always 1 to 1 match between node and domain model. In case of nodes, that are composed of other nodes it returns nested one! It can still be managed by searching recursively in parent, but fails when they are not in the same tree.

For that particular case, following protocol was proposed:

/// Object adopting selectable allows to set selected state
public protocol Selectable: class {
    var isSelectable: Bool { get }
    var selected: SelectionState { get set }
}

protocol SelectableNode: Selectable {
    var reference: Selectable? { get set }
}

extension SelectableNode {
    public var isSelectable: Bool {
        return reference?.isSelectable ?? false
    }
    public var selected: SelectionState {
        get { return reference?.selected ?? .none }
        set { reference?.selected = newValue }
    }
}

We specify there a Selectable protocol, to handle selection (by interacting with selection state). Adopting SelectableNode protocol allows to pass that selection stuff to some other instance, and so handling it only in places where it matters.

Usually, it will require now to define some kind of base class, that adopts SelectableNode protocol, and then inheriting it in our every custom node. But for SpriteKit, we will have to create base nodes for (naming only few):

Much easier solution seems to be adding protocol conformance to one class - SKNode directly:

// SKNode+SelectableNode.swift

fileprivate var referenceKey: UInt8 = 10

extension SKNode: SelectableNode {
  var reference: Selectable? {
      get { return (self.referenceObject as? Selectable) ?? parent.reference }
      set { referenceObject = newValue }
  }
  var referenceObject: AnyObject? {
      get {
          return optionalAssociatedObject(base: self, key: &referenceKey, initialiser: { () -> AnyObject? in
              return nil
          })
      }
      set {
          guard newValue is Selectable else { return }
          associateOptionalObject(base: self, key: &referenceKey, value: newValue)
      }
  }
}