Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all 4373 articles
Browse latest View live

ARC and Memory Management in Swift

$
0
0

ARC-Memory-Swift-feature

As a modern, high-level programming language, Swift handles much of the memory management of your apps and allocates or deallocates memory on your behalf. It does so using a neat technology called Automatic Reference Counting, or ARC. In this tutorial, you’ll learn all about ARC and memory management in Swift.

With an understanding of this system, you can influence when the life of a heap object ends. Swift uses ARC to be extremely predictable and efficient in resource constrained environments — like iOS.

Because ARC is “automatic”, you don’t have explicitly participate in the reference counting of objects. You do, however, need to consider relationships between objects to avoid memory leaks. This is a very important requirement often overlooked by new developers.

In this tutorial, you’ll level up your Swift and ARC skills by learning the following:

  • How ARC works.
  • What reference cycles are and how to break them.
  • An example of a reference cycle in practice, and how to detect one with the latest Xcode visualization tools.
  • How to deal with mixed value and reference types.

Getting Started

Open Xcode and click File\New\Playground…. Select the iOS Platform, name it MemoryManagement and then click Next. Save it wherever you wish and then remove the boilerplate code and save the playground.

Next, add the following code to your playground:

class User {
  var name: String
 
  init(name: String) {
    self.name = name
    print("User \(name) is initialized")
  }
 
  deinit {
    print("User \(name) is being deallocated")
  }
}
 
let user1 = User(name: "John")

This defines a class called User and creates an instance of it. The class User has one property, name, an init method (called just after memory allocation) and a deinit method (called just prior to deallocation). The print statements let you see what is happening.

You will notice that the playground shows "User John is initialized\n" in the sidebar, on the print statement within init. However, you will notice that the print statement within deinit is never called. This means that the object is never deinitialized, which means it’s never deallocated. That’s because the scope in which it was initialized is never closed — the playground itself never goes out of scope — so the object is not removed from memory.

Change the let user1 initialization by wrapping it in a do like so:

do {
  let user1 = User(name: "John")
}

This creates a scope around the initialization of the user1 object. At the end of the scope, we would expect user1 to be deallocated.

Now you see both print statements that correspond to initialization and deinitialization in the sidebar. This shows the object is being deinitialized at the end of the scope, just before it’s removed from memory.

The lifetime of a Swift object consists of five stages:

  1. Allocation (memory taken from stack or heap)
  2. Initialization (init code runs)
  3. Usage (the object is used)
  4. Deinitialization (deinit code runs)
  5. Deallocation (memory returned to stack or heap)

While there are no direct hooks into allocation and deallocation, you can use print statements in init and deinit as a proxy for monitoring those processes. Sometimes “deallocate” and “deinit” are used interchangeably, but they are actually two distinct stages in an object’s lifetime.

Reference counting is the mechanism by which an object is deallocated when it is no longer required. The question here is “when can you be sure an object won’t be needed in the future?” Reference counting manages this by keeping a usage count, also known as the reference count, inside each object instance.

This count indicates how many “things” reference the object. When the reference count of an objects hits zero no clients of the object remain, so the object deinitializes and deallocates.

SchemeOne

When you initialize the User object, it starts with a reference count of 1 since the constant user1 references that object. At the end of the do block, user1 goes out of scope, the count decrements, and the reference count decrements to zero. As a result, user1 deinitializes and subsequently deallocates.

Reference cycles

In most cases, ARC works like a charm; as a developer, you don’t usually have to worry about memory leaks, where unused objects stay alive indefinitely.

But it’s not all plain sailing! Leaks can happen!

How can leaks happen? Imagine a situation where two objects are no longer required, but each reference one another. Since each has a non-zero reference count, deallocation, of both objects, can never occur.

ReferenceCycle

This is called a strong reference cycle. It fools ARC and prevents it from cleaning up. As you can see, the reference count at the end is not zero, and thus object1 and object2 are never deallocated even though they’re no longer required.

To see this in action, add the following code after the User class but before the existing do block:

class Phone {
  let model: String
  var owner: User?
 
  init(model: String) {
    self.model = model
    print("Phone \(model) is initialized")
  }
 
  deinit {
    print("Phone \(model) is being deallocated")
  }
}

Then change the do block to look like this:

do {
  let user1 = User(name: "John")
  let iPhone = Phone(model: "iPhone 6s Plus")
}

This adds a new class called Phone, and creates an instance of this new class.

This new class is rather simple: two properties, one for the model and one for the owner, an init and a deinit method. The owner property is optional, since a Phone can exist without a User.

Next, add the following code to the User class, immediately after the name property:

private(set) var phones: [Phone] = []
func add(phone: Phone) {
  phones.append(phone)
  phone.owner = self
}

This adds a phones array property to hold all phones owned by a user. The setter is made private so that clients are forced to use add(phone:). This method ensures that owner is set properly when you add it.

Currently, as you can see in the sidebar, both Phone and User objects deallocate as expected.

But now make the do block look like this:

do {
  let user1 = User(name: "John")
  let iPhone = Phone(model: "iPhone 6s Plus")
  user1.add(phone: iPhone)
}

Here, you add iPhone to user1. This automatically sets the owner property of iPhone to user1. A strong reference cycle between the two objects prevents ARC from deallocating them. As a result, both user1 and iPhone never deallocate.

UserIphoneCycle

Weak References

To break strong reference cycles, you can specify the relationship between reference counted objects as weak. Unless otherwise specified, all references are strong. Weak references, by contrast, don’t increase the strong reference count of the object.

In other words, weak references don’t participate in the lifecycle management of the object. Additionally, weak references are always declared as optional types. This means when the reference count goes to zero, the reference can automatically be set to nil.

WeakReference

In the image above, the dashed arrow represents a weak reference. Notice how the reference count of object1 is 1 because variable1 refers to it. The reference count of object2 is 2, because both variable2 and object1 refer to it. While object2 references object1, it does so weakly, meaning it doesn’t affect the strong reference count of object1.

When both variable1 and variable2 go away, object1 will be left with a reference count of zero and deinit will be called. This removes the strong reference to object2 which then subsequently deinitializes.

Back in your playground, break the UserPhone reference cycle by making the owner reference weak as shown below:

class Phone {
  weak var owner: User?
  // other code...
}

UserIphoneCycleWeaked

Now user1 and iPhone deallocate properly at the end of the do block as you can see in the results sidebar.

Unowned References

There is another reference modifier you can use that doesn’t increase the reference count: unowned.

What’s the difference between unowned and weak? A weak reference is always optional and automatically becomes nil when the referenced object deinitializes. That’s why you must define weak properties as optional var types for your code to compile (because the variable needs to change).

Unowned references, by contrast, are never optional types. If you try to access an unowned property that refers to a deinitialized object, you will trigger a runtime error comparable to force unwrapping a nil optional type.

Table

Time to get some practice using unowned. Add a new class CarrierSubscription before the do block as shown:

class CarrierSubscription {
  let name: String
  let countryCode: String
  let number: String
  let user: User
 
  init(name: String, countryCode: String, number: String, user: User) {
    self.name = name
    self.countryCode = countryCode
    self.number = number
    self.user = user
 
    print("CarrierSubscription \(name) is initialized")
  }
 
  deinit {
    print("CarrierSubscription \(name) is being deallocated")
  }
}

CarrierSubscription has four properties: the name of the subscription, the countryCode and phone number, and a reference to a User object.

Next, add the following to the User class, after the nameproperty:

var subscriptions: [CarrierSubscription] = []

This adds a subscriptions property, which will hold an array of CarrierSubscrition objects.

Also, add the following to the top of the Phone class, after the owner property:

var carrierSubscription: CarrierSubscription?
 
func provision(carrierSubscription: CarrierSubscription) {
  self.carrierSubscription = carrierSubscription
}
 
func decommission() {
  self.carrierSubscription = nil
}

This adds an optional CarrierSubscription property and two new functions to provision and decommission a carrier subscription on the phone.

Next, add the following to init inside CarrierSubscription, just before the print statement:

user.subscriptions.append(self)

This ensures that this CarrierSubscription is added to the user’s array of subscriptions.

Finally, change the do block to look like this:

do {
  let user1 = User(name: "John")
  let iPhone = Phone(model: "iPhone 6s Plus")
  user1.add(phone: iPhone)
  let subscription1 = CarrierSubscription(name: "TelBel", countryCode: "0032", number: "31415926", user: user1)
  iPhone.provision(carrierSubscription: subscription1)
}

Notice what is printed in the results sidebar. Again you see a reference cycle: neither user1, iPhone or subscription1 are deallocated at the end. Can you find where the issue is now?

UserIphoneSubCycle

Either the reference from user1 to subscription1 or the reference from subscription1 to user1 should be unowned to break the cycle. The question is which of the two to choose. This is where a little bit of knowledge of your domain helps.

A user owns a carrier subscription, and (contrary to what carriers may think!), the carrier subscription does not own the user. Moreover, it doesn’t make sense for a CarrierSubscription to exist without an owning User. This is why you declared it as an immutable let property in the first place.

Since a User with no CarrierSubscription can exist, but no CarrierSubscription can exist without a User, the user reference should be unowned.

Add unowned to the user property of CarrierSubscription like so:

class CarrierSubscription {
  let name: String
  let countryCode: String
  let number: String
  unowned let user: User
  // Other code...
}

This breaks the reference cycle and lets every object deallocate.

UserIphoneCycleSubSolve

Reference Cycles with Closures

Reference cycles for objects occur when properties reference each other. Like objects, closures are also reference types and can cause cycles. Closures capture (or close over) the objects that they operate on.

For example, if a closure is assigned to the property of a class, and that closure uses instance properties of that same class, you have a reference cycle. In other words, the object holds a reference to the closure via a stored property; the closure holds a reference to the object via the captured value of self.

Closure Reference

Add the following to CarrierSubscription, just after the user property:

lazy var completePhoneNumber: () -> String = {
  self.countryCode + " " + self.number
}

This closure computes and returns a complete phone number. The property is declared with lazy, meaning that it will not be assigned until it’s used the first time. This is required because it’s using self.countryCode and self.number, which aren’t available until after the initializer runs.

Add the following line at the end of the do block:

print(subscription1.completePhoneNumber())

You will notice that user1 and iPhone deallocate, but not CarrierSubscription due to the strong reference cycle between the object and the closure.

Closure cycle

Swift has a simple, elegant way to break strong reference cycles in closures. You declare a capture list in which you define the relationships between the closure and the objects it captures.

To illustrate how the capture list works, consider the following code:

var x = 5
var y = 5
 
let someClosure = { [x] in
  print("\(x), \(y)")
}
 
x = 6
y = 6
 
someClosure()        // Prints 5, 6
print("\(x), \(y)")  // Prints 6, 6

The variable x is in the capture list, so a copy of x is made at the point the closure is defined. It is said to be captured by value. On the other hand, y is not in the capture list, and is instead captured by reference. This means that when the closure runs, y will be whatever it is at that point, rather than at the point of capture.

Capture lists come in handy for defining a weak or unowned relationship between objects used in a closure. In this case, unowned is a good fit since the closure could not exist while the instance of CarrierSubscription has gone away.

Change the completePhoneNumber closure in CarrierSubscription to look like this:

lazy var completePhoneNumber: () -> String = {
  [unowned self] in
  return self.countryCode + " " + self.number
}

This adds [unowned self] to the capture list for the closure. It means that self is captured as an unowned reference instead of a strong reference.

This solves the reference cycle. Hooray!

The syntax used here is actually a shorthand for a longer capture syntax, which introduces a new identifier. Consider the longer form:

var closure = {
  [unowned newID = self] in
  // Use unowned newID here...
}

Here, newID is an unowned copy of self. Outside the closure’s scope, self keeps its original meaning. In the short form, which you used above, a new self variable is created which shadows the existing self variable just for the closure’s scope.

In your code, the relationship between self and the completePhoneNumber closure is unowned. If you are sure that a referenced object from a closure will never deallocate, you can use unowned. If it does deallocate, you are in trouble.

Add the following code to the end of the playground:

// A class that generates WWDC Hello greetings.  See http://wwdcwall.com
class WWDCGreeting {
  let who: String
 
  init(who: String) {
    self.who = who
  }
 
  lazy var greetingMaker: () -> String = {
    [unowned self] in
    return "Hello \(self.who)."
  }
}
 
let greetingMaker: () -> String
 
do {
  let mermaid = WWDCGreeting(who: "caffinated mermaid")
  greetingMaker = mermaid.greetingMaker
}
 
greetingMaker() // TRAP!

The playground hits a runtime exception because the closure expects self.who to still be valid, but it was deallocated when mermaid went out of scope. This example may seem contrived, but it can easily happen in real life such as when you use closures to run something much later, such as after an asynchronous network call has finished.

Change the greetingMaker variable in WWDCGreeting to look like this:

lazy var greetingMaker: () -> String = {
  [weak self] in
  return "Hello \(self?.who)."
}

There are two changes you made to the original greetingMaker closure. First, you replaced unowned with weak. Second, since self became weak, you needed to access the who property with self?.who.

The playground no longer crashes, but you get a curious result in the sidebar: "Hello, nil.". Perhaps this is acceptable, but more often you want to do something completely different if the object is gone. Swift’s guard let makes this easy.

Re-write the closure one last time, to look like this:

lazy var greetingMaker: () -> String = {
  [weak self] in
  guard let strongSelf = self else {
    return "No greeting available."
  }
  return "Hello \(strongSelf.who)."
}

The guard statement binds a new variable strongSelf from weak self. If self is nil, the closure returns "No greeting available.". On the other hand, if self is not nil, strongSelf makes a strong reference, so the object is guaranteed to live until the end of the closure.

This idiom, sometimes referred to as the strong-weak dance, is part of the Ray Wenderlich Swift Style Guide since it’s a robust pattern for handling this behavior in closures.

testskillz

Finding Reference Cycles in Xcode 8

Now that you understand the principles of ARC, what reference cycles are and how to break them, it’s time to look at a real world example.

Download the starter project and open it in Xcode 8. It needs to be Xcode 8 (or newer) because Xcode 8 added some fun new features that you’re going to use.

Build and run the project, and you’ll see the following:

ss5

This is a simple contacts app. Feel free to tap on a contact to get more information or add contacts using the + button on the top right.

Have a look at the code:

  • ContactsTableViewController: shows all the Contact objects from the database.
  • DetailViewController: shows the details for a certain Contact object.
  • NewContactViewController<: allows the user to add a new contact.
  • ContactTableViewCell: a custom table view cell to show the details of a Contact object.
  • Contact: the model for a contact in the database.
  • Number: the model for a phone number.

There is, however, something horribly wrong with the project: buried in there is a reference cycle. Your user won’t notice the issue for quite some time since the leaking objects are small — and their size makes it even harder to trace. Luckily, Xcode 8 has a new built-in tool to help you find even the smallest leaks.

Build and run the app again. Delete three or four contacts by swiping their cells to the left and tapping delete. They appear the have disappeared completely, right?

ss1

While the app is still running, move over to the bottom of Xcode and click the Debug Memory Graph button:

ss2

Observe the new kind of issues (warnings, errors, etc) in Xcode 8: Runtime Issues. They look like a purple square with a white exclamation mark inside, such as the one shown selected in this screenshot:

ss3

In the Navigator, select one of the problematic Contact objects. The cycle is clearly visible: Contact and Number objects keep each other alive by referencing one another.

4

These type of graphs are a sign for you to look into your code. Consider that a Contact can exist without a Number, but a Number should not exist without a Contact. How would you solve the cycle? Should the reference from Contact to Number or the one from Number to Contact be weak or unowned?

Give it your best shot first, then take a look below if you need help!

Solution Inside SelectShow>

Bonus: Cycles with Value Types and Reference Types

Swift types can be categorized as reference types, like classes, and value types, like structures or enumerations. The major difference is that value types are copied when they are passed around, whereas reference types share a single copy of referenced information.

Does this mean that you can't have cycles with value types? Yes: everything is copied with value types, and thus no cyclical relationships can exist since no real references are created. You need at least two references to make a cycle.

Back in your playground, add the following at the end:

struct Node { // Error
  var payload = 0
  var next: Node? = nil
}

Hmm, the compiler's not happy. A struct (value type) cannot be recursive or use an instance of itself. Otherwise, a struct of this type would have an infinite size. Change it to a class like so:

class Node {
  var payload = 0
  var next: Node? = nil
}

Self reference is not an issue for classes (i.e. reference types), so the compiler error disappears.

Now, add this to your playground:

class Person {
  var name: String
  var friends: [Person] = []
  init(name: String) {
    self.name = name
    print("New person instance: \(name)")
  }
 
  deinit {
    print("Person instance \(name) is being deallocated")
  }
}
 
do {
  let ernie = Person(name: "Ernie")
  let bert = Person(name: "Bert")
 
  ernie.friends.append(bert) // Not deallocated
  bert.friends.append(ernie) // Not deallocated
}

Here’s an example of a mixture of value types and reference types that form a reference cycle.

ernie and bert stay alive by keeping a reference to each other in their friends array, although the array itself is a value type. Make the array unowned; Xcode will show an error: unowned only applies to class types.

To break the cycle here, you’ll have to create a generic wrapper object and use it to add instances to the array. If you don’t know what generics are or how to use them, check out the Introduction to Generics tutorial on this site.

Add the following above the definition of the Person class:

class Unowned<T: AnyObject> {
  unowned var value: T
  init (_ value: T) {
    self.value = value
  }
}

Then, change the definition of the friends property in Person like so:

var friends: [Unowned<Person>] = []

And finally, change the do block to look like this:

do {
  let ernie = Person(name: "Ernie")
  let bert = Person(name: "Bert")
 
  ernie.friends.append(Unowned(bert))
  bert.friends.append(Unowned(ernie))
}

ernie and bert now deallocate happily!

The friends array isn’t a collection of Person objects anymore, but instead a collection of Unowned objects that serve as a wrapper for the Person instances.

To access the Person object within Unowned use the value property, like so:

let firstFriend = bert.friends.first?.value // get ernie

Where to Go From Here?

Here’s the download of the complete playground and the Xcode project from this tutorial.

You now have a good understanding of memory management in Swift and know how ARC works. If you want to learn more about the new debug tools in Xcode 8, I suggest watching this WWDC session.

If you want an even more in-depth look at how weak references are implemented in Swift, check out Mike Ash’s blog post Swift Weak References. It covers how weak references in Swift differ from the Objective-C implementation, and how Swift actually keeps two counts under the hood: one for strong references and one for weak references.

Finally, if you are a RW subscriber, check out the iOS 10 Screencast: Memory Graph Debugger. It gives some great tips for getting the most out of the memory visualizer.

What do you think about the ARC approach? Let me know in the comments!

The post ARC and Memory Management in Swift appeared first on Ray Wenderlich.


Views vs. Layers, and Xcode Extensions – Podcast S06 E08

$
0
0
xcode extensions

Want to extend the functionality of Xcode 8? Find out how in the episode!

Join Mic, Jake, and Greg as they revisit the topic of views vs. layers and discuss when one is more appropriate than the other. The panel then move on to talk about the security of Xcode 8, how Apple’s new approach has killed off unofficial third-party plugins, and how they now provide APIs for official Xcode extensions.

[Subscribe in iTunes] [RSS Feed]

Our Sponsor

Hired is the platform for the best iOS developer jobs.

Candidates registered with Hired receive an average of 5 offers on the platform, all from a single application. Companies looking to hire include Facebook, Uber and Stripe.

With Hired, you get job offers and salary and/or equity before you interview, so you don’t have to waste your time interviewing for jobs you might not end up wanting, and it’s totally free to use!

Plus you will receive a $2000 bonus from Hired if you find a job through the platform, just for signing up using the show’s exclusive link.

Interested in sponsoring a podcast episode? We sell ads via Syndicate Ads, check it out!

Show Notes

Contact Us

Where To Go From Here?

We hope you enjoyed this episode of our podcast. Be sure to subscribe in iTunes to get notified when the next episode comes out.

We’d love to hear what you think about the podcast, and any suggestions on what you’d like to hear in future episodes. Feel free to drop a comment here, or email us anytime at podcast@raywenderlich.com.

The post Views vs. Layers, and Xcode Extensions – Podcast S06 E08 appeared first on Ray Wenderlich.

Swift Tutorial Part 2: Types and Operations

$
0
0

part2-feature
Welcome to the second part of this learning Swift mini-series!

This part carries on from Part 1: Expressions, Variables & Constants. We recommend you start with the first part of this tutorial series to get the most out of it.

Now that you know how to perform basic operations and manipulate data using operations, it’s time to learn more about types. Formally, a type describes a set of values and the operations that can be performed them.

In this tutorial, you’ll learn about handling different types, including strings which allow you to represent text. You’ll learn about converting between types, and you’ll also be introduced to type inference which makes your life as a programmer a lot simpler.

Finally, you’ll learn about tuples which allow you to make your own types made up of multiple values of any type.

Getting Started

Sometimes you’ll have data in one format and need to convert it to another. The naïve way to attempt this would be like so:

var integer: Int = 100
var decimal: Double = 12.5
integer = decimal

Swift will complain if you try to do this and spit out an error on the third line:

Cannot assign value of type 'Double' to type 'Int'

Some programming languages aren’t as strict and will perform conversions like this automatically. Experience shows this kind of automatic conversion is the source of software bugs and often hurts performance. Swift disallows you from assigning a value of one type to another and avoids these issues.

Remember, computers rely on us programmers to tell them what to do. In Swift, that includes being explicit about type conversions. If you want the conversion to happen, you have to say so!

Instead of simply assigning, you need to explicitly say that you want to convert the type. You do it like so:

var integer: Int = 100
var decimal: Double = 12.5
integer = Int(decimal)

The assignment on the third line now tells Swift unequivocally that you want to convert from the original type, Double, to the new type, Int.

Note: In this case, assigning the decimal value to the integer results in a loss of precision: The integer variable ends up with the value 12 instead of 12.5. This is why it’s important to be explicit. Swift wants to make sure you know what you’re doing and that you may end up losing data by performing the type conversion.

Operators with Mixed Types

So far, you’ve only seen operators acting independently on integers or doubles. But what if you have an integer that you want to multiply by a double?

You might think you could do it like this:

let hourlyRate: Double = 19.5
let hoursWorked: Int = 10
let totalCost: Double = hourlyRate * hoursWorked

If you try that, you’ll get an error on the final line:

Binary operator '*' cannot be applied to operands of type 'Double' and 'Int'

This is because in Swift, you can’t apply the * operator to mixed types. This rule also applies to the other arithmetic operators. It may seem surprising at first, but Swift is being rather helpful.

Swift forces you to be explicit about what you mean when you want an Int multiplied by a Double, because the result can be only one type. Do you want the result to be an Int, converting the Double to an Int before performing the multiplication? Or do you want the result to be a Double, converting the Int to a Double before performing the multiplication?

In this example, you want the result to be a Double. You don’t want an Int, because in that case, Swift would convert the hourlyRate constant into an Int to perform the multiplication, rounding it down to 19 and losing the precision of the Double.

You need to tell Swift you want it to consider the hoursWorked constant to be a Double, like so:

let hourlyRate: Double = 19.5
let hoursWorked: Int = 10
let totalCost: Double = hourlyRate * Double(hoursWorked)

Now, each of the operands will be a Double when Swift multiplies them, so totalCost is a Double as well.

Type Inference

Up to this point, each time you’ve seen a variable or constant declared it’s been accompanied by an associated type, like this:

let integer: Int = 42
let double: Double = 3.14159

You may be asking yourself why you need to bother writing the : Int and : Double, since the right hand side of the assignment is already an Int or a Double. It’s redundant, to be sure; your crazy-clever brain can see this without too much work.

It turns out the Swift compiler can deduce this as well. It doesn’t need you to tell it the type all the time — it can figure it out on its own. This is done through a process called type inference. Not all programming languages have this, but Swift does, and it’s a key component of Swift’s power as a language.

So, you can simply drop the type in most places where you see one.

For example, consider the following constant declaration:

let typeInferredInt = 42

Sometimes it’s useful to check the inferred type of a variable or constant. You can do this in a playground by holding down the Option key and clicking on the variable or constant’s name. Xcode will display a popover like this:

type_inferred_int

Xcode tells you the inferred type by giving you the declaration you would have had to use if there were no type inference. In this case, the type is Int.

It works for other types, too:

let typeInferredDouble = 3.14159

Option-clicking on this reveals the following:

type_inferred_double

You can see from this that type inference isn’t magic. Swift is simply doing what your brain does very easily. Programming languages that don’t use type inference can often feel verbose, because you need to specify the often obvious type each time you declare a variable or constant.

Sometimes you want to define a constant or variable and ensure it’s a certain type, even though what you’re assigning to it is a different type. You saw earlier how you can convert from one type to another. For example, consider the following:

let wantADouble = 3

Here, Swift infers the type of wantADouble as Int. But what if you wanted Double instead?

The first thing you could do is the following:

let actuallyDouble = Double(3)

This is like you saw before with type conversion.

Another option would be to not use type inference at all and do the following:

let actuallyDouble: Double = 3

There is a third option, like so:

let actuallyDouble = 3 as Double

This uses a new keyword you haven’t seen before, as, which also performs a type conversion.

Note: Literal values like 3 don’t have a type. It’s only when using them in an expression or assigning them to a constant or variable that Swift infers a type for them.

A literal number value that doesn’t contain a decimal point can be used as an Int as well as a Double. This is why you’re allowed to assign the value 3 to constant actuallyDouble.

Literal number values that do contain a decimal point cannot be integers. This means we could have avoided this entire discussion had we started with

let wantADouble = 3.0

Sorry! :]

Mini-exercises

  1. Create a constant called age1 and set it equal to 42. Create a constant called age2 and set it equal to 21. Check using Option-click that the type for both has been inferred correctly as Int.
  2. Create a constant called avg1 and set it equal to the average of age1 and age2 using the naïve operation (age1 + age2) / 2. Use Option-click to check the type and check the result of avg1. Why is it wrong?
  3. Correct the mistake in the above exercise by casting age1 and age2 to Double in the formula. Use Option-click to check the type and check the result of avg1. Why is it now correct?

Strings

Numbers are essential in programming, but they aren’t the only type of data you need to work with in your apps. Text is also an extremely common data type, such as people’s names, their addresses, or even the words of a book. All of these are examples of text that an app might need to handle.

Most computer programming languages store text in a data type called a string. This part introduces you to strings, first by giving you background on the concept of strings and then by showing you how to use them in Swift.

How Computers Represent Strings

Computers think of strings as a collection of individual characters. All code, in whatever programming language, can be reduced to raw numbers. Strings are no different!

That may sound very strange. How can characters be numbers? At its base, a computer needs to be able to translate a character into the computer’s own language, and it does so by assigning each character a different number. This forms a two-way mapping from character to number that is called a character set.

unicode_comic

When you press a character key on your keyboard, you are actually communicating the number of the character to the computer. Your word processor application converts that number into a picture of the character and finally, presents that picture to you.

Unicode

In isolation, a computer is free to choose whatever character set mapping it likes. If the computer wants the letter a to equal the number 10, then so be it. But when computers start talking to each other, they need to use a common character set. If two computers used different character sets, then when one computer transferred a string to the other, they would end up thinking the strings contained different characters.

There have been several standards over the years, but the most modern standard is Unicode. It defines the character set mapping that almost all computers use today.

Note: You can read more about Unicode at its official website, http://unicode.org/.

As an example, consider the word cafe. The Unicode standard tells us that the letters of this word should be mapped to numbers like so:

cafe

The number associated with each character is called a code point. So in the example above, c uses code point 99, a uses code point 97, and so on.

Of course, Unicode is not just for the simple Latin characters used in English, such as c, a, f and e. It also lets you map characters from languages around the world. The word cafe, as you’re probably aware, is derived from French, in which it’s written as café. Unicode maps these characters like so:

cafe_with_accent

And here’s an example using Chinese characters (this, according to Google translate, means “Computer Programming”):

computer_programming_chinese

You’ve probably heard of emojis, which are small pictures you can use in your text. These pictures are, in fact, just normal characters and are also mapped by Unicode. For example:

poo_face

This is only two characters. The code points for these are very large numbers, but each is still only a single code point. The computer considers these as no different than any other two characters.

Note: The word “emoji” comes from Japanese, where “e” means picture and “moji” means character.

Strings in Swift

Swift, like any good programming language, can work directly with characters and strings. It does so through the data types Character and String, respectively. In this section, you’ll learn about these data types and how to work with them.

Characters and Strings

The Character data type can store a single character. For example:

let characterA: Character = "a"

This stores the character a. It can hold any character — even an emoji:

let characterDog: Character = "&#x1f436;"

But this data type is designed to hold only single characters. The String data type, on the other hand, stores multiple characters. For example:

let stringDog: String = "Dog"

It’s as simple as that! The right-hand side of this expression is what’s known as a string literal; it’s the Swift syntax for representing a string.

Of course, type inference applies here as well. If you remove the type in the above declaration, then Swift does the right thing and makes the stringDog a String constant:

let stringDog = "Dog" // Inferred to be of type String
Note: There’s no such thing as a character literal in Swift. A character is simply a string of length one. However, Swift infers the type of any string literal to be String, so if you want a Character instead, you must make the type explicit.

Concatenation

You can do much more than create simple strings. Sometimes you need to manipulate a string, and one common way to do so is to combine it with another string.

In Swift, you do this in a rather simple way: by using the addition operator. Just as you can add numbers, you can add strings:

var message = "Hello" + " my name is "
let name = "Matt"
message += name // "Hello my name is Matt"

You need to declare message as a variable rather than a constant because you want to modify it. You can add string literals together, as in the first line, and you can add string variables or constants together, as in the last line.

It’s also possible to add characters to a string. However, Swift’s strictness with types means you have to be explicit when doing so, just as you have to be when you work with numbers if one is an Int and the other is a Double.

To add a character to a string, you do this:

let exclamationMark: Character = "!"
message += String(exclamationMark) // "Hello my name is Matt!"

With this code, you explicitly convert the Character to a String before you add it to message.

Interpolation

You can also build up a string by using interpolation, which is a special Swift syntax that lets you build a string in a way that’s easy to read:

let name = "Matt"
let message = "Hello my name is \(name)!" // "Hello my name is Matt!"

As I’m sure you’ll agree, this is much more readable than the example from the previous section. It’s an extension of the string literal syntax, whereby you replace certain parts of the string with other values. You enclose the value you want to give the string in parentheses preceded by a backslash.

This syntax works in just the same way to build a string from other data types, such as numbers:

let oneThird = 1.0 / 3.0
let oneThirdLongString = "One third is \(oneThird) as a decimal."

Here, you use a Double in the interpolation. At the end of this code, your oneThirdLongString constant will contain the following:

One third is 0.3333333333333333 as a decimal.

Of course, it would actually take infinite characters to represent one third as a decimal, because it’s a repeating decimal. String interpolation with a Double gives you no way to control the precision of the resulting string.

This is an unfortunate consequence of using string interpolation; it’s simple to use, but offers no ability to customize the output.

Mini-exercises

  1. Create a string constant called firstName and initialize it to your first name. Also create a string constant called lastName and initialize it to your last name.
  2. Create a string constant called fullName by adding the firstName and lastName constants together, separated by a space.
  3. Using interpolation, create a string constant called myDetails that uses the fullName constant to create a string introducing yourself. For example, my string would read: "Hello, my name is Matt Galloway.".

Tuples

Sometimes data comes in pairs or triplets. An example of this is a pair of (x, y) coordinates on a 2D grid. Similarly, a set of coordinates on a 3D grid is comprised of an x-value, a y-value and a z-value.

In Swift, you can represent such related data in a very simple way through the use of a tuple.

A tuple is a type that represents data composed of more than one value of any type. You can have as many values in your tuple as you like. For example, you can define a pair of 2D coordinates where each axis value is an integer, like so:

let coordinates: (Int, Int) = (2, 3)

The type of coordinates is a tuple containing two Int values. The types of the values within the tuple, in this case Int, are separated by commas surrounded by parentheses. The code for creating the tuple is much the same, with each value separated by commas and surrounded by parentheses.

Type inference can infer tuple types too:

let coordinates = (2, 3)

You could similarly create a tuple of Double values, like so:

let coordinates = (2.1, 3.5)
// Inferred to be of type (Double, Double)

Or you could mix and match the types comprising the tuple, like so:

let coordinates = (2.1, 3)
// Inferred to be of type (Double, Int)

And here’s how to access the data inside a tuple:

let coordinates = (2, 3)
let x = coordinates.0
let y = coordinates.1

You can reference each item in the tuple by its position in the tuple, starting with zero. So in this example, x will equal 2 and y will equal 3.

Note: Starting with zero is a common practice in computer programming and is called zero indexing.

In the previous example, it may not be immediately obvious that the first value, at index 0, is the x-coordinate and the second value, at index 1, is the y-coordinate. This is another demonstration of why it’s important to always name your variables in a way that avoids confusion.

Fortunately, Swift allows you to name the individual parts of a tuple and you can be explicit about what each part represents. For example:

let coordinatesNamed = (x: 2, y: 3)
// Inferred to be of type (x: Int, y: Int)

Here, the code annotates the values of coordinatesNamed to contain a label for each part of the tuple.

Then, when you need to access each part of the tuple, you can access it by its name:

let x = coordinatesNamed.x
let y = coordinatesNamed.y

This is much clearer and easier to understand. More often than not, it’s helpful to name the components of your tuples.

If you want to access multiple parts of the tuple at the same time, as in the examples above, you can also use a shorthand syntax to make it easier:

let coordinates3D = (x: 2, y: 3, z: 1)
let (x, y, z) = coordinates3D

This declares three new constants, x, y and z, and assigns each part of the tuple to them in turn. The code is equivalent to the following:

let coordinates3D = (x: 2, y: 3, z: 1)
let x = coordinates3D.x
let y = coordinates3D.y
let z = coordinates3D.z

If you want to ignore a certain element of the tuple, you can replace the corresponding part of the declaration with an underscore. For example, if you were performing a 2D calculation and wanted to ignore the z-coordinate of coordinates3D, then you’d write the following:

let (x, y, _) = coordinates3D

This line of code only declares x and y. The _ is special and simply means you’re ignoring this part for now.

Note: You’ll find that you can use the underscore throughout Swift to ignore a value.

Mini-exercises

  1. Declare a constant tuple that contains three Int values followed by a Double. Use this to represent a date (month, day, year) followed by an average temperature for that date.
  2. Change the tuple to name the constituent components. Give them names related to the data that they contain: month, day, year and averageTemperature.
  3. In one line, read the day and average temperature values into two constants. You’ll need to employ the underscore to ignore the month and year.
  4. Up until now, you’ve only seen constant tuples. But you can create variable tuples, too. Change the tuple you created in the exercises above to a variable by using var instead of let. Now change the average temperature to a new value.

A Whole Lot of Number Types

You’ve been using Int to represent whole numbers. An Int is represented with 64-bits on most modern hardware and with 32-bits on older, or more resource constrained systems. Swift provides many more number types that use different amounts of storage. For whole numbers, you can use the explicit signed types Int8, Int16, Int32, Int64. These types consume 1, 2, 4, and 8 bytes of storage respectively. Each of these types use 1-bit to represent the sign.

If you are only dealing with non-negative values there are a set of explicit unsigned types that you can use. These include UInt8, UInt16, UInt32 and UInt64. While you cannot represent negative values with these the extra 1-bit lets you represent values that are twice as big as their signed counterparts.

Here is a summary of the different integer types and their storage characteristics. Most of the time you will just want to use an Int. These become useful if your code is interacting with another piece of software that uses one of these more exact sizes or if you need to optimize for storage size.

int_sizes

You’ve been using Double to represent fractional numbers. Swift offers a Float type which has less range and precision than Double but requires half as much storage. Modern hardware has been optimized for Double so it is the one that you should reach for unless you have good reason not to.

float_sizes

Most of the time you will just use Int and Double to represent numbers but every once in a while you might encounter the other types. You already know how to deal with them. For example, suppose you need to add together an Int16 with a UInt8 and an Int32. You can do that like so:

let a: Int16 = 12
let b: UInt8 = 255
let c: Int32 = -100000
 
let answer = Int(a) + Int(b) + Int(c)  // answer is an Int

A Peek Behind the Curtains: Protocols

Even though there are a dozen different numeric types, they are pretty easy to understand and use. This is because they all roughly support the same operations. In other words, once you know how to use an Int, using any one of the flavors is very straight-forward.

One of the truly great features of Swift is how it formalizes the idea of type commonality using what are called protocols. By learning a protocol you instantly understand how an entire family of types that use that protocol work.

In the case of integers, the functionality can be diagrammed like so:

int_protocols

The arrows indicate conformance to (sometimes called adoption of) a protocol. While this graph does not show all of the protocols that integer types conform to, it gives you insight about how things are organized.

Swift is the first protocol-based language. As you begin to understand the protocols that underly the types, you can start leveraging the system in ways not possible with other languages. By the end of this book you will be hooking into existing protocols and even creating new ones of your own.

Key points

  • Type casting allows you to convert variables of one type into another.
  • Type casting is required when using an operator, such as the basic arithmetic operators (+, -, *, /), with mixed types.
  • Type inference allows you to omit the type when Swift already knows it.
  • Unicode is the standard for mapping characters to numbers.
  • A single mapping in Unicode is called a code point.
  • The Character data type stores single characters. The String data type stores collections of characters, or strings.
  • You can combine strings by using the addition operator.
  • You can use string interpolation to build a string in-place.
  • You can use tuples to group data into a single data type.
  • Tuples can either be unnamed or named. Their elements are accessed with index numbers for unnamed tuples, or programmer given names for named tuples.
  • There are many kinds of numeric types with different storage and precision capabilities
  • Protocols are how types are organized in Swift

Give the following challenges a try to solidify your knowledge and you’ll be ready to move on!

Challenges

  1. Create a constant called coordinates and assign a tuple containing two and three to it.
  2. Create a constant called namedCoordinate with a row and column component.
  3. Which of the following are valid statements?
let character: Character = "Dog"
let character: Character = "&#x1f436;"
let string: String = "Dog"
let string: String = "&#x1f436;"
  1. Is this valid code?
let tuple = (day: 15, month: 8, year: 2015)
let day = tuple.Day
  1. What is wrong with the following code?
let name = "Matt"
name += " Galloway"
  1. What is the type of the constant called value?
let tuple = (100, 1.5, 10)
let value = tuple.1
  1. What is the value of the constant called month?
let tuple = (day: 15, month: 8, year: 2015)
let month = tuple.month
  1. What is the value of the constant called summary?
let number = 10
let multiplier = 5
let summary = "\(number) multiplied by \(multiplier) equals \(number * multiplier)"
  1. What is the sum of a, b minus c?
let a = 4
let b: Int32 = 100
let c: UInt8 = 12
  1. What is the numeric difference between Double.pi and Float.pi?

Where to Go From Here?

Types are a fundamental part of programming. They’re what allow you to correctly store your data. You’ve seen a few more here, including strings and tuples as well as a bunch of numeric types. Later on you’ll learn how to define your own types with structs, enums and classes.

You can download the final playgrounds and challenges from this tutorial here.

In the next part, you’ll learn about Boolean logic and simple control flow. This is required for any program to be able to make decisions about how the program should proceed based on the data it’s manipulating. Once it’s posted we’ll provide a link right here so you can continue with your Swift adventures!

We encourage you to try the challenges above; if you find a particularly interesting solution to one of the challenges, or have any questions or comments on this tutorial, please tell us about it in the discussion below!

swift-apprentice

This tutorial was taken from Chapter 3 of Swift Apprentice, Second Edition available from the raywenderlich.com store.

The book has been completely updated to work with Xcode 8 and Swift 3. Check it out and let us know what you think!

The post Swift Tutorial Part 2: Types and Operations appeared first on Ray Wenderlich.

Screencast: Beginning C# Part 11: Switch Statements

Swift Tutorial Part 3: Flow Control

$
0
0

part3-feature

Welcome to the third and final part of this Swift mini-series! If you haven’t read through Part 1 or Part 2 of this series, we suggest you do that first.

When writing a computer program, you need to be able to tell the computer what to do in different scenarios. For example, a calculator app would need to do one thing if the user tapped the addition button and another thing if the user tapped the subtraction button.

In computer-programming terms, this concept is known as control flow. It is so named because the flow of the program is controlled by various methods.

In this tutorial, you’ll learn how to make decisions and repeat tasks in your programs by using syntax to control the flow. You’ll also learn about Booleans, which represent true and false values, and how you can use these to compare data.

Getting Started

You’ve seen a few types now, such as Int, Double and String. Here you’ll learn about another type, one that will let you compare values through the comparison operators.

When you perform a comparison, such as looking for the greater of two numbers, the answer is either true or false. Swift has a data type just for this! It’s called a Bool, which is short for Boolean, after a rather clever man named George Boole who invented an entire field of mathematics around the concept of true and false.

This is how you use a Boolean in Swift:

let yes: Bool = true
let no: Bool = false

And because of Swift’s type inference, you can leave off the type:

let yes = true
let no = false

A Boolean can only be either true or false, denoted by the keywords true and false. In the code above, you use the keywords to set the initial state of each constant.

Boolean Operators

Booleans are commonly used to compare values. For example, you may have two values and you want to know if they’re equal: either they are (true) or they aren’t (false).

In Swift, you do this using the equality operator, which is denoted by ==:

let doesOneEqualTwo = (1 == 2)

Swift infers that doesOneEqualTwo is a Bool. Clearly, 1 does not equal 2, and therefore doesOneEqualTwo will be false.

Similarly, you can find out if two values are not equal using the != operator:

let doesOneNotEqualTwo = (1 != 2)

This time, the comparison is true because 1 does not equal 2, so doesOneNotEqualTwo will be true.

The prefix ! operator, also called the not-operator, toggles true to false and false to true. Another way to write the above is:

let alsoTrue = !(1 == 2)

Because 1 does not equal 2, (1==2) is false, and then ! flips it to true.

Two more operators let you determine if a value is greater than (>) or less than (<) another value. You’ll likely know these from mathematics:

let isOneGreaterThanTwo = (1 > 2)
let isOneLessThanTwo = (1 < 2)

And it’s not rocket science to work out that isOneGreaterThanTwo will equal false and isOneLessThanTwo will equal true.

There’s also an operator that lets you test if a value is less than or equal to another value: <=. It’s a combination of < and ==, and will therefore return true if the first value is either less than the second value or equal to it.

Similarly, there’s an operator that lets you test if a value is greater than or equal to another — you may have guessed that it’s >=.

Boolean Logic

Each of the examples above tests just one condition. When George Boole invented the Boolean, he had much more planned for it than these humble beginnings. He invented Boolean logic, which lets you combine multiple conditions to form a result.

One way to combine conditions is by using AND. When you AND together two Booleans, the result is another Boolean. If both input Booleans are true, then the result is true. Otherwise, the result is false.

george_boole

In Swift, the operator for Boolean AND is &&, used like so:

let and = true && true

In this case, and will be true. If either of the values on the right was false, then and would be false.

Another way to combine conditions is by using OR. When you OR together two Booleans, the result is true if either of the input Booleans is true. Only if both input Booleans are false will the result be false.

In Swift, the operator for Boolean OR is ||, used like so:

let or = true || false

In this case, or will be true. If both values on the right were false, then or would be false. If both were true, then or would still be true.

In Swift, Boolean logic is usually applied to multiple conditions. Maybe you want to determine if two conditions are true; in that case, you’d use AND. If you only care about whether one of two conditions is true, then you’d use OR.

For example, consider the following code:

let andTrue = 1 < 2 && 4 > 3
let andFalse = 1 < 2 && 3 > 4
 
let orTrue = 1 < 2 || 3 > 4
let orFalse = 1 == 2 || 3 == 4

Each of these tests two separate conditions, combining them with either AND or OR.

It’s also possible to use Boolean logic to combine more than two comparisons. For example, you can form a complex comparison like so:

let andOr = (1 < 2 && 3 > 4) || 1 < 4

The parentheses disambiguates the expression. First Swift evaluates the sub-expression inside the parentheses, and then it evaluates the full expression, following these steps:

1. (1 < 2 && 3 > 4) || 1 < 4
2. (true && false) || true
3. false || true
4. true

String Equality

Sometimes you want to determine if two strings are equal. For example, a children’s game of naming an animal in a photo would need to determine if the player answered correctly.

In Swift, you can compare strings using the standard equality operator, ==, in exactly the same way as you compare numbers. For example:

let guess = "dog"
let dogEqualsCat = guess == "cat"

Here, dogEqualsCat is a Boolean that in this case equals false, because "dog" does not equal "cat". Simple!

Just as with numbers, you can compare not just for equality, but also to determine is one value is greater than or less that another value. For example:

let order = "cat" < "dog"

This syntax checks if one string comes before another alphabetically. In this case, order equals true because "cat" comes before "dog".

The subject of equality brings up an interesting feature of Unicode: There are two ways to represent some characters. One example is the é in café, which is an e with an acute accent. You can represent this character with either a single character or with two.

You saw the single character, code point 233, earlier in this tutorial. The two-character case is an e on its own followed by an acute accent combining character, which is a special character that modifies the previous character.

So you can represent the e with an acute accent by either of these means:

combining_characters

The combination of these two characters in the second diagram forms what is known as a grapheme cluster.

Another example of combining characters are the special characters used to change the skin color of certain emojis. For example:

emoji_skin_colour

Here, the thumbs up emoji is followed by a skin tone combining character. On platforms that support it, including iOS and macOS, the rendered emoji is a single thumbs up character with the skin tone applied.

Combining characters make equality of strings a little trickier. For example, consider the word café written once using the single é character, and once using the combining character, like so:

cafe_vs_cafe_combining

These two strings are of course logically equal. When they are printed onscreen, they look exactly the same. But they are represented inside the computer in different ways. Many programming languages would consider these strings to be unequal because those languages work by comparing the characters one by one. Swift, however, considers these strings to be equal by default. Let’s see that in action:

let stringA = "café"
let stringB = "cafe\u{0301}"
 
let equal = stringA == stringB
Note: In the code above, the acute accent combining character is written using the Unicode shorthand, which is \u followed by the code point in hexadecimal, in braces. You can use this shorthand to write any Unicode character. I had to use it here for the combining character because there’s no way to type this character on my keyboard!

In this case, equal is true, because the two strings are logically the same.

String comparison in Swift uses a technique known as canonicalization. Say that three times fast! Before checking equality, Swift canonicalizes both strings, which means they’re converted to use the same special character representation.

It doesn’t matter which way it does the canonicalization — using the single character or using the combining character — as long as both strings get converted to the same style. Once the canonicalization is complete, Swift can compare individual characters to check for equality.

Mini-exercises

  1. Create a constant called myAge and set it to your age. Then, create a constant called isTeenager that uses Boolean logic to determine if the age denotes someone in the age range of 13 to 19.
  2. Create another constant called theirAge and set it to my age, which is 30. Then, create a constant called bothTeenagers that uses Boolean logic to determine if both you and I are teenagers.
  3. Create a constant called reader and set it to your name as a string. Create a constant called author and set it to my name, Matt Galloway. Create a constant called authorIsReader that uses string equality to determine if reader and author are equal.
  4. Create a constant called readerBeforeAuthor which uses string comparison to determine if reader comes before author.

The if Statement

The first and most common way of controlling the flow of a program is through the use of an if statement, which allows the program to do something only if a certain condition is true. For example, consider the following:

if 2 > 1 {
  print("Yes, 2 is greater than 1.")
}

This is a simple if statement. If the condition is true, then the statement will execute the code between the braces. If the condition is false, then the statement won’t execute the code between the braces. It’s as simple as that!

You can extend an if statement to provide code to run in case the condition turns out to be false. This is known as the else clause. Here’s an example:

let animal = "Fox"
 
if animal == "Cat" || animal == "Dog" {
  print("Animal is a house pet.")
} else {
  print("Animal is not a house pet.")
}

Here, if animal equals either "Cat" or "Dog", then the statement will run the first block of code. If animal does not equal either "Cat" or "Dog", then the statement will run the block inside the else part of the if statement, printing the following to the debug area:

Animal is not a house pet.

But you can go even further than that with if statements. Sometimes you want to check one condition, then another. This is where else-if comes into play, nesting another if statement in the else clause of a previous if statement.

You can use it like so:

let hourOfDay = 12
let timeOfDay: String
 
if hourOfDay < 6 {
  timeOfDay = "Early morning"
} else if hourOfDay < 12 {
  timeOfDay = "Morning"
} else if hourOfDay < 17 {
  timeOfDay = "Afternoon"
} else if hourOfDay < 20 {
  timeOfDay = "Evening"
} else if hourOfDay < 24 {
  timeOfDay = "Late evening"
} else {
  timeOfDay = "INVALID HOUR!"
}
print(timeOfDay)

These nested if statements test multiple conditions one by one until a true condition is found. Only the code associated with that first true condition is executed, regardless of whether subsequent else-if conditions are true. In other words, the order of your conditions matters!

You can add an else clause at the end to handle the case where none of the conditions are true. This else clause is optional if you don’t need it; in this example you do need it, to ensure that timeOfDay has a valid value by the time you print it out.

In this example, the if statement takes a number representing an hour of the day and converts it to a string representing the part of the day to which the hour belongs. Working with a 24-hour clock, the statements are checked one-by-one in order:

  • The first check is to see if the hour is less than 6. If so, that means it’s early morning.
  • If the hour is not less than 6, the statement continues to the first else-if, where it checks the hour to see if it’s less than 12.
  • Then in turn, as conditions prove false, the statement checks the hour to see if it’s less than 17, then less than 20, then less than 24.
  • Finally, if the hour is out of range, the statement prints that information to the console.

In the code above, the hourOfDay constant is 12. Therefore, the code will print the following:

Afternoon

Notice that even though both the hourOfDay < 20 and hourOfDay < 24 conditions are also true, the statement only executes the first block whose condition is true; in this case, the block with the hourOfDay < 17 condition.

Short Circuiting

An important fact about if statements is what happens when there are multiple Boolean conditions separated by ANDs (&&) or ORs (||).

Consider the following code:

if 1 > 2 && name == "Matt Galloway" {
  // ...
}

The first condition of the if-statement, 1 > 2 is false. Therefore the whole expression cannot ever be true. So Swift will not even bother to check the second part of the expression, namely the check of name.

Similarly, consider the following code:

if 1 < 2 || name == "Matt Galloway" {
  // ...
}

Since 1 < 2 is true, the whole expression must be true as well. Therefore once again, the check of name is not executed.

This will come in handy later on when you start dealing with more complex data types.

Encapsulating Variables

if statements introduce a new concept scope, which is a way to encapsulate variables through the use of braces.

Let’s take an example. Imagine you want to calculate the fee to charge your client. Here’s the deal you’ve made:

You earn $25 for every hour up to 40 hours, and $50 for every hour thereafter.

Using Swift, you can calculate your fee in this way:

var hoursWorked = 45
 
var price = 0
if hoursWorked > 40 {
  let hoursOver40 = hoursWorked - 40
  price += hoursOver40 * 50
  hoursWorked -= hoursOver40
}
price += hoursWorked * 25
 
print(price)

This code takes the number of hours and checks if it’s over 40. If so, the code calculates the number of hours over 40 and multiplies that by $50, then adds the result to the price. The code then subtracts the number of hours over 40 from the hours worked. It multiplies the remaining hours worked by $25 and adds that to the total price.

In the example above, the result is as follows:

1250

The interesting thing here is the code inside the if statement. There is a declaration of a new constant, hoursOver40, to store the number of hours over 40. Clearly, you can use it inside the if statement. But what happens if you try to use it at the end of the above code?

...
 
print(price)
print(hoursOver40)

This would result in the following error:

Use of unresolved identifier 'hoursOver40'

This error informs you that you’re only allowed to use the hoursOver40 constant within the scope in which it was created. In this case, the if statement introduced a new scope, so when that scope is finished, you can no longer use the constant.

However, each scope can use variables and constants from its parent scope. In the example above, the scope inside of the if statement uses the price and hoursWorked variables, which you created in the parent scope.

The Ternary Conditional Operator

Now I want to introduce a new operator, one you didn’t see in the previous tutorial. It’s called the ternary conditional operator and it’s related to if statements.

If you wanted to determine the minimum and maximum of two variables, you could use if statements, like so:

let a = 5
let b = 10
 
let min: Int
if a < b {
  min = a
} else {
  min = b
}
 
let max: Int
if a > b {
  max = a
} else {
  max = b
}

By now you know how this works, but it’s a lot of code. Wouldn’t it be nice if you could shrink this to just a couple of lines? Well, you can, thanks to the ternary conditional operator!

The ternary conditional operator takes a condition and returns one of two values, depending on whether the condition was true or false. The syntax is as follows:

(<CONDITION>) ? <TRUE VALUE> : <FALSE VALUE>

You can use this operator to rewrite your long code block above, like so:

let a = 5
let b = 10
 
let min = a < b ? a : b
let max = a > b ? a : b

In the first example, the condition is a < b. If this is true, the result assigned back to min will be the value of a; if it’s false, the result will be the value of b.

I’m sure you agree that’s much simpler! This is a useful operator that you’ll find yourself using regularly.

Note: Because finding the greater or smaller of two numbers is such a common operation, the Swift standard library provides two functions for this purpose: max and min. If you were paying attention earlier in this series, then you’ll recall you’ve already seen these.

Mini-exercises

  1. Create a constant called myAge and initialize it with your age. Write an if statement to print out Teenager if your age is between 13 and 19, and Not a teenager if your age is not between 13 and 19.
  2. Create a constant called answer and use a ternary condition to set it equal to the result you print out for the same cases in the above exercise. Then print out answer.

Loops

Loops are Swift’s way of executing code multiple times. In this section, you’ll learn about one type of loop, the while loop. If you know another programming language, you’ll find the concepts and maybe even the syntax to be familiar.

While Loops

A while loop repeats a block of code while a condition is true.

You create a while loop this way:

while <CONDITION> {
  <LOOP CODE>
}

Every iteration, the loop checks the condition. If the condition is true, then the loop executes and moves on to another iteration. If the condition is false, then the loop stops. Just like if statements, while loops introduce a scope.

The simplest while loop takes this form:

while true {
}

This is a while loop that never ends because the condition is always true. Of course, you would never write such a while loop, because your program would spin forever! This situation is known as an infinite loop, and while it might not cause your program to crash, it will very likely cause your computer to freeze.

force_quit

Here’s a more useful example of a while loop:

var sum = 1
 
while sum < 1000 {
  sum = sum + (sum + 1)
}

This code calculates a mathematical sequence, up to the point where the value is greater than 1000.

The loop executes as follows:

  • Before iteration 1: sum = 1, loop condition = true
  • After iteration 1: sum = 3, loop condition = true
  • After iteration 2: sum = 7, loop condition = true
  • After iteration 3: sum = 15, loop condition = true
  • After iteration 4: sum = 31, loop condition = true
  • After iteration 5: sum = 63, loop condition = true
  • After iteration 6: sum = 127, loop condition = true
  • After iteration 7: sum = 255, loop condition = true
  • After iteration 8: sum = 511, loop condition = true
  • After iteration 9: sum = 1023, loop condition = false

After the ninth iteration, the sum variable is 1023, and therefore the loop condition of sum < 1000 becomes false. At this point, the loop stops.

Repeat-while Loops

A variant of the while loop is called the repeat-while loop. It differs from the while loop in that the condition is evaluated at the end of the loop rather than at the beginning.

You construct a repeat-while loop like this:

repeat {
  <LOOP CODE>
} while <CONDITION>

Here’s the example from the last section, but using a repeat-while loop:

var sum = 1
 
repeat {
  sum = sum + (sum + 1)
} while sum < 1000

In this example, the outcome is the same as before. However, that isn’t always the case — you might get a different result with a different condition.

Consider the following while loop:

var sum = 1
 
while sum < 1 {
  sum = sum + (sum + 1)
}

And now consider the corresponding repeat-while loop, which uses the same condition:

var sum = 1
 
repeat {
  sum = sum + (sum + 1)
} while sum < 1

In the case of the regular while loop, the condition sum < 1 is false right from the start. That means the body of the loop won’t be reached! The value of sum will equal 1 because the loop won’t execute any iterations.

In the case of the repeat-while loop, however, sum will equal 3 because the loop will execute once.

Breaking Out of a Loop

Sometimes you want to break out of a loop early. You can do this using the break statement, which immediately stops the execution of the loop and continues on to the code after the loop.

For example, consider the following code:

var sum = 1
 
while true {
  sum = sum + (sum + 1)
  if sum >= 1000 {
    break
  }
}

Here, the loop condition is true, so the loop would normally iterate forever. However, the break means the while loop will exit once the sum is greater than or equal to 1000. Neat!

You’ve seen how to write the same loop in different ways, demonstrating that in computer programming, there are often many ways to achieve the same result. You should choose the method that’s easiest to read and conveys your intent in the best way possible. This is an approach you’ll internalize with enough time and practice.

Mini-exercises

  1. Create a variable named counter and set it equal to 0. Create a while loop with the condition counter < 10 which prints out counter is X (where X is replaced with counter value) and then increments counter by 1.
  2. Create a variable named counter and set it equal to 0. Create another variable named roll and set it equal to 0. Create a repeat-while loop. Inside the loop, set roll equal to Int(arc4random_uniform(6)) which means to pick a random number between 0 and 5. Then increment counter by 1. Finally, print After X rolls, roll is Y where X is the value of counter and Y is the value of roll. Set the loop condition such that the loop finishes when the first 0 is rolled.

Key Points

  • You use the Boolean data type Bool to represent true and false.
  • The comparison operators, all of which return a Boolean, are:
    Equal: ==
    Not equal: !=
    Less than: <
    Greater than: >
    Less than or equal: <=
    Greater than or equal: >=
  • You can use Boolean logic to combine comparison conditions.
  • Swift’s use of canonicalization ensures that the comparison of strings accounts for combining characters.
  • You use if statements to make simple decisions based on a condition.
  • You use else and else-if within an if statement to extend the decision-making beyond a single condition.
  • Short circuiting ensures that only the minimal required parts of a Boolean expression are evaluated.
  • You can use the ternary operator in place of simple if statements.
  • Variables and constants belong to a certain scope, beyond which you cannot use them. A scope inherits visible variables and constants from its parent.
  • While loops allow you to perform a certain task a number of times until a condition is met.
  • The break statement lets you break out of a loop.

First Challenges

Here are a few challenges before you head into the next section on advanced control flow:

  1. What’s wrong with the following code?
let firstName = "Matt"
 
if firstName == "Matt" {
  let lastName = "Galloway"
} else if firstName == "Ray" {
  let lastName = "Wenderlich"
}
let fullName = firstName + " " + lastName
  1. In each of the following statements, what is the value of the Boolean answer constant?
let answer = true && true
let answer = false || false
let answer = (true && 1 != 2) || (4 > 3 && 100 < 1)
let answer = ((10 / 2) > 3) && ((10 % 2) == 0)
  1. Suppose the squares on a chessboard are numbered left to right, top to bottom, with 0 being the top-left square and 63 being the bottom-right square. Rows are numbered top to bottom, 0 to 7. Columns are numbered left to right, 0 to 7. Given a current position on the chessboard, expressed as a row and column number, calculate the next position on the chessboard, again expressed as a row and column number. The ordering is determined by the numbering from 0 to 63. The position after 63 is again 0.
  2. Given the coefficients a, b and c, calculate the solutions to a quadratic equation with these coefficients. Take into account the different number of solutions (0, 1 or 2). If you need a math refresher, this Wikipedia article on the quadratic equation will help https://en.wikipedia.org/wiki/Quadratic_formula.
  3. Given a month (represented with a String in all lowercase) and the current year (represented with an Int), calculate the number of days in the month. Remember that because of leap years, "february" has 29 days when the year is a multiple of 4 but not a multiple of 100. February also has 29 days when the year is a multiple of 400.
  4. Given a number, determine if this number is a power of 2. (Hint: you can use log2(number) to find the base 2 logarithm of number. log2(number) will return a whole number if number is a power of two. You can also solve the problem using a loop and no logarithm.)
  5. Print a table of the first 10 powers of 2.
  6. Given a number n, calculate the n-th Fibonacci number. (Recall Fibonacci is 1, 1, 2, 3, 5, 8, 13, ... Start with 1 and 1 and add these values together to get the next value. The next value is the sum of the previous two. So the next value in this case is 8+13 = 21.)
  7. Given a number n, calculate the factorial of n. (Example: 4 factorial is equal to 1 * 2 * 3 * 4.)
  1. Given a number between 2 and 12, calculate the odds of rolling this number using two six-sided dice. Compute it by exhaustively looping through all of the combinations and counting the fraction of outcomes that give you that value. Don't use a formula.

Advanced Control Flow

In this section, you’ll continue to learn how to control the flow of execution. You’ll learn about another loop known as the for loop.

Loops may not sound very interesting, but they’re very common in computer programs. For example, you might have code to download an image from the cloud; with a loop, you could run that multiple times to download your entire photo library. Or if you have a game with multiple computer-controlled characters, you might need a loop to go through each one and make sure it knows what to do next.

You’ll also learn about switch statements which are particularly powerful in Swift. They allow you to inspect a value and decide what to do based on its value. They’re incredibly powerful when used with some advanced Swift features such as pattern matching.

Ranges

Before you dive into the for loop statement, you need to know about the Range data type, which lets you represent a sequence of numbers. Let’s look at two types of Range.

First, there’s closed range, which you represent like so:

let closedRange = 0...5

The three dots (...) indicate that this range is closed, which means the range goes from 0 to 5 inclusive. That’s the numbers (0, 1, 2, 3, 4, 5).

Second, there’s half-open range, which you represent like so:

let halfOpenRange = 0..<5

Here, you replace the three dots with two dots and a less-than sign (..<). Half-open means the range goes from 0 to 5, inclusive of 0 but not of 5. That’s the numbers (0, 1, 2, 3, 4).

Both open and half-open ranges must always be increasing. In other words, the second number must always be greater than or equal to the first.

Ranges are commonly used in both for loops and switch statements, which means that throughout the rest of this section, you’ll use ranges as well!

For Loops

In the previous section, you looked at while loops. Now that you know about ranges, it’s time to look at another type of loop: the for loop. This is probably the most common loop you’ll see, and you’ll use it to run code a certain number of times.

You construct a for loop like this:

for <CONSTANT> in <RANGE> {
  <LOOP CODE>
}

The loop begins with the for keyword, followed by a name given to the loop constant (more on that shortly), followed by in, followed by the range to loop through.

Here’s an example:

let count = 10
var sum = 0
 
for i in 1...count {
  sum += i
}

In the code above, the for loop iterates through the range 1 to count. At the first iteration of the loop, i will equal the first element in the range: 1. Each time around the loop, i will increment until it’s equal to count; the loop will execute one final time and then finish.

Note: If you’d used a half-open range, the the last iteration would see i equal to count - 1.

Inside the loop, you add i to the sum variable; it runs 10 times to calculate the sequence 1 + 2 + 3 + 4 + 5 + ... all the way up to 10.

Here are the values of the constant i and variable sum for each iteration:

  • Start of iteration 1: i = 1, sum = 0
  • Start of iteration 2: i = 2, sum = 1
  • Start of iteration 3: i = 3, sum = 3
  • Start of iteration 4: i = 4, sum = 6
  • Start of iteration 5: i = 5, sum = 10
  • Start of iteration 6: i = 6, sum = 15
  • Start of iteration 7: i = 7, sum = 21
  • Start of iteration 8: i = 8, sum = 28
  • Start of iteration 9: i = 9, sum = 36
  • Start of iteration 10: i = 10, sum = 45
  • After iteration 10: sum = 55

In terms of scope, the i constant is only visible inside the scope of the for loop, which means it’s not available outside of the loop.

Note: If you’re mathematically astute, you might notice that this example computes triangle numbers. Here’s a quick explanation: http://bbc.in/1O89TGP

Xcode’s playground gives you a handy way to visualize such an iteration. Hover over the sum += i line in the results pane, and you’ll see a white dot on the right. Hover over that dot to reveal a plus (+) button:

loop_before_graph

Click this plus (+) button and Xcode will display a graph underneath the line within the playground code editor:

loop_after_graph

This graph lets you visualize the sum variable as the loop iterates.

Finally, sometimes you only want to loop a certain number of times, and so you don’t need to use the loop constant at all. In that case, you can employ the underscore to indicate you’re ignoring it, like so:

let count = 10
var sum = 1
var lastSum = 0
 
for _ in 0..<count {
  let temp = sum
  sum = sum + lastSum
  lastSum = temp
}

This code doesn’t require a loop constant; the loop simply needs to run a certain number of times. In this case, the range is 0 through count and is half-open. This is the usual way of writing loops that run a certain number of times.

It’s also possible to only perform the iteration under certain conditions. For example, imagine you wanted to compute a sum similar to that of triangle numbers, but only for odd numbers:

let count = 10
var sum = 0
for i in 1...count where i % 2 == 1 {
  sum += i
}

The loop above has a where clause in the for loop statement. The loop still runs through all values in the range 1 to count, but it will only execute the loop’s code block when the where condition is true; in this case, where i is odd.

Continue and Labeled Statements

Sometimes you’d like to skip a loop iteration for a particular case without breaking out of the loop entirely. You can do this with the continue statement, which immediately ends the current iteration of the loop and starts the next iteration.

Note: In many cases, you can use the simpler where clause you just learned about. The continue statement gives you a higher level of control, letting you decide where and when you want to skip an iteration.

Take the example of an 8 by 8 grid, where each cell holds a value of the row multiplied by the column:

full_board

It looks much line a multiplication table, doesn’t it?

Let’s say you wanted to calculate the sum of all cells but exclude all even rows, as shown below:

first_board_example

Using a for loop, you can achieve this as follows:

var sum = 0
 
for row in 0..<8 {
  if row % 2 == 0 {
    continue
  }
 
  for column in 0..<8 {
    sum += row * column
  }
}

When the row modulo 2 equals 0, the row is even. In this case, continue makes the for loop skip to the next row.

Just like break, continue works with both for loops and while loops.

The second code example will calculate the sum of all cells, excluding those where the column is greater than or equal to the row.

To illustrate, it should sum the following cells:

second_board_example

Using a for loop, you can achieve this as follows:

var sum = 0
 
rowLoop: for row in 0..<8 {
  columnLoop: for column in 0..<8 {
    if row == column {
      continue rowLoop
    }
    sum += row * column
  }
}

This last code block makes use of labeled statements, labeling the two loops as rowLoop and the columnLoop, respectively. When the row equals the column inside the inner columnLoop, the outer rowLoop will continue.

You can use labeled statements like these with break to break out of a certain loop. Normally, break and continue work on the innermost loop, so you need to use labeled statements if you want to manipulate an outer loop.

Mini-exercises

  1. Create a variable called range and set it equal to a range starting at 1 and ending with 10 inclusive. Write a for loop which iterates over this range and prints the square of each number.
  2. Write a for loop which iterates over the same range as in the exercise above and prints the square root of each number. Hint: you will need to type convert your loop variable.
  3. Above, you saw a for loop which iterated over only the even rows like so:
var sum = 0
for row in 0..<8 {
  if row % 2 == 0 {
    continue
  }
  for column in 0..<8 {
    sum += row * column
  }
}

Change this to use a where clause on the first for loop to skip even rows instead of using continue. Check that the sum is 448 as in the initial example.

Switch Statements

Another way to control flow is through the use of a switch statement, which lets you execute different bits of code depending on the value of a variable or constant.

Here’s a very simple switch statement that acts on an integer:

let number = 10
 
switch number {
case 0:
  print("Zero")
default:
  print("Non-zero")
}

In this example, the code will print the following:

Non-zero

The purpose of this switch statement is to determine whether or not a number is zero. It will get more complex — I promise!

To handle a specific case, you use case followed by the value you want to check for, which in this case is 0. Then, you use default to signify what should happen for all other values.

Here’s another example:

let number = 10
 
switch number {
case 10:
  print("It's ten!")
default:
  break
}

This time you check for 10, in which case, you print a message. Nothing should happen for other values. When you want nothing to happen for a case, or you want the default state to run, you use the break statement. This tells Swift that you meant to not write any code here and that nothing should happen. Cases can never be empty, so you must write some code, even if it’s just a break!

Of course, switch statements also work with data types other than integers. They work with any data type! Here’s an example of switching on a string:

let string = "Dog"
 
switch string {
case "Cat", "Dog":
  print("Animal is a house pet.")
default:
  print("Animal is not a house pet.")
}

This will print the following:

Animal is a house pet.

In this example, you provide two values for the case, meaning that if the value is equal to either "Cat" or "Dog", then the statement will execute the case.

Advanced Switch Statements

You can also give your switch statements more than one case. In the previous section, you saw an if statement using multiple else-if statements to convert an hour of the day to a string describing that part of the day. You could rewrite that more succinctly with a switch statement, like so:

let hourOfDay = 12
let timeOfDay: String
 
switch hourOfDay {
case 0, 1, 2, 3, 4, 5:
  timeOfDay = "Early morning"
case 6, 7, 8, 9, 10, 11:
  timeOfDay = "Morning"
case 12, 13, 14, 15, 16:
  timeOfDay = "Afternoon"
case 17, 18, 19:
  timeOfDay = "Evening"
case 20, 21, 22, 23:
  timeOfDay = "Late evening"
default:
  timeOfDay = "INVALID HOUR!"
}
 
print(timeOfDay)

This code will print the following:

Afternoon

Remember ranges? Well, you can use ranges to simplify this switch statement. You can rewrite it using ranges as shown below:

let hourOfDay = 12
let timeOfDay: String
 
switch hourOfDay {
case 0...5:
  timeOfDay = "Early morning"
case 6...11:
  timeOfDay = "Morning"
case 12...16:
  timeOfDay = "Afternoon"
case 17...19:
  timeOfDay = "Evening"
case 20..<24:
  timeOfDay = "Late evening"
default:
  timeOfDay = "INVALID HOUR!"
}

This is more succinct than writing out each value individually for all cases.

When there are multiple cases, the statement will execute the first one that matches. You’ll probably agree that this is more succinct and more clear than using an if statement for this example. It’s slightly more precise as well, because the if statement method didn’t address negative numbers, which here are correctly deemed to be invalid.

It’s also possible to match a case to a condition based on a property of the value. As you learned in the first part of this tutorial series, you can use the modulo operator to determine if an integer is even or odd. Consider this code:

let number = 10
 
switch number {
case let x where x % 2 == 0:
  print("Even")
default:
  print("Odd")
}

This will print the following:

Even

This switch statement uses the let-where syntax, meaning the case will match only when a certain condition is true. The let part binds a value to a name, while the where part provides a Boolean condition that must be true for the case to match. In this example, you’ve designed the case to match if the value is even — that is, if the value modulo 2 equals 0.

The method by which you can match values based on conditions is known as pattern matching.

In the previous example, the binding introduced a unnecessary constant x; it’s simply another name for number. You are allowed to use number in the where clause and replace the binding with an underscore to ignore it:

let number = 10
 
switch number {
case _ where number % 2 == 0:
  print("Even")
default:
  print("Odd")
}

Partial Matching

Another way you can use switch statements with matching to great effect is as follows:

let coordinates = (x: 3, y: 2, z: 5)
 
switch coordinates {
case (0, 0, 0): // 1
  print("Origin")
case (_, 0, 0): // 2
  print("On the x-axis.")
case (0, _, 0): // 3
  print("On the y-axis.")
case (0, 0, _): // 4
  print("On the z-axis.")
default:        // 5
  print("Somewhere in space")
}

This switch statement makes use of partial matching. Here’s what each case does, in order:

  1. Matches precisely the case where the value is (0, 0, 0). This is the origin of 3D space.
  2. Matches y=0, z=0 and any value of x. This means the coordinate is on the x-axis.
  3. Matches x=0, z=0 and any value of y. This means the coordinate is on the y-axis.
  4. Matches x=0, y=0 and any value of z. This means the coordinate is on the z-axis.
  5. Matches the remainder of coordinates.

You’re using the underscore to mean that you don’t care about the value. If you don’t want to ignore the value, then you can bind it and use it in your switch statement, like this:

let coordinates = (x: 3, y: 2, z: 5)
 
switch coordinates {
case (0, 0, 0):
  print("Origin")
case (let x, 0, 0):
  print("On the x-axis at x = \(x)")
case (0, let y, 0):
  print("On the y-axis at y = \(y)")
case (0, 0, let z):
  print("On the z-axis at z = \(z)")
case let (x, y, z):
  print("Somewhere in space at x = \(x), y = \(y), z = \(z)")
}

Here, the axis cases use the let syntax to pull out the pertinent values. The code then prints the values using string interpolation to build the string.

Notice how you don’t need a default in this switch statement. This is because the final case is essentially the default; it matches anything, because there are no constraints on any part of the tuple. If the switch statement exhausts all possible values with its cases, then no default is necessary.

Also notice how you could use a single let to bind all values of the tuple: let (x, y, z) is the same as (let x, let y, let z).

Finally, you can use the same let-where syntax you saw earlier to match more complex cases. For example:

let coordinates = (x: 3, y: 2, z: 5)
 
switch coordinates {
case let (x, y, _) where y == x:
  print("Along the y = x line.")
case let (x, y, _) where y == x * x:
  print("Along the y = x^2 line.")
default:
  break
}

Here, you match the “y equals x” and “y equals x squared” lines.

And those are the basics of switch statements!

Mini-exercises

  1. Write a switch statement that takes an age as an integer and prints out the life stage related to that age. You can make up the life stages, or use my categorization as follows: 0-2 years, Infant; 3-12 years, Child; 13-19 years, Teenager; 20-39, Adult; 40-60, Middle aged; 61+, Elderly.
  2. Write a switch statement that takes a tuple containing a string and an integer. The string is a name, and the integer is an age. Use the same cases that you used in the previous exercise and let syntax to print out the name followed by the life stage. For example, for myself it would print out "Matt is an adult.".

Key Points

  • You can use ranges to create a sequence of numbers, incrementing to move from one value to another.
  • Closed ranges include both the start and end values.
  • Half-open ranges include the start value and stop one before the end value.
  • For loops allow you to iterate over a range.
  • The continue statement lets you finish the current iteration of a loop and begin the next iteration.
  • Labeled statements let you use break and continue on an outer loop.
  • You use switch statements to decide which code to run depending on the value of a variable or constant.
  • The power of a switch statement comes from leveraging pattern matching to compare values using complex rules.

Give these challenges a try to see if you fully understand the subtleties of loop and switch statements.

Second Challenges

  1. In the following for loop:
var sum = 0
for i in 0...5 {
  sum += i
}

What will be the value of sum, and how many iterations will happen?

  1. In the while loop below:
var aLotOfAs = ""
while aLotOfAs.characters.count < 10 {
  aLotOfAs += "a"
}

How many instances of the character “a” will there be in aLotOfAs? Hint: aLotOfAs.characters.count will tell you how many characters there are in the string aLotOfAs.

  1. Consider the following switch statement:
switch coordinates {
case let (x, y, z) where x == y && y == z:
  print("x = y = z")
case (_, _, 0):
  print("On the x/y plane")
case (_, 0, _):
  print("On the x/z plane")
case (0, _, _):
  print("On the y/z plane")
default:
  print("Nothing special")
}

What will this code print when coordinates is each of the following?

let coordinates = (1, 5, 0)
let coordinates = (2, 2, 2)
let coordinates = (3, 0, 1)
let coordinates = (3, 2, 5)
let coordinates = (0, 2, 4)
  1. A closed range can never be empty. Why?
  2. Print a countdown from 10 to 0. (Note: do not use the reversed() method, which will be introduced later.)
  3. Print 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0. (Note: do not use the stride(from:by:to:) function, which will be introduced later.)

Where to Go From Here?

You’ve learned a lot about the core language features for dealing with data over these past few tutorials — from data types to variables, then on to decision-making with Booleans and loops with ranges.

You can download the final playgrounds and challenges from the first part and second part of this tutorial here.

We encourage you to try the challenges above; if you find a particularly interesting solution to one of the challenges, or have any questions or comments on this tutorial, please tell us about it in the discussion below!

swift-apprentice

This tutorial was taken from Chapters 4 and 5 of Swift Apprentice, Second Edition available from the raywenderlich.com store.

The book has been completely updated to work with Xcode 8 and Swift 3. Check it out and let us know what you think!

The post Swift Tutorial Part 3: Flow Control appeared first on Ray Wenderlich.

Using Framer to Prototype iOS Animations

$
0
0

Framer-featureStatic prototypes suck. With static prototypes, you can visualize the app’s visual design, but not the interaction design.

Considering how important interaction design is to apps, static prototypes are like a puzzle with pieces missing.

So why doesn’t everyone create interactive prototypes instead? Well, it can take a lot of time to prototype user interactions using a tool like After Effects. It’s hard to spend so much time when you might throw it away the next day.

Enter Framer: an easy-to-use tool for developers and designers to create interactive prototypes. Framer makes it quick to prototype interactions, iterate on the fly, and bring back the magic!

In this Framer tutorial, you’ll recreate this lovely navigation header animation created by Voleg:

animSlow-3

In this tutorial, you will focus on prototyping the animation for the menu expanding/collapsing, as that’s the most interesting part. Let’s get started!

Getting Started

First, download and install the following software (you can use the free trials for the purposes of this tutorial):

Open Framer, and you’ll see the following welcome screen:

welcome

Click the Animate example project to get a feel for the IDE:

Framer

On the left is the Coding Panel; on the right is the Prototype Panel. The Layers Panel sits in the middle.

Feel free to look through the code to get a preview of what’s coming, but don’t worry if you don’t understand it for now. Close this example project, you’re going to create a new one.

Creating a New Prototype

Create a new file in Framer, by going to File\New. Then, click the Insert\Layer to create a new Layer.

InsertLayer

Click in a blank spot in the code editor to unselect the layer attributes. You should then see your new layer in each panel:

  • As code in the Coding Panel
  • As a reference in the Layers Panel
  • As a grey square in the Prototype Panel

Mouse over the name of the layer in the Layers Panel to see its location on the prototype.

highlightedSquare

Change the name to square in the coding panel.

squareCodingPanel

Click on the square next to the left of the line of code to see and modify the layer’s attributes in the Layers Panel and to move it around in the Prototype Panel.

squareLayerClick

Drag the square to the middle of the prototype, and observe the changes in the Coding and Layers Panels. It should now look something like this:

firstChange

The changes you make on the layer by interacting with it on the prototype are immediately reflected in the code — and vice versa.

Being able to use either the code or the visual editor to make our changes is a huge advantage of prototyping with Framer as opposed to working with Xcode and Swift.

Delete the existing code, paste the following in the coding panel, and observe the immediate change in the prototype:

square = new Layer
	x: 250
	y: 542
	height: 250
	width: 250
	backgroundColor: "rgba(255,25,31,0.8)"

Pretty neat, right?

Note: You write code in Framer using CoffeeScript, a simple language that compiles into Javascript. If you’ve never used it before, don’t worry – it’s pretty simple and you can learn a lot about its syntax just by following along with this tutorial.

Note that indentation matters in CoffeeScript, so make sure your indentation matches mine or the code won’t work! Indentation is CoffeeScript’s replacement for {}‘s.

Tabs vs spaces matter too. Framer defaults to use tabs by default, so if you see code that uses spaces like this:

badIndent

Backspace until you hit the left edge, and replace spaces with tabs to get something like this:

goodIndent

When you’re copying and pasting code and moving to a newline, always backspace to the beginning of the line. Otherwise your code might be interpreted as part of something else.

Your First Framer Animation

Time to make some magic happen! You’ll make a red square turn into an orange circle when it’s tapped.

Add an empty line after your layer definition, then add the following:

square.onTap ->
	square.backgroundColor = "rgba(255,120,0,0.8)"
	square.borderRadius = 150

This makes the shape respond to tap events.

Click on the red square, and it will turn into an orange circle.

firstChangeVidFinal

The biggest advantage of prototyping with Framer, rather than Xcode and Swift, is the ability to interact with your prototype immediately after you make your change. Removing the time-suck of constantly building and running Xcode projects greatly improves your prototyping speed — and your ability to iterate quickly through your designs.

All right, I know what you’re thinking. Snoozeville! The transition is too sudden, and the user can’t return to the red square. That’s easy to fix.

Instead of specifying what happens to the square layer on a tap, create a new state.

Replace the code you just added with the following:

# 1
square.states.add
	orangeCircle:
		backgroundColor: "rgba(255,120,0,0.8)"
		borderRadius: 150
 
# 2
square.onTap ->
	square.states.next()

Let’s review this section by section:

  1. This defines a new state orangeCircle for square. This new state sets the layer’s backgroundColor to orange and its borderRadius to 150. You can see a full list of properties you can set on layer’s in the framer.js documentation.
  2. This configures the tap event so that square will transition to its next state.

Click the square to see how the transition has improved:

secondChange

Your shape now animates to the orangeCircle state and animates back to the square’s original state. That’s because you’ve added states to a layer loop, so the state after orangeCircle is the default state.

Let’s try adding another state. Add this line right below section 1:

square.states.add
	greenRounded:
		backgroundColor: ("green")
		borderRadius: 50

Now, tap the square in the prototype panel and you’ll see it loop between all three states:

thirdChange

With just a few lines of code, and barely any setup, you’ve created a slick animation!

Think you’re ready for something harder and way cooler, like an actual UI?

ChallengeAccepted

Using Framer to Recreate an Animation

Take another look at the animation you’ll be recreating:

animSlow-3

Again, you’ll be focusing on the menu expanding/collapsing portion of this animation in this tutorial.

The most important part in recreating an animation is to break it down to its basic components. Not only does this help you understand the animation better, but it also helps you create the step-by-step instructions on how to do it yourself!

There are three different problems to tackle in this animation: The selected state, the deselected state, and the transition between them.

Deselected

deselectedState

This is what the user sees initially:

  • There are four tappable banners.
  • Each banner has a different color, icon, and title.
  • Each banner casts a shadow on the banner below it.

Selected

selectedState

This is what the user sees when they tap a banner:

  • The top bar with the main color, title and a smaller icon
  • A white background
  • The top bar casts a shadow on the layer underneath
  • Four cards underneath, two on the top, two on the bottom, and spaced evenly
  • The color of the cards depends on the selected banner

The user taps to transition between the two states. In the deselected state, the user taps on one of the colored banners. In the selected state, the user taps on the top bar.

Transitioning from Deselected to Selected

selToDesel

Here’s what happens as the UI transitions from deselected to the selected state:

  • All four banners shrink to slightly less than half of their original height.
  • The tapped banner goes on top, the others fold behind it.
  • The banner animation starts slow, speeds up quite a bit, then slows down dramatically.
  • The only remaining shadow is the one on the top banner.
  • The icon on the quite suddenly shrinks from 100% of its size to nothing. The animation starts quickly but finishes with a lot of damping.
  • The text in the middle of the tapped banner moves vertically to the middle of the top bar.
  • The background cards have no real animation, but the colors of the cards match the tapped banner.

Transitioning from Selected to Deselected

deselToSel

Here’s what happens as the UI transitions from the selected to the deselected state:

  • All four banners expand and move down, so that one begins where the other ends to cover the whole screen. Each banner is 1/4th of the height of the screen
  • The banners are always in a specific order, so the banner you tap on doesn’t influence how the banners appear in the deselected state.
  • The expanding banner animation starts off a little slow, speeds up quite significantly, then finishes with a lot of damping.
  • Each banner casts its own shadow.
  • The icon reappears; it grows from nothing to 100% of its size. The animation starts quickly, then finishes with damping.
  • The text moves about 4/5ths of the way down the expanded banner.

Now that the visuals are broken down, you can move on to the fun part — building the animation!

Note: This particular animation wasn’t too difficult to dissect, but it’s usually helpful to record an animation with QuickTime and then slow it down with After Effects or Adobe Photoshop to pick out the finer details. For example, this technique helped me discern whether the icon should disappear immediately when you tap a banner, or if it’s a more gradual disappearing act.

Laying Things Out

First, download the starter assets for this tutorial. This package contains all the assets needed to recreate the animation — the icons, the text and the cards.

First, install the Archive font included in the starter assets. It’s important to do this before you open the Sketch file, otherwise it won’t display correctly.

Next, open SweetStuff.sketch to see the assets I’ve created for you:

StarterAssets

Go back to Framer and create a new file with File\New. Then click the Import button on the toolbar:
importSketch copy

Leave the export size at @1x and press Import again. You should see something like this:

importedSketch

Framer just created a variable named sketch that holds references to all the layers in your Sketch file you’ll need to create your prototype. You can see all these layers in the Layers Panel.

Note: Make sure that you have the Sketch file opened before you press Import, or otherwise Framer won’t be able to detect your Sketch file.

By default, the device selected for your prototype is an iPhone 6S Silver. However, your prototype should work on lots of other devices as well. You’ll create a device variable as a reference to the selected device, and you’ll lay things out relative to the device’s screen.

Add the following after the sketch variable:

device = Framer.Device.screen

Color names can be verbose, so define the following color constants at the top to make them friendlier to use:

blue = "rgb(97,213,242)"
green = "rgb(150,229,144)"
yellow = "rgb(226,203,98)"
red = "rgb(231,138,138)"

Next, create a container layer to hold everything. Add the following:

container = new Layer
	width: device.width
	height: device.height
	backgroundColor: 'rgba(255, 255, 255 1)'
	borderRadius: 5
	clip: true

This creates a container layer and sets its size equal to that of the device. Finally, you set the backgroundColor to white; this will serve as the background color for the cards. You’ll add all subsequent layers to the container layer.

By setting clip to true, nothing outside the bounds of the container will show.

Deselected State

Onwards to the fun part! You’ll start by creating the deselected screen first.

deselectedScreen

Start with the menu layers. Since you know the width and height of the layers, define them as constants as follows:

menuHeight = container.height/4
menuWidth = container.width

Add the code below and observe what happens:

cookieMenu = new Layer
	height: menuHeight
	width: menuWidth
	x: 0
	y: 0
	backgroundColor: blue

This creates a new layer for your cookie menu, gives it the backgroundColor blue and sets the x origin, the y origin, the width and the height.

Now you’ll do the same thing for the other menus, but keep in mind that the y has to start at the end of the previous layer. The y-position of the current layer is y: previousMenu.y + previousMenu.height:

Add the following code to create the other menus:

cupcakeMenu = new Layer
	height: menuHeight
	width: menuWidth
	x: 0
	y: cookieMenu.y + cookieMenu.height
	backgroundColor: green
 
fruitMenu = new Layer
	height: menuHeight
	width: menuWidth
	x: 0
	y: cupcakeMenu.y + cupcakeMenu.height
	backgroundColor: yellow
 
iceCreamMenu = new Layer
	height: menuHeight
	width: menuWidth
	x: 0
	y: fruitMenu.y + fruitMenu.height
	backgroundColor: red

Next, add some shadow to create the illusion of the menus being on top of one another.

Add the following to the end of every layer definition:

	shadowY: 2
	shadowBlur: 40
	shadowSpread: 3
	shadowColor: "rgba(25,25,25,0.3)"

Hooray! Shadow! But… it’s going the wrong way. That’s because the layers were added on top of each other from top to bottom, with the ice cream menu on top, rather than the other way around.

ShadowWrongWay

Create a function to reposition the menus and bring them on top in the correct order. The signature for a function definition in Framer looks like this:

functionName = ([params]) ->

Add the following function to reposition the menus after your layer definitions:

repositionMenus = () ->
	iceCreamMenu.bringToFront()
	fruitMenu.bringToFront()
	cupcakeMenu.bringToFront()
	cookieMenu.bringToFront()
 
repositionMenus()

The repositionMenus function takes no parameters and brings the menus layers to the front in the order from bottom to top. At the end, you call the function.

Check out the prototype panel – now the shadows should appear in the proper order.

ShadowRightWay

Adding Icons and Titles

Now let’s add some icons and titles to your menus, starting with the cookie menu.

Add the following code to the end of your file:

cookieIcon = sketch.Cookie
cookieIcon.superLayer = cookieMenu
 
cookieText = sketch.CookieText
cookieText.superLayer = cookieMenu

This adds two new variables: cookieIcon and cookieText and sets them equal to the corresponding sketch layers, Cookie and CookieText. You then set the superLayer of both to container.

Your next task is to position these layers now. cookieIcon should go in the center of its superLayer, and cookieText should center horizontally, but position itself 4/5ths of the way down its superLayer. The icon should go in the center of the layer’s superLayer.

Add the following code to center the icon:

cookieIcon.center()

Add the following code to set the position of the text;

cookieText.centerX()
cookieText.y = cookieText.superLayer.height * 0.8

Now just repeat this for the rest of the menus, using the following code:

cookieIcon = sketch.Cookie
cookieIcon.superLayer = cookieMenu
cookieIcon.center()
 
cookieText = sketch.CookieText
cookieText.superLayer = cookieMenu
cookieText.centerX()
cookieText.y = cookieText.superLayer.height * 0.8
 
cupcakeIcon = sketch.Cupcake
cupcakeIcon.superLayer = cupcakeMenu
cupcakeIcon.center()
 
cupcakeText = sketch.CupcakeText
cupcakeText.superLayer = cupcakeMenu
cupcakeText.centerX()
cupcakeText.y = cupcakeText.superLayer.height * 0.8
 
fruitIcon = sketch.Raspberry
fruitIcon.superLayer = fruitMenu
fruitIcon.center()
 
fruitText = sketch.FruitText
fruitText.superLayer = fruitMenu
fruitText.centerX()
fruitText.y = fruitText.superLayer.height * 0.8
 
iceCreamIcon = sketch.IceCream
iceCreamIcon.superLayer = iceCreamMenu
iceCreamIcon.center()
 
iceCreamText = sketch.IceCreamText
iceCreamText.superLayer = iceCreamMenu
iceCreamText.centerX()
iceCreamText.y = iceCreamText.superLayer.height * 0.8

This should give you something that looks very much like the deselected state:

deselectedState1

Whew! You made it. Give yourself a little pat on the pack for getting this far. :]

Refactoring

But, in retrospect, that’s a whole lot of code to make one screen…

notsureif

Just think of how unwieldy your code might be when you’ll have to juggle all the state changes and other details.

Preposterous! This looks like a good time to refactor your code.

Instead of creating each menu layer and its icon and title separately, you’ll create some helper functions to make the code look neater and easier to read.

Replace everything you’ve done so far after the menuHeight and menuWidth definitions with the following:

# 1
menuItems = []
colors = [blue, green, yellow, red]
icons = [sketch.Cookie, sketch.Cupcake, sketch. Raspberry, sketch.IceCream]
titles = [sketch.CookieText, sketch.CupcakeText, sketch.FruitText, sketch.IceCreamText]
 
# 2
addIcon = (index, sup) ->
	icon = icons[index]
	icon.superLayer = sup
	icon.center()
	icon.name = "icon"
 
# 3
addTitle = (index, sup) ->
	title = titles[index]
	title.superLayer = sup
	title.centerX()
	title.y = sup.height - sup.height*0.2
	title.name = "title"
 
# 4
for menuColor, i in colors
	menuItem = new Layer
		height: menuHeight
		width: menuWidth
		x: 0
		y: container.height/4 * i
		shadowY: 2
		shadowBlur: 40
		shadowSpread: 3
		shadowColor: "rgba(25,25,25,0.3)"
		superLayer: container
		backgroundColor: menuColor
		scale: 1.00
	menuItems.push(menuItem)
	addIcon(i, menuItem)
	addTitle(i, menuItem)
 
repositionMenus = () ->
	menuItems[3].bringToFront()
	menuItems[2].bringToFront()
	menuItems[1].bringToFront()
	menuItems[0].bringToFront()
 
repositionMenus()

Let’s review this section by section:

  1. This sets up some arrays to keep track of the menus, colors, icons and titles.
  2. This is a helper function to insert the icon sublayer for each menu item.
  3. This is a helper function to add the title sublayer for each menu item.
  4. This loops through each menu item, creating a new layer and calling the helper functions to add the icon and title. Note that it stores each layer in a menuItems array so you can easily access them later.

And this, ladies and gentleman, is clean code. Onwards to the next challenge!

Transitioning to Selected State

The first step is to add a new state named collapse to the main menuItems loop. Think about this for a second though. What do you need to do to menuItem when it enters the collapse state?

collapseState

You’ll need to transition from an expanded state to a collapsed state.

expandedToCollapsed

Review the changes from before:

  • The y-position of the layer becomes 0.
  • The height goes from 1/4th of the screen to about 1/9ths of the screen.
  • The icon disappears gradually.
  • The y-position of the text changes so the text moves up.
  • You only see the shadow of the selected menuItem

Focus on the easy things first: the height and y-positions for the menuItem. Comment out the two following lines in the for loop, but don’t remove them — you’ll need them later.

# 	addIcon(i, menuItem)
# 	addTitle(i, menuItem)
Note: To comment out a line, press Command + / on the line.

Add the following collapsedMenuHeight constant with the other constants after the container layer:

collapsedMenuHeight = container.height/9

Add the collapse state right before menuItems.push(menuItem), just after the commented out parts:

	menuItem.states.add
		collapse:
			height: collapsedMenuHeight
			y : 0

Now to make the menuItems listen and respond to tap events.

Define the following tap events on each menuItem, just after the menuItem for loop and before repositionMenus:

#onTap listeners
menuItems[0].onTap ->
	for menuItem in menuItems
		menuItem.states.next()
	this.bringToFront()
 
menuItems[1].onTap ->
	for menuItem in menuItems
		menuItem.states.next()
	this.bringToFront()
 
menuItems[2].onTap ->
	for menuItem in menuItems
		menuItem.states.next()
	this.bringToFront()
 
menuItems[3].onTap ->
	for menuItem in menuItems
		menuItem.states.next()
	this.bringToFront()

Every time you tap on a menuItem, you loop through all the menuItems and transition each of them to its next state. this.bringToFront() brings the tapped menuItem to the top. By declaring each tap event separately, it’s easier to change them later.

Note: The keyword this is useful when you’re trying to refer to an object you’re manipulating, instead of using its name explicitly. It’s clearer and, in most cases, shorter and it increases the legibility of your code.

Give it a try to see how your touch interaction works so far:

ScreenFlow

Finishing Touches

So far so good, except you need to add the icon and titles back, and fix a few issues.

To do this, you’ll need to track when a menuItem is selected.

Add the following variable after the menuItems for loop and before the onTap listeners:

selected = false

You initially set selected to false since nothing is selected when you start.

Now you’re ready to start writing the function for switching between selected and deselected states. Add the following code before the repositionMenus function, after the onTap listeners:

# 1
menuStateChange = (currentItem) ->
	# 2
	for menuItem in menuItems
		menuItem.states.next()
	# 3
	if !selected
		currentItem.bringToFront()
	# 4
	else
		repositionMenus()
	# 5
	selected = !selected

This function does the following:

  1. Accepts a parameter currentItem, which is the tapped menuItem.
  2. Iterates through all menuItems and makes each transition to its next state.
  3. If no menuItem was selected, then selected is false, so you place currentItem at the front.
  4. If a menuItem was selected, then selected is true, so you return the menus to their default states with repositionMenus().
  5. Finally, you flip the state of the selected Boolean.

Now you can leverage this function in your onTap implementation. Change the onTap implementation for each as shown below for each menuItem instance, 0 through 3:

menuItems[0].onTap ->
	menuStateChange(this)

Awesome. Now if you look closely, you may notice that when the menu items are compressed, the shadow looks particularly heavy. This is because all four layers have shadows that are stacking on top of each other.

QuadrupleShadow

To fix this, in menuStateChange, change the for loop as follows:

for menuItem in menuItems
	if menuItem isnt currentItem
		menuItem.shadowY = 0
		menuItem.shadowSpread = 0
		menuItem.shadowBlur = 0
	menuItem.states.next()

This hides the shadow for any layer that isn’t the topmost layer, when the layers are collapsed.

Even though the animation looks pretty cool by now, there’s still two key things missing: the icon and the text.

Uncomment the below code found in the menuItems for loop (make sure that these are the last lines in the for loop):

addIcon(i, menuItem)
addTitle(i, menuItem)

Remember when you added names to the icon and title sublayers in addIcon and addTitle? Here’s where those come in handy. Those names will help you distinguish between different sublayers in a menuItem.

Add the following function to collapse the menu, just after menuStateChange():

collapse = (currentItem) ->
	# 1
	for menuItem in menuItems
		# 2
		for sublayer in menuItem.subLayers
			# 3
			if sublayer.name is "icon"
				sublayer.animate
					properties:
						scale: 0
						opacity: 0
						time: 0.3
			# 4
			if sublayer.name is "title"
				sublayer.animate
					properties:
						y: collapsedMenuHeight/2
					time: 0.3

Here’s what’s going on in the code above:

  1. Iterate through each of the menuItems.
  2. For each menuItem, iterate through its subLayers.
  3. If you hit the icon sublayer, animate the scale to 0 and the opacity to 0 over the space of 0.3 seconds.
  4. When you hit the title sublayer, animate the y-position to the middle of the current menu.

Now, add the following function immediately below the one you just added:

expand = () ->
	# 1
	for menuItem in menuItems
		# 2
		for sublayer in menuItem.subLayers
			# 3
			if sublayer.name is "icon"
				sublayer.animate
					properties:
						scale: 1
						opacity: 1
					time: 0.3
			# 4
			if sublayer.name is "title"
				sublayer.animate
					properties:
						y: menuHeight * 0.8
					time: 0.3

Taking it step-by-step:

  1. Iterate through each of the menuItems.
  2. For each menuItem, iterate through its subLayers.
  3. If you hit the icon sublayer, animate the scale to 100% and the opacity to 1 over the space of 0.3 seconds.
  4. When you hit the title sublayer, animate the y-position to menuHeight * 0.8.

Add the calls to collapse() and expand() to menuStateChange() as shown below:

menuStateChange = (currentItem) ->
	# remove shadow for layers not in front
	for menuItem in menuItems
		if menuItem isnt currentItem
			menuItem.shadowY = 0
			menuItem.shadowSpread = 0
			menuItem.shadowBlur = 0
		menuItem.states.next()
	if !selected
		currentItem.bringToFront()
		collapse(currentItem)
	else
		expand()
		repositionMenus()
	selected = !selected

Check out the prototype panel and now you’ll see the icon and title animate properly as well:

ScreenFlow2

You’re almost at the finish line! You don’t have much farther to go! :]

Animation Settings

You can animate every layer in Framer; you can fine tune the animation by setting the following values:

  • properties: width, height, scale, borderRadius and others; the layer will animate from whatever state it is in to what you define here.
  • time: How long the animation lasts.
  • delay: If there should be a delay before the animation, and how long the delay should be.
  • repeat: How many times to repeat the animation.
  • curve: How fast the animation should run.
  • curve options: Fine-tuning options for the animation curve.

The most confusing two above are the curve and the curve options setting. You can use this tool to prototype animation curves with options: Framer.js Animation Playground.

Since you haven’t defined an animation curve for any of the animations, the prototype uses Framer’s default curve, ease:

badAnimation

This curve makes the prototype look stiff and unnatural. The curve you want is a spring curve that lets you have more control over what happens at each step of the transformation.

Time to translate the words used to describe these animations into numbers through the curveOptions settings.

This animation calls for a simple spring curve with the following parameters:

  • tension: How “stiff” the curve should be. The bigger the number, the more speed and bounce the animation should have.
  • friction: How much resistance to apply. The bigger the number, the more damped the animation will be.
  • velocity: The initial velocity of the animation.

Often you won’t just know what these numbers are. You will have to play around until you’re satisfied.

There are two animations with different speeds going on here:

Animating the menus: Starts slow, speeds up a lot, then slows down dramatically:

animatingMenu

Animating the icon and title: The icon size goes from 100% to 0%, and the animation is fairly sudden. It starts fast, but finishes with a lot of damping:

animationIcons

Compared to the previous animation, this one has a lot less tension and the transitions between the different speeds are quicker. Give it a similar friction number for that nice damping effect and a small initial velocity.

Add the following just before menuItems.push(menuItem):

menuItem.states.animationOptions =
	curve: "spring"
	curveOptions:
		tension: 200
		friction: 25
		velocity: 8
	time: 0.25

This sets a spring animation for the menus and sets tension to 200, friction to 25, and velocity to 8. It runs just a little faster than the icon and title animations.

Find each instance of sublayer.animate, and add the following under the time line in the properties section, matching the indentation:

curve: "spring"
curveOptions:
	tension: 120
	friction: 18
	velocity: 5

This will add a similar spring animation to the titles and icons, but they will be a little less springy and move a little slower than the menu sections.

You’ll add this code in four times in total: twice under collapse and twice under expand for both the icon and the title subLayers.

To compare your results, here’s an example of the collapse function with the required indentation:

collapse = (currentItem) ->
	for menuItem in menuItems
		for sublayer in menuItem.subLayers
			if sublayer.name is "icon"
				sublayer.animate
					properties:
						scale: 0
						opacity: 0
					time: 0.3
					curve: "spring"
					curveOptions:
						tension: 120
						friction: 18
						velocity: 5
			if sublayer.name is "title"
				sublayer.animate
					properties:
						y: collapsedMenuHeight/2
					time: 0.3
					curve: "spring"
					curveOptions:
						tension: 120
						friction: 18
						velocity: 5

Here’s how things should look after you’re done:

finishedAnim

Where to Go From Here?

And that’s a wrap! Congrats on your first Framer prototype :]

You can download the finished project here. It goes a bit further than this tutorial in that it shows you how to display the cards inside each menu item as well. The complete project, along with this same prototype created in Xcode + Swift are included.

If you have any questions or comments on this tutorial, please feel free to join the discussion in the forums below!

The post Using Framer to Prototype iOS Animations appeared first on Ray Wenderlich.

Firebase Tutorial: Getting Started

$
0
0
Firebase tutorial

Learn the fundamentals in the Firebase tutorial.

Update note: This tutorial has been updated for iOS 10 and Swift 3 by Attila Hegedüs. The original tutorial was written by David East.

Firebase is a mobile-backend-as-a-service that provides several features for building powerful mobile apps. Firebase has three core services: a realtime database, user authentication and hosting. With the Firebase iOS SDK, you can use these services to build powerful apps without writing a single line of server code.

The realtime database is one of the most unique features of Firebase.

Ever used pull-to-refresh to fetch new data? You can forget about it with Firebase.

When a Firebase database updates, all connected users receive updates in realtime. This means your app can stay up to date without user interaction.

In this Firebase tutorial, you’ll learn all the fundamentals of Firebase by making a collaborative grocery list app called Grocr. When items get added to the list they’ll appear instantly on any user’s devices, but you’re not going to stop there. You’ll tweak Grocr to work offline, so the list stays in sync even with a spotty grocery store data connection.

As you work, you’ll learn about:

  • Saving data to a Firebase database
  • Syncing data from Firebase in realtime
  • Authenticating users
  • Monitoring online users
  • Enabling offline support

Get ready to realtime all the things!

Getting Started

Download the Grocr starter project. It uses CocoaPods to manage the Firebase dependency and contains a basic working project.

Open Grocr.xcworkspace in Xcode. This project contains three view controllers:

  1. LoginViewController.swift. Right now the login is using hard coded user credentials, but you’ll fix that soon.
  2. GroceryListTableViewController.swift. This controller is a subclass of UITableViewController that adds items to a list of local data using a UIAlertController.
  3. OnlineUsersTableViewController.swift. This controller will use Firebase’s presence feature to display all of the users currently online.

In addition to the view controllers, there are two models, defined in GroceryItem.swift and User.swift. These are just two structs that serve as models for your data.

Build and run, and you’ll see the app looks like this:

Grocr-Starter

Note: You may see some ‘nullability’ compiler warning messages when you build the project. These are from the Firebase code itself so there’s not much you can do about them. You can just ignore them.

You can log in just by tapping Login, which will use a hard-coded user. If you play around, you’ll see that the app currently only works with local data. Next, you’ll use Firebase to bring the app to life.

Setting up a Firebase Account

There are two main steps to setting up Firebase in an iOS project:

  1. Create a free Firebase account;
  2. Get the URL of your first app.

To create an account, visit the Getting Started page. After logging in with your Google account for the first time, you’ll have a clean Firebase console created for you. Don’t worry about forking over any money – everything covered today falls well within Firebase’s free plan.

01-firebase-welcome

It’s time to create your first project, so click CREATE NEW PROJECT.

In the dialog that appears, enter Grocr as the Project Name and specify your preferred Country/region:

02-firebase-create-project

Click CREATE PROJECT, and you will be taken to the dashboard where you can manage your Firebase project:

03-firebase-dashboard

This will serve as the container for all Firebase services. You’ll use it to store data and authenticate your users.

To get started, select Add Firebase to your iOS app. The sample project’s bundle ID is rw.firebase.gettingstarted, so enter this in the iOS bundle ID field:

04-firebase-add-ios-app-1

Click ADD APP and a file called GoogleService-Info.plist will automatically be downloaded. Follow the instructions and move it to the Grocr project in Xcode:

04-firebase-add-ios-app-2

Click CONTINUE. The next page describes how to install the Firebase SDK installed:

04-firebase-add-ios-app-3

The sample project already has this installed for you, so just hit CONTINUE. The last page explains how to connect to Firebase when your app starts:

04-firebase-add-ios-app-4

Just hit FINISH to see your new project’s details:

04-firebase-add-ios-app-5

That’s it!

Creating a Connection to Firebase

With your Firebase app set up, go to Xcode and open GroceryListTableViewController.swift. Where the properties are defined, add the following:

let ref = FIRDatabase.database().reference(withPath: "grocery-items")

This establishes a connection to your Firebase database using the provided path. In the documentation, these Firebase properties are referred to as references because they refer to a location in your Firebase database.

In short, this property allows for saving and syncing of data to the given location.

You’ll notice that the base URL is not used. Instead, it uses a child path of grocery-items. The Firebase database is a JSON NoSQL database, so all data is stored as JSON.

JSON is a hierarchical key-value data structure—keys refer to objects that can contain values pointing to other objects. JSON data is simply a tree of key value pairs.

With Firebase, the key is a URL and the value is arbitrary data that could be a number, string, boolean or object.

Structuring Data

No matter how it’s formatted on the client, all data stored in Firebase is JSON. Take a look at the following sample JSON data:

// The root of the tree
{
  // grocery-items
  "grocery-items": {
 
    // grocery-items/milk
    "milk": {
 
      // grocery-items/milk/name
      "name": "Milk",
 
      // grocery-items/milk/addedByUser
      "addedByUser": "David"
    },
 
    "pizza": {
      "name": "Pizza",
      "addedByUser": "Alice"
    },
  }
}

In the JSON tree above, you can see there’s a path mapped to every piece of data. You can continue to traverse down the tree and retrieve data at deeper locations.

In the case of the data above, you can retrieve all grocery items by using the path:

grocery-items

If you only want to get the first grocery item you can navigate to a child path using the path:

grocery-items/milk

Since all Firebase keys map to paths, the key names you choose are especially important.

Understanding Firebase References

A fundamental theme to grasp is that a Firebase reference points to a location in Firebase where data is stored. If you create multiple references, then they all share the same connection.

Have a look at this sample code:

// 1
let rootRef = FIRDatabase.database().reference()
 
// 2
let childRef = FIRDatabase.database().reference(withPath: "grocery-items")
 
// 3
let itemsRef = rootRef.child("grocery-items")
 
// 4
let milkRef = itemsRef.child("milk")
 
// 5
print(rootRef.key)   // prints: ""
print(childRef.key)  // prints: "grocery-items"
print(itemsRef.key)  // prints: "grocery-items"
print(milkRef.key)   // prints: "milk"

Here’s what’s going on:

  1. You create a reference to the root of the Firebase database.
  2. Using a URL, you can create a reference to a child location in your Firebase database.
  3. From the rootRef you can use child(_:) to create a child reference by passing the child path. This reference is the same as the one above.
  4. Using the itemsRef, you can create a child reference to the milk location.
  5. Every reference has a key property. This property tells you what the key name is in the Firebase database.

You don’t need to add this code anywhere in the sample project, it’s purely for illustrative purposes. :]

Adding New Items to the List

Near the bottom of GroceryListTableViewController.swift, find addButtonDidTouch(_:).

This is where you present the user with a UIAlertController to add a new item.

Inside the method, locate saveAction. Currently, it only saves the data to a local array, so saveAction won’t sync across multiple clients and disappears when you restart the app.

Nobody’s going to want to use an app that doesn’t remember or sync their grocery list! Replace saveAction with the following:

let saveAction = UIAlertAction(title: "Save",
                               style: .default) { _ in
    // 1
    guard let textField = alert.textFields?.first,
      let text = textField.text else { return }
 
    // 2
    let groceryItem = GroceryItem(name: text,
                           addedByUser: self.user.email,
                             completed: false)
    // 3
    let groceryItemRef = self.ref.child(text.lowercased())
 
    // 4
    groceryItemRef.setValue(groceryItem.toAnyObject())
}

Here’s what’s going on:

  1. Get the text field (and its text) from the alert controller.
  2. Using the current user’s data, create a new GroceryItem that is not completed by default.
  3. Create a child reference using child(_:). The key value of this reference is the item’s name in lowercase, so when users add duplicate items (even if they capitalize it, or use mixed case) the database saves only the latest entry.
  4. Use setValue(_:) to save data to the database. This method expects a Dictionary. GroceryItem has a helper function called toAnyObject() to turn it into a Dictionary.

Before you can connect to the database, you need to configure it. Head over to AppDelegate.swift and in application(_:didFinishLaunchingWithOptions:) add the following just before the return true:

FIRApp.configure()

Also, by default, the Firebase database requires user authentication for reading and writing. To change it, go to the Firebase dashboard in your browser and select the Database option on the left, followed by the RULES tab and set the rules as follows:

firebase-db-rules

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Then select the PUBLISH button to save your changes.

Build and run. In the Firebase dashboard, select the DATA tab and position the browser window next to the simulator. When you add an item in the simulator, you’ll see it appear in the dashboard:

fb-save

Now you have a grocery list app that adds data to Firebase in realtime! So this key feature is working correctly, but none of the items are added to the table view.

So, how about you get that data synchronizing from the database to the table view?

Retrieving Data

You retrieve data in Firebase by attaching an asynchronous listener to a reference using observeEventType(_:withBlock:).

Add the following to viewDidLoad() in GroceryListTableViewController.swift:

ref.observe(.value, with: { snapshot in
  print(snapshot.value)
})

This function takes two parameters: an instance of FIRDataEventType and a closure.

The event type specifies what event you want to listen for. The code listens for a .value event type, which in turn listens for all types of changes to the data in your Firebase database—add, removed, and changed.

When the change occurs, the database updates the app with the most recent data.

The app is notified of the change via the closure, which is passed an instance of FIRDataSnapshot. The snapshot, as its name suggests, represents the data at that specific moment in time. To access the data in the snapshot, you use the value property.

Build and run and you’ll see list items logged to the console as they’re added:

Optional({
    pizza =     {
        addedByUser = "hungry@person.food";
        completed = 0;
        name = Pizza;
    };
})

Synchronizing Data to the Table View

So that took care of logging—now to actually display the grocery list in your table view.

In GroceryListTableViewController.swift, replace the previous snippet with the following:

// 1
ref.observe(.value, with: { snapshot in
  // 2
  var newItems: [GroceryItem] = []
 
  // 3
  for item in snapshot.children {
    // 4
    let groceryItem = GroceryItem(snapshot: item as! FIRDataSnapshot)
    newItems.append(groceryItem)
  }
 
  // 5
  self.items = newItems
  self.tableView.reloadData()
})

Here’s what happening:

  1. Attach a listener to receive updates whenever the grocery-items endpoint is modified.
  2. Store the latest version of the data in a local variable inside the listener’s closure.
  3. The listener’s closure returns a snapshot of the latest set of data. The snapshot contains the entire list of grocery items, not just the updates. Using children, you loop through the grocery items.
  4. The GroceryItem struct has an initializer that populates its properties using a FIRDataSnapshot. A snapshot’s value is of type AnyObject, and can be a dictionary, array, number, or string. After creating an instance of GroceryItem, it’s added it to the array that contains the latest version of the data.
  5. Reassign items to the latest version of the data, then reload the table view so it displays the latest version.

Build and run. Add an item—how about some pizza? —it shows up in the table view.

fb-sync

No pull-to-refresh required to get the list to update in real time!

realtime-updates

Removing Items From the Table View

The table view will synchronize on any kind of change to your data, but right now there’s nothing to update Firebase when the user decides not to get that pizza.

To notify the database of a deletion, you need to set a Firebase reference to delete an item when the user swipes it away.

Locate tableView(_:commit:forRowAt:). Right now, this method removes a grocery item from the local array using the index path’s row. It works, but there’s a better way. Replace the existing implementation with the following:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
  if editingStyle == .delete {
    let groceryItem = items[indexPath.row]
    groceryItem.ref?.removeValue()
  }
}

Firebase follows a unidirectional data flow model, so the listener in viewDidLoad() notifies the app of the latest value of the grocery list. A removal of an item triggers a value change.

The index path’s row is used to retrieve the corresponding grocery item. Each GroceryItem has a Firebase reference property named ref, and calling removeValue() on that reference causes the listener you defined in viewDidLoad() to fire. The listener has a closure attached that reloads the table view using the latest data.

Build and run. Swipe an item, tap delete, and watch it vanish from both your app and in Firebase.

fb-delete

Nice work! Your items now delete in realtime.

Checking Off Items

Now you know how to add, remove, and sync items, and that’s all pretty cool. But what about when you’re actually shopping? Should you just delete stuff that you’ve got, or would it be better to mark things off as you add them to your basket?

Back in the analog days of pens and paper, people used to cross stuff off the grocery list, so you’ll mimic that familiar behavior in this app, but with a modern twist!

When tapped, items should turn gray and show a checkmark to give the user some visual feedback that the item is no longer needed.

grocery-list

Open GroceryListTableViewController.swift and find toggleCellCheckbox(_:isCompleted:). This method toggles the necessary view properties for UITableViewCell, depending on whether its associated item is complete.

It’s called from within tableView(_:cellForRowAtIndexPath:) when the table view is first loaded, and from within tableView(_:didSelectRowAt:) when the user taps on a row.

Replace the current implementation of tableView(_:didSelectRowAt:) with the following:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  // 1
  guard let cell = tableView.cellForRow(at: indexPath) else { return }
  // 2
  let groceryItem = items[indexPath.row]
  // 3
  let toggledCompletion = !groceryItem.completed
  // 4
  toggleCellCheckbox(cell, isCompleted: toggledCompletion)
  // 5
  groceryItem.ref?.updateChildValues([
    "completed": toggledCompletion
  ])
}

Here’s the play-by-play of what’s happening:

  1. Find the cell the user tapped using cellForRow(at:).
  2. Get the corresponding GroceryItem by using the index path’s row.
  3. Negate completed on the grocery item to toggle the status.
  4. Call toggleCellCheckbox(_:isCompleted:) to update the visual properties of the cell.
  5. Use updateChildValues(_:), passing a dictionary, to update Firebase. This method is different than setValue(_:) because it only applies updates, whereas setValue(_:) is destructive and replaces the entire value at that reference.

Build and run. Tap on an item and see that it toggles back and forth between the complete and incomplete statuses.

fb-toggle

Congratulations, you’ve got yourself a pretty sweet grocery list app now!

Sorting the Grocery List

You know how sometimes you forget to pick up that ice cream because it’s nestled between a couple of things you’ve already marked off and your eyes played tricks on you? Well you, dear reader, can fix that.

The app would be 10x more awesome if checked items moved themselves to the bottom of the list automatically. Then the remaining items would be clear and easy for your eyes to see.

Using Firebase queries, you can sort the list by arbitrary properties. Still working in GroceryListTableViewController.swift, update the observer in viewDidLoad() as follows:

ref.queryOrdered(byChild: "completed").observe(.value, with: { snapshot in
  var newItems: [GroceryItem] = []
 
  for item in snapshot.children {
    let groceryItem = GroceryItem(snapshot: item as! FIRDataSnapshot)
    newItems.append(groceryItem)
  }
 
  self.items = newItems
  self.tableView.reloadData()
})

To order the data by the completed value you call queryOrdered(byChild:) on the Firebase reference, which takes a key to order by.

Since the list needs to order by completed, the key completed is passed to the query. Then, queryOrdered(byChild:) returns a reference that informs the server to return data in an ordered fashion.

Build and run. Tap on a row to toggle its completion status. The completed items magically move to the bottom of the list.

fb-order

Wow! You’re really making grocery shopping easier here. Seems like it should be simple enough to sync the data across multiple users, for instance, with a significant other or housemate. This sounds like a job for…authentication!

Authenticating Users

Firebase has an authentication service that allows apps to authenticate through several providers. You can authenticate users with Google, Twitter, Facebook, Github, email & password, anonymous, and even custom backends. Here you’ll use email and password because it’s the easiest to set up.

To enable email and password authentication go to the Firebase dashboard and click on Auth.

fb-auth-1

Select the SIGN-IN METHOD tab and then, in the Sign-in providers section, select the Email/Password row. Hit the Enable switch and click SAVE:

authentication

Firebase stores credentials in the keychain, so as the last step enable Keychain Sharing by navigating to your target’s Capabilities and toggling Keychain Sharing.

keychain-sharing

Now you’re ready to authenticate your users using their email and password!

Registering Users

In LoginViewController.swift, find signUpDidTouch(_:). This presents a UIAlertController that allows the user to register for an account. Locate saveAction and add the following to its closure:

// 1
let emailField = alert.textFields![0]
let passwordField = alert.textFields![1]
 
// 2
FIRAuth.auth()!.createUser(withEmail: emailField.text!,
                           password: passwordField.text!) { user, error in
  if error == nil {
    // 3
    FIRAuth.auth()!.signIn(withEmail: self.textFieldLoginEmail.text!,
                           password: self.textFieldLoginPassword.text!)
  }
}

Here’s what’s going on in the above code:

  1. Get the email and password as supplied by the user from the alert controller.
  2. Call createUser(withEmail:password:) on the default Firebase auth object passing the email and password.
  3. If there are no errors, the user account has been created. However, you still need to authenticate this new user, so call signIn(withEmail:password:), again passing in the supplied email and password.

Build and run. Tap the Sign up button and enter an email and a password, then tap save. The view controller won’t navigate to anything on successful login just yet. If you refresh the Firebase Login & Auth tab you’ll see the newly created user.
fb-register-user

W00T! The app now registers users and then lets them log in. Don’t celebrate yet though, you need to finish the process so people can actually use the app as intended.

Logging Users In

The Sign up button can register and log in users. However, the Login button effectively does nothing because no authentication is performed.

Still working in LoginViewController.swift, find loginDidTouch(_:) and modify it so it matches the following:

@IBAction func loginDidTouch(_ sender: AnyObject) {
    FIRAuth.auth()!.signIn(withEmail: textFieldLoginEmail.text!,
                            password: textFieldLoginPassword.text!)
}

This code will authenticate the user when they attempt to log in by tapping the Login button.

You now need to perform the segue to the next controller only when the user has logged in.

Observing Authentication State

Firebase has observers that allow you to monitor a user’s authentication state. This is a great place to perform a segue.

Add the following to LoginViewController:

override func viewDidLoad() {
  super.viewDidLoad()
 
  // 1
  FIRAuth.auth()!.addStateDidChangeListener() { auth, user in
    // 2
    if user != nil {
      // 3
      self.performSegue(withIdentifier: self.loginToList, sender: nil)
    }
  }
}

Here’s a run-down of what’s happening:

  1. Create an authentication observer using addStateDidChangeListener(_:). The block is passed two parameters: auth and user.
  2. Test the value of user. Upon successful user authentication, user is populated with the user’s information. If authentication fails, the variable is nil.
  3. On successful authentication, perform the segue. Pass nil as the sender. This may seem strange, but you’ll set this up in GroceryListTableViewController.swift.

Setting the User in the Grocery List

Go to GroceryListTableViewController.swift, and add the following to the bottom of viewDidLoad():

FIRAuth.auth()!.addStateDidChangeListener { auth, user in
  guard let user = user else { return }
  self.user = User(authData: user)
}

Here you attach an authentication observer to the Firebase auth object, that in turn assigns the user property when a user successfully signs in.

Build and run. If a user is logged in, they bypass LoginViewController and segue to the GroceryListTableViewController. When users add items, their email will show in the detail of the cell.

fb-user-add

Success! The app now has basic user authentication.

Monitoring Users’ Online Status

Now that the app has user authentication, its time to detect which users are online. Open GroceryListTableViewController.swift and add the following property:

let usersRef = FIRDatabase.database().reference(withPath: "online")

This is a Firebase reference that points to an online location that stores a list of online users.

Next, add the following to the bottom of the addStateDidChangeListener(_:) closure inside viewDidLoad():

// 1
let currentUserRef = self.usersRef.child(self.user.uid)
// 2
currentUserRef.setValue(self.user.email)
// 3
currentUserRef.onDisconnectRemoveValue()

The code above follows these steps:

  1. Create a child reference using a user’s uid, which is generated when Firebase creates an account.
  2. Use this reference to save the current user’s email.
  3. Call onDisconnectRemoveValue() on currentUserRef. This removes the value at the reference’s location after the connection to Firebase closes, for instance when a user quits your app. This is perfect for monitoring users who have gone offline.

Build and run. When the view loads the current user’s email is added as a child in the online location.

fb-monitoring

Great! Now it’s time to change the number of the bar button item as the user count grows.

Updating the Online User Count

Still working in GroceryListTableViewController.swift, add the following code to viewDidLoad():

usersRef.observe(.value, with: { snapshot in
  if snapshot.exists() {
    self.userCountBarButtonItem?.title = snapshot.childrenCount.description
  } else {
    self.userCountBarButtonItem?.title = "0"
  }
})

This creates an observer that is used to monitor online users. When users go on-and-offline, the title of userCountBarButtonItem updates with the current user count.

Displaying a List of Online Users

Open OnlineUsersTableViewController.swift and, just as you did before, add a local reference to Firebase’s online users record in the class’s property section:

let usersRef = FIRDatabase.database().reference(withPath: "online")

Then, in viewDidLoad(), replace:

currentUsers.append("hungry@person.food")

with the following:

// 1
usersRef.observe(.childAdded, with: { snap in
  // 2
  guard let email = snap.value as? String else { return }
  self.currentUsers.append(email)
  // 3
  let row = self.currentUsers.count - 1
  // 4
  let indexPath = IndexPath(row: row, section: 0)
  // 5
  self.tableView.insertRows(at: [indexPath], with: .top)
})

Here’s what’s happening in the code:

  1. Create an observer that listens for children added to the location managed by usersRef. This is different than a value listener because only the added child is passed to the closure.
  2. Take the value from the snapshot, and then append it to the local array.
  3. The current row is always the count of the local array minus one because the indexes managed by the table view are zero-based.
  4. Create an instance NSIndexPath using the calculated row index.
  5. Insert the row using an animation that causes the cell to be inserted from the top.

This will only render items as they are added rather than reloading the entire list, and it also gives you the ability to specify a nice animation. :]

Since users can go offline, the table needs to react to users being removed as well. Add the following below the code you just added:

usersRef.observe(.childRemoved, with: { snap in
  guard let emailToFind = snap.value as? String else { return }
  for (index, email) in self.currentUsers.enumerated() {
    if email == emailToFind {
      let indexPath = IndexPath(row: index, section: 0)
      self.currentUsers.remove(at: index)
      self.tableView.deleteRows(at: [indexPath], with: .fade)
    }
  }
})

This simply adds an observer that listens for children of the usersRef reference being removed. It searches the local array for the email value to find the corresponding child item, and once located, it deletes the associated row from the table.

Build and run.

Tap Online in the Firebase users dashboard, and the current user’s email will appear in the table. Using a bit of trickery, it’s possible to add a user to Online, and once you do, it shows in the list. Click the Remove button in the Dashboard and the user fades from existence….
fb-users-table

Booyah! The table updates when users are added and removed.

monintoring-users

Enabling Offline

Grocery stores are notorious for spotty data connections. You’d think they’d all have Wi-Fi now, but no!

No problem, you’ll just set up your database to work offline. Open AppDelegate.swift and add the following to the end of application(_:didFinishLaunchingWithOptions:), before return true:

FIRDatabase.database().persistenceEnabled = true

Yup, that’s it! Just like that your app works offline. Even offline updates that occur across app restarts will apply to your Firebase database once a connection is made. Oooh-ahhhh!

Where To Go From Here?

You can download the completed version of Grocr here.

Note:You still have to add your own GoogleService-Info.plist and enable Keychain sharing after downloading the final project.

Throughout this Firebase tutorial, you’ve learned the basics of Firebase by building a collaborative grocery list app. You’ve implemented saving data to a Firebase database, syncing data in realtime, authenticating users, monitoring online user status, and enabling offline support. And you did all this without writing a single line of server code! :]

If you’re curious about Firebase, please check out the documentation, as well as the examples provided by Firebase themselves.

If you have any comments or questions about this Firebase tutorial, Firebase, or the sample app, please join the forum discussion below!

The post Firebase Tutorial: Getting Started appeared first on Ray Wenderlich.

Making Vesper, NetNewsWire & OmniOutliner: A Top Dev Interview With Brent Simmons

$
0
0
Brent Simmons at Golden Gardens beach, staring at baby unicorns in the sand, that are on fire, at sunset.

Brent Simmons at Golden Gardens beach, staring at baby unicorns in the sand, that are on fire, at sunset.

Welcome to another installment of our Top App Dev Interview series. Each interview in this series focuses on a successful mobile app or developer and the path they took to get where they are today. Today’s special guest is Brent Simmons.

Brent is a prolific technologist, technology writer and speaker. He is best known for being a developer at The Omni Group, the developer of Vesper and creator of NetNewsWire.

When he’s not busy programming, Brent is either writing on his blog or speaking at various conferences around the world, including AltConf & CocoaConf.

Focus

Brent, you’ve worked on a number of popular apps such as NetNewsWire, MarsEdit, Glassboard, Vesper, OmniFocus, and more. Out of all of these apps you’ve worked on, what is the one you are most proud of, and why?

The answer probably depends on my mood — I’d give a different answer every time you ask!

But today — maybe because my old boss and friend Dave Winer recently visited — I’d say Manila. Manila was a very early blogging system that shipped in 1999, when I was working at UserLand Software. After Dave had the idea and did the foundation and design, I took over as lead developer and shipped it.

And it was awesome. It had “Edit this Page“ buttons so that if you were looking at something on your blog, you could edit it. This was new at the time. We had hundreds of thousands of users on our free service — some well-known blogs such as Daily Kos and Robert Scoble got started on Manila.

This was the first big project I ever did and, even though it’s not still around, it did help start the blogging revolution, which was a big part of the open web, which is one of my passions.

You’ll note that almost everything I’ve done — certainly the best things — have to do with reading and writing on the web. That’s been the theme of my career.

That’s a great point. In addition to being well known for development, you also are a prolific blogger, podcaster, and conference speaker. These kinds of things obviously take a lot of time and energy – why do you do it and what benefits have you seen in your life from these pursuits?

Although Manila is the product I’m most proud of, if we expanded this to ask what *thing* I’m most proud of, the answer would be obvious: my blog. I’ve been writing it for 17 years. When I was a kid I wanted to grow up to be a writer, and I *am* a writer. My blog isn’t a side thing: it’s the main achievement of my career.

I wasn’t sure that I’d like speaking at conferences. I’m extremely introverted. I like reading and writing, which are things I can do alone in a room. But, to my surprise, I found there’s a part of me that’s a little bit of a ham. I like performing for a crowd — and I definitely think of speaking as a type of performance. I even like playing music for people as a Conditional Breakpoint.

My uncle is an actor. I never understood the appeal of acting, and I figured he was from outer space — until, sometime in my 40s, I started to become comfortable on stage, and I started to understand and really enjoy the connection you can make with an audience. Now I get it, at least a little bit.

What I try to do when speaking is pretend it’s just you and me sitting in a bar. I shoot for relaxed and conversational and intimate and, hopefully, a little funny. I tell stories. I’m not afraid to be weird. I prepare a bunch (more as time goes by), but with the goal of making it seem like nothing, as if I just got up and started talking to you. (It should be no surprise that my approach to writing is similar.)

Let’s go back to apps. Some of your apps are now managed by other companies – for example, NetNewsWire is run by Black Pixel, and MarsEdit is now run by Red Sweater. Why did you decide to transition these to other companies rather than maintain the apps yourself, and how did you perform the transition in practice?

NetNewsWire by Brent Simmons

NetNewsWire by Brent Simmons

With MarsEdit, I had just started working at NewsGator. NewsGator’s initial focus as a company was RSS, and so NetNewsWire was the priority. I couldn’t do a good job with both NetNewsWire and MarsEdit, so rather than shut down MarsEdit, we found it a great home with Daniel Jalkut.

We sold NetNewsWire to Black Pixel many years later. I’d been working on NetNewsWire for nine years, and it was clear to me that to do syncing well and maintain iPhone, iPad, and Mac versions, it needed a bigger team. At the same time I had an opportunity to start a new company (Sepia Labs) and create a new group messaging app: Glassboard. There was no way I could work on NetNewsWire part time, when what it needed was more people, not half of me. So we looked for a good home.

Part of the transition is coming up with a deal that both sides like. These tend to involve some cash, some revenue-sharing for a period of time, and some incentives for the purchasing company to put resources into the app.

The second part is turning over the code and related resources. I’ve always made myself available for technical assistance after the sale, but there’s been very little of that needed.

Given your success and senior status, you must have a large number of ideas for new apps to work on, or opportunities to collaborate with other people on new projects. How do you decide which opportunities to pursue?

This part has actually gotten easier. I’m 48 years old right now, and I have a day job. This means I can do one or two more serious apps, and that’s it. Probably just one.

It happens that there are just two app ideas I’ve been obsessed with for a couple decades. I picked the one of the two I’m most passionate about, and I’m working on that.

There are other ideas and opportunities that come up that sound really, really fun. But then I remember that time is limited, and there’s this thing I want to make and maintain for the rest of my career, and that’s that. So I stick with that one thing.

Surviving as an Indie

Recently, you and John Gruber announced that you are shutting down your app Vesper, which although it was popular hasn’t earned enough money to make it a financial success. If you had to pinpoint the #1 reason why Vesper didn’t earn as much as you had hoped, what would you say it was?

Vesper by John Gruber, Brent Simmons, and Dave Wiskus

Vesper by John Gruber, Brent Simmons, and Dave Wiskus

If we had done as John suggested in his recent post on Daring Fireball — designed the iOS app first, but then shipped the Mac app before shipping the iOS app — I believe it would have had a much better shot.

Also: I’d probably use Cloudkit for syncing. It didn’t exist when we started, and then it did exist but didn’t have a web API, and we were strongly interested in doing a web version of Vesper. Now that Cloudkit *does* have a web API, it would have suited our needs. This would have saved us a bunch of time and money.

This isn’t the isn’t first time that you pointed out that it’s hard to make a full-time living as an indie iOS developer these days, due to the saturated market, downward price pressure, and other problems. However you have pointed out it’s easier to be an indie macOS developer. Why do you think indie Mac developers are having more success?

Some people are finding success on the iOS App Store — and I congratulate those folks. This includes Omni, where I work.

But I do think it’s far more difficult to make a living as an indie iOS developer in 2016 than it was as a Mac developer in 2005. My suspicion is that in the Mac market, 2016 is not very different from 2005, and you can still make money there. But iOS is like a giant curtain laid across the map, so people don’t see the opportunity.

The thing about Mac users, at least with productivity apps, is that they’ve always been willing to pay for value. They love their Macs, and they love apps that help them get their work done, and they’re very happy to root for and support indie developers. Mac users *want* great apps, and they want us to succeed.

It’s a much smaller market than iOS, sure, but it’s full of people who love apps and let it show. I love writing Mac apps in large part *because* of Mac users. They’re the best. They’re our partners.

What advice would you give someone who would love to quit their job and try being an indie developer?

In 2008 I wrote Advice for Indies, which includes these lines:

“But you have to actually build it. You have to work every day. You have to sit in the chair and stay seated. And sleep and come back to the chair. You need to wear out that chair and then buy a new one and then wear out that one.”

I’ve worn out a few chairs. So much of it is work ethic. Keep doing it. You might want to go play Destiny or watch Doctor Who — but you’ve got a feature to finish and bugs to fix.

That’s not to say that you shouldn’t have a life. You should, absolutely. But recognize that it really does take a lot of constant work. You have to be in that chair.

Brent's desk at The Omni Group using his favourite camera app, Bitcam.

Brent’s workspace at The Omni Group using his favourite camera app, BitCam.

Indie Dev vs. Full-Time

You have a great history of your own successful projects as an indie developer, and also working within a team like The Omni Group. Which do you prefer and why?

OmniGraffle, one of the many apps made by the Omni Group.

OmniGraffle, one of the many apps made by the Omni Group.

They’re different, for sure, but in each case development is very social. It’s rare that great apps come fully-formed from the head of a solo developer.

I wasn’t sure if I’d like the more traditional environment of Omni. I take the bus to work, sit in my office, have lunch with the company (at 12:30 every day), and talk to and communicate with people throughout the day. It’s nice to be on a team. Everyone pulls their weight, and the company is full of smart and dedicated people. It turns out I *do* like it.

It’s also nice that when I’m not there, I’m not there. As an indie I worked at home and thus I was always at work. It’s good for the soul to have a dividing line.

Can you please describe the process of moving from prototype to shipped feature within The Omni Group, and how that compares to a similar process when you are an indie developer?

At Omni and at Q Branch the process is pretty similar: the designers figure out how a thing should work, then write it up and provide mock-ups — and then I ask questions, and that may or may not trigger further revisions — and then I go and build the feature. Which may then trigger further design and revisions, sometimes small and sometimes large. It’s iterative.

For my personal projects, I’m both designer and builder — and so I run a bunch of things (though not every single thing) by my beta testers. Sometimes I absolutely know what I want and I do it and there’s no need for early feedback. Other times I just need a sanity check. Other times I’m trying to pick from a few options. And then sometimes I really just have no clue how feature X should work.

And, of course, my beta testers — who are so much more than beta testers, since they’re in on everything from way before the beta — often have ideas I hadn’t thought of. They’re absolutely critical to building great apps.

Swift

What do you think of Swift? Are you using it in your projects? If so, what benefits or challenges have you found using it?

Brent is in love with Swift!

Brent is using Swift at Omni and on personal projects

I love Swift, and — both at Omni and on my personal projects — I write new code in Swift. And sometimes port old code over to Swift.

I think, however, it’s important to think critically about our tools, and not just love shiny new things for their shininess.

Example: I’m a protocol-oriented-programming partisan since before Swift. Objective-C and Cocoa are very much about delegates, protocols, and composition-over-inheritance, and that’s where I learned it.

And yet, one of the most natural uses for protocols is to make it so you can have a collection of heterogenous items that all implement the same interface. Think of an email client. You might have an Account protocol, and different classes for IMAP, POP, Gmail, etc. that conform to that protocol.

Somewhere in your data model you’ll have a collection of these Accounts. I tend to use sets, because I want to ensure uniqueness and because any sorting should be done at or near the UI level. Can you have, in Swift, a Set, where Account is a protocol? No, because you can’t make a protocol Hashable.

There are workarounds, for sure, but that doesn’t change the fact that a thing that ought to be natural and simple isn’t.

But it’s early days. The language is still evolving. And the things that bug me – this particular problem and others — are likely to get fixed in the future.

The project we’re all on as a community now is not just about evolving the language but also experimenting and trying new things in the process of defining idiomatic Swift. This takes longer than language evolution, by necessity, but it’s absolutely critical to Swift’s future, so that collaboration (on a team, on an open source project) works well.

In other words, we’re all working on defining what is insane gibberish Swift and what’s normal. That process involves writing a lot of insane gibberish Swift! But that’s not bad, really. That’s just how the process works. And it’s fun.

Inspiration

What is the best tool you have used to help with day to day work?

My most constant tool has been BBEdit. I’ve been a BBEdit user since version 2 — which must have been about 20 years ago. Great app, great company.

A close runner-up is MarsEdit, which must be going on 12 years old now. I write my blog entirely in MarsEdit.

Another important tool is Slack. I keep it open on my Mac. I hate the app so very, very much — but it’s also great. If only it weren’t a webview wrapper. Ugh.

What else do you consider “great apps”?

Outside of mine and Omni’s apps:

Who do you look up to? Who do you consider a success, either in our industry or otherwise?

My heroes when I was getting started were Mac developers in the ’90s: Dave Winer, Peter Lewis, Mark Alldritt, Chuck Shotton, Rich Siegel, and a few others. I wanted to do what they did.

I’m not sure that there’s anyone I “look up” to these days, not exactly — but there are a ton of developers I respect. Many of them are my friends, though certainly not all. The list isn’t limited to the people I follow on Twitter, but that’s a good place to start.

I consider as a success any developer who sets out to write great software and then does. I don’t care about the business side any more than I absolutely have to — I care about the craft. If the craft is good, then I call it a success.

Where To Go From Here?

And that concludes our Top App Dev Interview with Brent Simmons. A huge thanks to Brent for sharing his journey with the iOS community :]

We hope you enjoyed this inspiring interview and that you take Brent’s advice to heart when it comes to becoming an indie app developer or working with a company like The Omni Group.

If you are an app developer with a hit app or game in the top 100 in the App store, we’d love to hear from you. Please drop us a line anytime.

If you have a request for any particular developer you’d like to hear from, please post your suggestion below!

The post Making Vesper, NetNewsWire & OmniOutliner: A Top Dev Interview With Brent Simmons appeared first on Ray Wenderlich.


Swift Algorithm Club: September Digest

$
0
0

SwiftAlgClub-Sept-Digest-feature

The Swift Algorithm Club is an open source project to implement popular algorithms and data structures in Swift.

We thought it would be useful to periodically give a status update with how things are going with the project.

First of all, we want to thank our amazing community of over 60 contributors for supporting this awesome project. Over the past months, we’ve grown from just a few dozen articles to over 70!

This is an amazing feat, so thanks again to everyone who has contributed thus far.

The Great Swift 3 Migration

This month is a special month for us at the Swift Algorithm Club, because Swift 3 is now released!

We’ve been hard at work in the past few weeks starting to port all the algorithms to Swift 3:

Screen Shot 2016-09-19 at 4.00.28 PM

So far, 26 of the 72 folders have been migrated over to Swift 3. Migration has generally been quite straightforward – it’s just the process of:

  • Making sure the playground compiles correctly
  • Making sure README.md file reflects the updated playground
  • Incorporate the new Swift API design guidelines in terms of naming of methods

Want to help out? It’s a great way to learn about algorithms and Swift 3 at the same time. If so, check out our Github issue and sign up!

Other News

In addition to the Swift 3 migration project, this month has also brought some new algorithm contributions to the project:

  • Fortune’s Algorithm
  • Karatsuba Manipulation
Note: New contributions are now accepted only if they are written in Swift 3. This allows us to keep our focus looking forward.

In addition, it’s been about a month since our last tutorial, the binary search tree. This means it’s about time for our next one to roll out!

Our next article is on Linked Lists, a core fundamental that powers many of the more complex algorithms. Look forward to it on the site soon!

Where To Go From Here?

The Swift Algorithm Club is always looking for new members. Whether you’re here to learn or here to contribute, we’re happy to have you around.

To learn more about the SAC, check out our introductory article. We hope to see you at the club! :]

The post Swift Algorithm Club: September Digest appeared first on Ray Wenderlich.

iOS Apprentice Updated for Swift 3 & iOS 10

$
0
0

iOSApprentice-featureHappy Wednesday – it’s book release day during the iOS 10 Feast!

This week’s book release is the iOS Apprentice Fifth Edition. This is our book for complete beginners to iOS 10 development, where you learn how to build four complete apps from scratch.

In this update, Matthijs Hollemans has completely updated the text and illustrations for Swift 3, iOS 10, and Xcode 8.

This is a free update for existing PDF customers, as our way of thanking you for supporting our site.

Did you know that iOS Apprentice was first written for iOS 5, and Matthijs has updated it for every version of iOS since then for free? You can’t beat that value! :]

Here’s what one of our readers has to say:

“Over the years, I have read iOS books/ebooks by Dave Mark, Big Nerd Ranch, Wei-Ming Lee, Neil Smythe, Matt Neuburg, many RW tutorials and probably several others, but Matthijs Hollemans’ tutorials absolutely tower over the rest. . . .Matthijs’s knowledge is profound and his presentations are flawless, but his detailed explanations are pure dev gold.” –chicago in a recent forum post

Where To Go From Here?

Here’s how to get your copy:

  • If you are an existing PDF customer the iOS Apprentice, download it now on your My Loot page.
  • If you don’t have the PDF version of iOS Apprentice yet, pick up a copy on the iOS Apprentice store page.

You can get 10% off on this book — or anything else in our store — with the code IOS10FEAST.

Speaking of which, be sure to check out everything we’re offering this year in the iOS 10 Feast, including $40,000 in giveaways!

To enter, simply retweet this post with the #ios10feast hashtag using the button below:


We hope you enjoy this massive free update, and stay tuned for more book releases and updates coming soon!

The post iOS Apprentice Updated for Swift 3 & iOS 10 appeared first on Ray Wenderlich.

Android Animation Tutorial

$
0
0

Android animation

It’s hard to imagine the mobile experience without animated elements. They’re fun, beautiful and hold a power of persuasion that static UI elements only wish they could have.

Building animations that make on-screen objects seem alive might seem like it could be as complex as aerospace engineering. Fear not though, Android has quite a few tools to help you create animations with relative ease. :]

You’ll learn to get comfortable with some essential animation tools in this tutorial as you work through launching Doge on a rocket into space (maybe even to the moon) and hopefully get it back safely on the ground :]

By creating these Doge animations, you’ll learn how to:

  • Create property animations — the most useful and simple Android animations
  • Move and fade Android Views
  • Combine animations in a sequence or start them simultaneously
  • Repeat and reverse animations
  • Adjust the animations’ timing
  • Become a bit of a rocket scientist. :]

Prerequisites: This Android tutorial is all about animation, so you need basic knowledge of Android programming and familiarity with Java, Android Studio and XML layouts.

If you’re completely new to Android, you might want to first check out our Android Tutorial for Beginners: Part 1.

Many animation. Such code. Fast rocket.

Getting Started

Animations are such a fun topic to explore! The best way to master building animations is by getting your hands dirty in code :]

First, download the Rocket Launch Starter. Import it into Android Studio then run it on your device. You’ll find everything you need to get going quickly.

Your device will display a list of all the animations you’ll implement.

list

Click any item on the list.

doge_rocket

You should see two static images: Doge and the rocket, and Doge is ready to take a ride. For now, all the screens are the same and none are yet animated.

How do Property Animations Work?

Before you work with the first animation, walk down theory road a bit so you’re clear on the logic behind the magic. :]

Imagine that you need to animate a rocket launch from the bottom edge to the top edge of the screen and that the rocket should make it exactly in 50 ms.

Here’s a plotted graph that shows how the rocket’s position changes over time:

linear-interpolator

The animation above appears to be smooth and continuous. However, smartphones are digital and work with discrete values. Time does not flow continuously for them; it advances by tiny steps.

Animation consists of many still images, also known as frames, that are displayed one by one over a specified time period. The concept today is the same as it was for the first cartoons, but the rendering is a little different.

Elapsed time between frames is named frame refresh delay — it’s 10 ms by default for property animations.

Here’s where animation is different than it was in the early days of film: when you know the rocket moves at a constant speed, you can calculate the position of the rocket at any given time.

You see six animation frames shown below. Notice that:

  • In the beginning of the animation, the rocket is at the bottom edge of the screen.
  • The rocket’s position moves upward by the same fraction of its path with every frame.
  • By the end of the animation, the rocket is at the top edge of the screen.

frames

TL/DR: When drawing a given frame, you calculate the rocket’s position based on the duration and frame refresh rate.

Fortunately, you don’t have to do all the calculations manually, because ValueAnimator is happy to do it for you.

To set up an animation, you just specify start and end values of the property being animated, as well as the duration. Additionally, you need to add a listener to call to set a new position for your rocket for every frame.

Time Interpolators

You probably noticed that your rocket moves with the same constant speed during the entire animation — not terribly realistic. Material design encourages you to create vivid animations that catch the user’s attention while behaving in a more natural way.

Android’s animation framework makes use of time interpolators. ValueAnimator incorporates a time interpolator – it has an object that implements TimeInterpolator interface. Time interpolators determine how the animated value changes over time.

Have a look again at the graph of position changes over time in the simplest case — a Linear Interpolator:

linear-interpolator

Here is how this LinearInterpolator responds to time change.

table_linear

Depending on the time, the rocket position changes at a constant speed or linearly.

AccelerateInterpolator

Animations can also have non-linear interpolators. One such example is the AccelerateInterpolator, which looks much more interesting.

table_acc

It squares the input value, making the rocket start slowly and accelerate quickly — just like a real rocket does!

That’s pretty much all the theory you need to know to get started, so now it’s time for…

Your First Animation

Take some time to familiarize yourself with the project before you move on. The package com.raywenderlich.rocketlaunch.animationactivities contains BaseAnimationActivity and all other activities that extend this class.

Open activity_base_animation.xml file in the res/layout folder.

In the root, you’ll find a FrameLayout that contains two instances of ImageView with images; one has rocket.png and the other has doge.png. Both have android:layout_gravity set to bottom|center_horizontal to force the images to show up at the center-bottom of the screen.

Note: You’ll do a lot of file navigation in this tutorial. Use these handy shortcuts in Android Studio to move between things easily:

  • Navigate to any file with command + O on Mac / Ctrl + N on Linux and Windows

  • Navigate to a Java class with command + shift + O on Mac / Ctrl + Shift + N on Linux and Windows

BaseAnimationActivity is a super class of all other animation activities in this app.

Open BaseAnimationActivity.java and have a look inside. At the top are View member variables that are accessible from all animation activities:

  • mRocket is the view with the image of the rocket
  • mDoge is the view that contains the Doge image
  • mFrameLayout is the FrameLayout that contains both mRocket and mDoge
  • mScreenHeight will equal the screen height for the sake of convenience

Note that mRocket and mDoge are both a type of ImageView, but you declare each as a View since property animations work with all Android Views.

Take a look at onCreate() to observe the code:

// 1
super.onCreate(savedInstanceState);
 
// 2
setContentView(R.layout.activity_base_animation);
mRocket = findViewById(R.id.rocket);
mDoge = findViewById(R.id.doge);
mFrameLayout = findViewById(R.id.container);
 
// 3
mFrameLayout.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    // 4
    onStartAnimation();
  }
});

Here’s what you’ve got going on in here:

  1. Call onCreate() on the superclass.
  2. Apply XML layout and bind FrameLayout, mRocket and mDoge to their corresponding views
  3. Set onClickListener on FrameLayout.
  4. Call onStartAnimation() whenever the user taps the screen. This is an abstract method defined by each of the activities that extend BaseAnimationActivity.

This basic code is shared by all the Activities you’ll be editing in this tutorial. Now that you’re familiar with it, it’s time to start customizing!

Launch the Rocket

Doge isn’t going anywhere unless you initiate the rocket launch, and it’s the best animation to start with because it’s pretty easy. Who’d have thought that rocket science is so simple?

Open LaunchRocketValueAnimatorAnimationActivity.java, and add the following code to the body of onStartAnimation():

//1
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, -mScreenHeight);
 
//2
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    //3
    float value = (float) animation.getAnimatedValue();
    //4
    mRocket.setTranslationY(value);
  }
});
 
//5
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setDuration(DEFAULT_ANIMATION_DURATION);
//6
valueAnimator.start();
  1. Create an instance of ValueAnimator by calling the static method ofFloat. It accepts the floating point numbers that’ll apply to the specified property of the animated object over time. In this case, the values start at 0 and end with -mScreenHeight. Android starts screen coordinates at the top-left corner, so the rocket’s Y translation changes from 0 to the negative of the screen height — it moves bottom to top.
  2. Call addUpdateListener() and pass in a listener. ValueAnimator calls this listener with every update to the animated value — remember the default delay of 10 ms.
  3. Get the current value from the animator and cast it to float; current value type is float because you created the ValueAnimator with ofFloat.
  4. Change the rocket’s position by using the setTranslationY().
  5. Set up the animator’s duration and interpolator.
  6. Start the animation.

Build and run. Select Launch a Rocket in the list. You’ll get a new screen. Tap it!

linear-launch

That was fun, right? :] Don’t worry about Doge getting left behind — he’ll catch his rocketship to the moon a bit later.

Put a Spin on It

How about giving the rocket a little spin action? Open RotateRocketAnimationActivity.java and add the following to onStartAnimation:

// 1
ValueAnimator animator = ValueAnimator.ofFloat(0, 360);
 
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    float value = (float) animation.getAnimatedValue();
    // 2
    mRocket.setRotation(value);
  }
});
 
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(DEFAULT_ANIMATION_DURATION);
animator.start();

Can you spot the difference?

  1. Change the animator values to go from 0 to 360 because you want the rocket to make a full turn. Note that you could create a U-turn effect with 0 to 180.
  2. Instead of setTranslationY, you call setRotation because that’s what needs to change.

Build, run and select Spin a rocket. Tap on the new screen:

spin-a-rocket

Accelerate the Launch

Open AccelerateRocketAnimationActivity.java and add the following code to your old friend onStartAnimation():

// 1
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, -mScreenHeight);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    float value = (float) animation.getAnimatedValue();
    mRocket.setTranslationY(value);
  }
});
 
// 2 - Here set your favorite interpolator
valueAnimator.setInterpolator(new AccelerateInterpolator(1.5f));
valueAnimator.setDuration(DEFAULT_ANIMATION_DURATION);
 
// 3
valueAnimator.start();

The above code is identical to onStartAnimation() in LaunchRocketValueAnimationActivity.java except for one line: the interpolator used for the setInterpolator().

Build, run and select Accelerate a rocket in the list. Tap on the new screen to see how your rocket behaves.

Again, we see that poor Doge doesn’t catch the rocket to the moon…poor fella. Hang in there, buddy!

accelerate

Since you used AccelerateInterpolator, you should see you rocket accelerating after liftoff. Feel free to play around with interpolators if you’d like. I’ll sit here and wait. I promise :]

Which Properties Can You Animate?

Until now, you’ve only animated position and rotation for View, but ValueAnimator doesn’t care what you do with the value that it supplies.

You can tell ValueAnimator to animate the value using any of the following types:

  • float if you create ValueAnimator instance with ofFloat
  • int if you do it with ofInt
  • ofObject is for the cases when float or int is not enough — it’s often used to animate colors

You can also animate any property of View. Some examples are:

  • setScaleX(float) and setScaleY(float) – these allow you to scale the view by x-axis or y-axis independently, or you can call both with the same value to animate the view’s size.
  • setTranslationX(float) and setTranslationY(float) – these allow you to change the view’s on-screen position.
  • setAlpha(float) – animate view’s transparency; 0 stands for completely transparent and 1 for completely opaque.
  • setRotation(float) – rotates the view on screen; the argument is in degrees, so 360 means a full clockwise turn. You may specify negative values as well, for instance, -90 means a counterclockwise quarter-turn.
  • setRotationX(float) and setRotationY(float) – the same as setRotation but along the x-axis and y-axis. These allow you to rotate in 3D.
  • setBackgroundColor(int) – lets you set a color. The integer argument must specify a color as Android constants Color.YELLOW, Color.BLUE do.

ObjectAnimator

Meet ObjectAnimator, a subclass of ValueAnimator. If you only need to animate a single property of a single object, ObjectAnimator may just be your new best friend.

Unlike ValueAnimator, where you must set a listener and do something with a value, ObjectAnimator can handle those bits for you almost automagically. :]

Go to LaunchRocketObjectAnimatorAnimationActivity.java class and enter the following code:

// 1
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mRocket, "translationY",
  0, -mScreenHeight);
// 2
objectAnimator.setDuration(DEFAULT_ANIMATION_DURATION);
objectAnimator.start();

Here’s what you’re doing:

  1. Creating an instance of ObjectAnimator (like you did with ValueAnimator) except that the former takes two more parameters:
    • mRocket is the object to animate
    • The object must have a setter method of the form set"propertyName"(), and the property you want to change is “translationY”. You’re able to do this because mRocket is an object of class View, which has an accessible setter with setTranslationY().
  2. You set the duration for the animation and start it.

Run your project. Select Launch a rocket (ObjectAnimator) in the list. Tap on the screen.

linear-launch

The rocket behaves the same as it did with ValueAnimator, but with less coding. :]

Note: There’s a limitation to ObjectAnimator — it can’t animate two objects simultaneously. To work around it, you create two instances of ObjectAnimator.

Consider your use cases and the amount of coding required when you decide to use ObjectAnimator or ValueAnimator.

Animating Color

Speaking of use cases, there’s animating colors to consider. Neither ofFloat() nor ofInt() can construct your animator and get good results with colors. You’re better off using ArgbEvaluator.

Open ColorAnimationActivity.java and put this code into onStartAnimation():

//1
ObjectAnimator objectAnimator = ObjectAnimator.ofObject(mFrameLayout, "backgroundColor",
  new ArgbEvaluator(),
  ContextCompat.getColor(this, R.color.background_from),
  ContextCompat.getColor(this, R.color.background_to));
 
// 2
objectAnimator.setRepeatCount(1);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
 
// 3
objectAnimator.setDuration(DEFAULT_ANIMATION_DURATION);
objectAnimator.start();

In the code above, you:

  1. Call ObjectAnimator.ofObjectand give it the following arguments:
    • mFrameLayout — the object with the property to be animated
    • "backgroundColor" — the property you want to animate
    • new ArgbEvaluator() — an additional argument that specifies how to interpolate between two different ARGB (alpha, red, green, blue) color values
    • Start and end color values — you make use of ComtextCompat.getColor() to get the color resource id.
  2. Set the number of times the animation will repeat with setRepeatCount(). Then you use setRepeatMode() to define what the animation does when it reaches the end. More on this soon.
  3. Set duration and start the animation.

Build and run. Pick the Background color item and tap on the screen.

bg-color

That’s amazing! Hey, you’re getting the hang of this pretty quickly. That’s a buttery-smooth background color change :]

Combining Animations

Animating a view is pretty awesome, but you’ve only changed one property and one object at a time. Animations need not be so restrictive.

It’s time to send Doge to the moon! :]

AnimatorSet allows you to play several animations together or in sequence. You pass your first animator to play(), which accepts an Animator object as an argument, and it returns a builder.

Then you can call the following methods on that builder, all which have the Animator object as an argument:

  • with() — to play the Animator passed as the argument simultaneously with the first one you specified in play()
  • before() — to play it before
  • after() — to play it after

You can create chains of calls such as these.

Open LaunchAndSpinAnimatorSetAnimatorActivity.java in your editor, and put the following code into onStartAnimation():

// 1
ValueAnimator positionAnimator = ValueAnimator.ofFloat(0, -mScreenHeight);
 
// 2
positionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    float value = (float) animation.getAnimatedValue();
    mRocket.setTranslationY(value);
  }
});
 
// 3
ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(mRocket, "rotation", 0, 180f);
// 4
AnimatorSet animatorSet = new AnimatorSet();
// 5
animatorSet.play(positionAnimator).with(rotationAnimator);
// 6
animatorSet.setDuration(DEFAULT_ANIMATION_DURATION);
animatorSet.start();

Here’s what you’re doing in this block:

  1. Create a new ValueAnimator.
  2. Attach an AnimatorUpdateListener to the ValueAnimator that updates the rocket’s position.
  3. Create an ObjectAnimator, a second animator that updates the rocket’s rotation.
  4. Create a new instance of AnimatorSet.
  5. Specify that you’d like to execute positionAnimator together with rotationAnimator.
  6. Just as with a typical animator, you set a duration and call start().

Build and run again. Select the Launch and spin (AnimatorSet). Tap the screen.

launch-n-spin

Doge defies the laws of physics with this one.

There’s a nifty tool to simplify animating several properties of the same object. The tool is called…

ViewPropertyAnimator

One of the greatest things about animation code that uses ViewPropertyAnimator is that it’s easy to write and read — you’ll see.

Open LaunchAndSpinViewPropertyAnimatorAnimationActivity.java and add the following call to onStartAnimation:

  mRocket.animate().translationY(-mScreenHeight)
    .rotationBy(360f)
    .setDuration(DEFAULT_ANIMATION_DURATION)
    .start();

In here, animate() returns an instance of ViewPropertyAnimator so you can chain the calls.

Build and run, select Launch and spin (ViewPropertyAnimator), and you’ll see the same animation as in the previous section.

Compare your code for this section to the AnimatorSet code snippet that you implemented in the previous section:

ValueAnimator positionAnimator = ValueAnimator.ofFloat(0, -mScreenHeight);
 
positionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    float value = (float) animation.getAnimatedValue();
    mRocket.setTranslationY(value);
  }
});
 
ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(mRocket, "rotation", 0, 360f);
 
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(positionAnimator).with(rotationAnimator);
animatorSet.setDuration(DEFAULT_ANIMATION_DURATION);
animatorSet.start();

not-bad

ViewPropertyAnimator may provide better performance for multiple simultaneous animations. It optimizes invalidated calls, so they only take place once for several properties — in contrast to each animated property causing its own invalidation independently.

Animating the Same Property of Two Objects

A nice feature of ValueAnimator is that you can reuse its animated value and apply it to as many objects as you like.

Test it out by opening FlyWithDogeAnimationActivity.java and putting the following code in onStartAnimation():

//1
ValueAnimator positionAnimator = ValueAnimator.ofFloat(0, -mScreenHeight);
positionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    float value = (float) animation.getAnimatedValue();
    // You can use value to set properties of many objects
    mRocket.setTranslationY(value);
    mDoge.setTranslationY(value);
  }
});
 
//2
ValueAnimator rotationAnimator = ValueAnimator.ofFloat(0, 360);
rotationAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    float value = (float) animation.getAnimatedValue();
    mDoge.setRotation(value);
  }
});
 
//3
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(positionAnimator).with(rotationAnimator);
animatorSet.setDuration(DEFAULT_ANIMATION_DURATION);
animatorSet.start();

In the above code you just created three animators:

  1. positionAnimator — for changing positions of both mRocket and mDoge
  2. rotationAnimator — for rotating Doge
  3. animatorSet — to combine the first two animators

Notice that you set translation for two objects at once in the first animator.

Run the app and select Don’t leave Doge behind (Animating two objects). You know what to do now. To the moon!

two-objects

Animation Listeners

Animation typically implies that a certain action has occurred or will take place. Typically, whatever happens usually comes at the end of your fancy animation.

You don’t get to observe it, but know that the rocket stops and stays off screen when the animation ends. If you don’t plan to land it or finish the activity, you could remove this particular view to conserve resources.

AnimatorListener — receives a notification from the animator when the following events occur:

  • onAnimationStart() — called when the animation starts
  • onAnimationEnd() — called when the animation ends
  • onAnimationRepeat() — called if the animation repeats
  • onAnimationCancel() — called if the animation is canceled

Open WithListenerAnimationActivity.java and add the following code to onStartAnimation():

//1
ValueAnimator animator = ValueAnimator.ofFloat(0, -mScreenHeight);
 
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    float value = (float) animation.getAnimatedValue();
    mRocket.setTranslationY(value);
    mDoge.setTranslationY(value);
  }
});
 
// 2
animator.addListener(new Animator.AnimatorListener() {
  @Override
  public void onAnimationStart(Animator animation) {
    // 3
    Toast.makeText(WithListenerAnimationActivity.this, "Doge took off", Toast.LENGTH_SHORT)
      .show();
  }
 
  @Override
  public void onAnimationEnd(Animator animation) {
    // 4
    Toast.makeText(WithListenerAnimationActivity.this, "Doge is on the moon", Toast.LENGTH_SHORT)
      .show();
    finish();
  }
 
  @Override
  public void onAnimationCancel(Animator animation) {
 
  }
 
  @Override
  public void onAnimationRepeat(Animator animation) {
 
  }
});
 
// 5
animator.setDuration(5000L);
animator.start();

The structure of the code above, with the exception of the listener part, should look the same as the previous section. Here’s what you’re doing in there:

  1. Create and set up an animator. You use ValueAnimator to change the position of two objects simultaneously — you can’t do the same thing with a single ObjectAnimator.
  2. Add the AnimatorListener.
  3. Show a toast message when the animation starts
  4. And another toast when it ends
  5. Start the animation as usual

Run the app. Select Animation events. Tap on the screen. Look at the messages!

events

Note: You also can add a listener to ViewPropertyAnimator by adding a setListener to a call chain before calling start():
mRocket.animate().setListener(new AnimatorListener() {
  // Your action
})

Alternatively, you can set start and end actions by calling withStartAction(Runnable) and withEndAction(Runnable). It’s the equivalent to an AnimatorListener with these actions.

Animation Options

Animations are not one-trick ponies that simply stop and go. They can loop, reverse, run for a specific duration, etc.

In Android, you can use the following methods to adjust an animation:

  • setRepeatCount — specifies the number of times the animation should repeat after the initial run.
  • setRepeatMode — defines what this animation should do when it reaches the end
  • setDuration — specifies the animation’s total duration

Open up FlyThereAndBackAnimationActivity.java, and add the following to onStartAnimation().

// 1
ValueAnimator animator = ValueAnimator.ofFloat(0, -mScreenHeight);
 
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    float value = (float) animation.getAnimatedValue();
    mRocket.setTranslationY(value);
    mDoge.setTranslationY(value);
  }
});
 
// 2
animator.setRepeatMode(ValueAnimator.REVERSE);
// 3
animator.setRepeatCount(3);
 
// 4
animator.setDuration(500L);
animator.start();

In here, you:

  1. Create an animator, as usual
  2. You can set the repeatMode to either of the following:
    • RESTART — restarts the animation from the beginning.
    • REVERSE — reverses the animation’s direction with every iteration.

    In this case, you set it to REVERSE because you want the rocket to take off and then go back to the same position where it started. Just like SpaceX! :]

  3. …Except you’ll do it twice.
  4. Set a duration and start the animation, as usual.

Note: So why does the third section specify the repeat count at three? Each up-and-down motion consumes two repetitions, so you need three to bring Doge back to earth twice: one to land the first time, and two to launch and land again. How many times would you like to see Doge bounce? Play around with it!

Run the app. Select Fly there and back (Animation options) in the list. A new screen is opened. Tap on the screen.

there-and-back

You should see your rocket jumping like a grasshopper! Take that, Elon Musk. :]

Declaring Animations in XML

You’ve made it to the best part of this tutorial. In this final section, you’ll learn how to declare once and use everywhere — yes, that’s right, you’ll be able to reuse your animations with impunity.

By defining animations in XML, you’re allowing reuse of animations throughout your code base.

Defining animations in XML bears some resemblance to composing view layouts.

If don’t have an animator folder under src/main/res, right-click on the res folder and select New then Android resource directory.

create_animator

On the next screen, select the resource type animator.
animator_dir

Go to res/animator folder and right-click it to select New, then Android resource file.

Enter the filename jump_and_blink. Keep pre-selected root element as Source set.

animator_res

In an open editor, you should see this:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
 
</set>

The following XML tags are available to you:

  • set — the same as AnimatorSet
  • animator — the same as ValueAnimator
  • objectAnimator — you guessed correctly; it stands for ObjectAnimator

When using an AnimatorSet in XML, you nest the ValueAnimator and ObjectAnimator objects inside it, similar to how you nest View objects inside ViewGroup objects (RelativeLayout, LinearLayout, etc.) in layout XML files.

Replace the contents of jump_and_blink.xml with the following code:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
  android:ordering="together">
 
  <objectAnimator
    android:propertyName="alpha"
    android:duration="1000"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:interpolator="@android:interpolator/linear"
    android:valueFrom="1.0"
    android:valueTo="0.0"
    android:valueType="floatType"/>
 
  <objectAnimator
    android:propertyName="translationY"
    android:duration="1000"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:interpolator="@android:interpolator/bounce"
    android:valueFrom="0"
    android:valueTo="-500"
    android:valueType="floatType"/>
</set>

Here you declare a root element, set tag. Its ordering attribute can be either together or sequential. It’s together by default, but you may prefer to specify it for clarity. The set tag has two child XML tags, each of which is an objectAnimator.

Take a look at the following attributes of objectAnimator:

  • android:valueFrom and android:valueTo — specify start and end values like you did when you created an instance of ObjectAnimator
  • android:valueType — value type; either floatType or intType
  • android:propertyName — the property you want to animate without the set part
  • android:duration — duration of the animation
  • android:repeatCount — the same as with setRepeatCount
  • android:repeatMode — the same as with setRepeatMode
  • android:interpolator — specify interpolator; it usually starts with @android:interpolator/. Start typing this and Android Studio will show all available interpolators under autocomplete options
  • You can’t specify your target object here, but you can do it later in Java

In the last block, you added two instances of objectAnimator to the AnimatorSet, and they will play together. Now, it’s time to use them.

Go to XmlAnimationActivity.java and add the following code to onStartAnimation():

  // 1
  AnimatorSet rocketAnimatorSet =
    (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.jump_and_blink);
  // 2
  rocketAnimatorSet.setTarget(mRocket);
 
  // 3
  AnimatorSet dogeAnimatorSet =
    (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.jump_and_blink);
  // 4
  dogeAnimatorSet.setTarget(mDoge);
 
  // 5
  AnimatorSet bothAnimatorSet = new AnimatorSet();
  bothAnimatorSet.playTogether(rocketAnimatorSet, dogeAnimatorSet);
  // 6
  bothAnimatorSet.setDuration(DEFAULT_ANIMATION_DURATION);
  bothAnimatorSet.start();

In the above code, you’re doing just a few things:

  1. First, you load AnimatorSet from R.animator.jump_and_blink file, just like you normally would to inflate a view layout
  2. Then you set mRocket as the target for just-loaded animator
  3. Load the animator from the same file once again
  4. Rinse and repeat for mDoge object
  5. Now you create a third AnimatorSet and set it up to play the first two simultaneously
  6. Set the duration for the root animator and start
  7. Whew! Rest just a little bit :]

Build and run. Select Jump and blink (Animations in XML) in the list. Tap to see your handiwork.

jump-n-blink

You should see Doge jumping, disappearing and then returning back to the ground safely :]

Where To Go From Here

You can grab the final project here.

During this tutorial you:

  • Created and used property animations with ValueAnimator and ObjectAnimator
  • Set up time interpolator of your choice for your animation
  • Animated position, rotation and color for View
  • Combined animations together
  • Used the spectacular ViewPropertyAnimator with the help of animate()
  • Repeated your animation
  • Defined the animation in XML for reuse across the project

Basically, you just gained Android animation super-powers.

If you’re hungry for more, check out the available time interpolators in Android’s documentation (see Known Indirect Subclasses). If you’re not happy with either of them, you can create your own. You can also set Keyframes for your animation to make them very sophisticated.

Android has other animations systems like View animations and Drawable Animations. You can also make use of Canvas and OpenGL ES APIs to create animations. Stay tuned :]

I hope you enjoyed the Introduction to Android Animations tutorial. Chime in with your questions, ideas and feedback in the forums below!

The post Android Animation Tutorial appeared first on Ray Wenderlich.

iOS 10 Screencast: SiriKit Resolution & Confirmation

Screencast: Beginning C# Part 12: Constants and Enumerations

Swift Algorithm Club: Swift Linked List Data Structure

$
0
0

Linked List Feature

The Swift Algorithm Club is an open source project on implementing data structures and algorithms in Swift.

Every month, Kelvin Lau and I feature a cool data structure or algorithm from the club in a tutorial on this site. If you want to learn more about algorithms and data structures, follow along with us!

In this tutorial, you’ll learn how to implement a linked list in Swift 3. The linked list implementation was first implemented by Matthijs Hollemans, the founder of the Swift Algorithm Club.

Note: New to the Swift Algorithm Club? Check out our getting started post first.

Getting Started

A linked list is a sequence of data item, where each item is referred to as a node.

There are two main types of linked lists:

Singly linked lists, are linked lists where each node only has a reference to the next node.

Singly linked list

Doubly linked lists, are linked lists where each node has a reference to the previous and next node.

Doubly linked list

You need to keep track of where the list begins and ends. That’s usually done with pointers called head and tail.

Head and tail

Linked List Implementation in Swift 3

In this section, you’ll implement a linked list in Swift 3.

Remember that a linked list is made up of nodes. So to start, let’s create a basic node class. Create a new Swift playground and add the following empty class:

public class Node {
 
}

Value

A node needs a value associated with it. Add the following between the curly braces:

var value: String
 
init(value: String) {
  self.value = value
}

You’ve declared a property named value of type String. In your own apps, this could be any datatype you want to store.

You also declare an initializer, which is required for initializing all non-optional stored properties for your class.

Next

In addition to a value, each node needs a pointer to the next node in the list.

To do this, add the following property to the class:

var next: Node?

You have declared a property named next of type Node. Note that you’ve made next an optional. This is because the last node in the linked list does not point to another node.

Previous

You are implementing a doubly-linked list so we also need a pointer to the previous node in the list.

To do this, add one last property to the class:

weak var previous: Node?

Note: To avoid ownership cycles, we declare the previous pointer to be weak. If you have a node A that is followed by node B in the list, then A points to B but also B points to A. In certain circumstances, this ownership cycle can cause nodes to be kept alive even after you deleted them. We don’t want that, so we make one of the pointers weak to break the cycle.

To learn more about ownership cycles, check out our ARC and Memory Management in Swift tutorial.

Linked List

Now that you have created the Node you also need to keep track of where the list begins and ends.

To do this, add this new LinkedList class to the bottom of the playground:

public class LinkedList {
  fileprivate var head: Node?
  private var tail: Node?
 
  public var isEmpty: Bool {
    return head == nil
  }
 
  public var first: Node? {
    return head
  }
 
  public var last: Node? {
    return tail
  }
}

This class will keep track of where the list begins and ends. It will also provide a number of other helper functions.

Append

To handle appending a new node on your list, you’ll declare a append(value:) method in your LinkedList class. Add the following new method to LinkedList:

public func append(value: String) {
  // 1
  let newNode = Node(value: value)
  // 2
  if let tailNode = tail {
    newNode.previous = tailNode
    tailNode.next = newNode
  } 
  // 3
  else {
    head = newNode
  }
  // 4
  tail = newNode
}

Let’s review this section by section:

  • Create a new Node to contain the value. Remember, the purpose of the Node class is so that each item in the linked list can point to the previous and next node.
  • If tailNode is not nil, that means there is something in the linked list already. If that’s the case, configure the new item to point to the tail of the list as it’s previous item. Similarly, configure the new last item on the list to point to the new node as it’s next item.
  • Finally, set the tail of the list to be the new item in either case.

Printing Your Linked List

Let’s try out your new linked list. Outside the implementation of LinkedList, write the following into your playground:

let dogBreeds = LinkedList()
dogBreeds.append(value: "Labrador")
dogBreeds.append(value: "Bulldog")
dogBreeds.append(value: "Beagle")
dogBreeds.append(value: "Husky")

After defining the list, we will try print the list to the console:

print(dogBreeds)

You can bring up the console by pressing the following keys in combination: Command-Shift-Y. You should see the following printed out to the console:

LinkedList

That isn’t very helpful. To display a more readable output string, you can make LinkedList adopt the CustomStringConvertable protocol. To do this, add the following just below the implementation of your LinkedList class:

// 1
extension LinkedList: CustomStringConvertible {
  // 2
  public var description: String {
    // 3
    var text = "["
    var node = head
    // 4
    while node != nil {
      text += "\(node!.value)"
      node = node!.next
      if node != nil { text += ", " }
    }
    // 5
    return text + "]"
  }
}

Here’s how the code works:

  1. You’ve declared an extension to your LinkedList class, and you’ve adopted the CustomStringConvertible protocol. This protocol expects you to implement a computed property with the name description, with the String type.
  2. You’ve declared the description property. This is a computed property, a read only property that returns a String.
  3. You’ve declared a text variable. This will hold the entire string. For now, it contains an opening brace to represent the start of the list.
  4. You then loop through the list appending the value of each item to the text string.
  5. You add a closing brace to the end of the text variable.

Now, when you call the print your LinkedList classes, you’ll get a nice representation of your list like this:

"[Labrador, Bulldog, Beagle, Husky]"

Accessing Nodes

Even though a linked list works most efficiently when you move through nodes in order via previous and next, sometimes it is handy to access an item by index.

To do this, you will declare a nodeAt(index:) method in your LinkedList class. This will return the Node at the specified index.

Update the implementation of LinkedList to include the following:

public func nodeAt(index: Int) -> Node? {
  // 1
  if index >= 0 {
    var node = head
    var i = index
    // 2
    while node != nil {
      if i == 0 { return node }
      i -= 1
      node = node!.next
    }
  }
  // 3
  return nil
}

Here’s what you’ve done:

  1. Added a check that the specified index is not negative. This prevents an infinite loop if the index is a negative value.
  2. Loop through the nodes until you reach the node at the specified index and return the node.
  3. If the index less than 0 or greater than the number of items in the list, then return nil.

Removing All Nodes

Removing all nodes is simple. We just assign nil to the head and tail:

public func removeAll() {
  head = nil
  tail = nil
}

Removing Individual Node

To remove an individual node, you will have to deal with three cases:

  1. Removing the first node. The requires the head and previous pointers to be updated:
    Remove first node
  2. Removing a node in the middle of the list. This requires the previous and next pointers to be updated:
    Remove middle node
  3. Removing the last node in the list. This requires the next and tail pointers to be updated:
    Remove last node

Update the implementation of LinkedList to include:

public func remove(node: Node) -> String {
  let prev = node.previous
  let next = node.next
 
  if let prev = prev {
    prev.next = next // 1
  } else { 
    head = next // 2
  }
  next?.previous = prev // 3
 
  if next == nil { 
    tail = prev // 4
  }
 
  // 5
  node.previous = nil 
  node.next = nil
 
  // 6
  return node.value
}

Here’s what you’ve done:

  1. Update the next pointer if you are not removing the first node in the list.
  2. Update the head pointer if you are removing the first node in the list.
  3. Update the previous pointer to the previous pointer of the deleted node.
  4. Update the tail if you are removing the last node in the list.
  5. Assign nil to the removed nodes previous and next pointers.
  6. Return the value for the removed node.

Generics

So far you’ve implemented a general-purpose linked list that stores String values. You’ve provided functionality to append, remove and access nodes in your LinkedList class. In this section we will use generics to abstract away the type requirement from our linked list.

Update the implementation of your Node class to the following:

// 1
public class Node<T> {
  // 2
  var value: T
  var next: Node<T>?
  weak var previous: Node<T>?
 
  // 3
  init(value: T) {
    self.value = value
  }
}

Here’s what you’ve done:

  1. You’ve changed the declaration of the Node class to take a generic type T.
  2. Your goal is to allow the Node class to take in values of any type, so you’ll constrain your value property to be type T rather than a String.
  3. You’ve also updated your initializer to take any type.

Generics: Challenge

Try updating the implementation of LinkedList to use generics.

The solution is provided in the spoiler section down below, but try it yourself first!

Solution Inside: Solution SelectShow>

Your code should compile now, so let’s test this out! At the bottom of your playground file, add the following code to verify that your generic linked list is working:

let dogBreeds = LinkedList<String>()
dogBreeds.append(value: "Labrador")
dogBreeds.append(value: "Bulldog")
dogBreeds.append(value: "Beagle")
dogBreeds.append(value: "Husky")
 
let numbers = LinkedList<Int>()
numbers.append(value: 5)
numbers.append(value: 10)
numbers.append(value: 15)

Where To Go From Here?

I hope you enjoyed this tutorial on making a linked list!

Here is a Swift playground with the above code. You can also find alternative implementations and further discussion in the linked list section of the Swift Algorithm Club repository.

This was just one of the many algorithm clubs focused on the Swift Algorithm Club repository. If you’re interested in more, check out the repo.

If you have any questions on linked lists in Swift, please join the forum discussion below!

Note: The Swift Algorithm Club is always looking for more contributors. If you’ve got an interesting data structure, algorithm, or even an interview question to share, don’t hesitate to contribute! To learn more about the contribution process, check out our Join the Swift Algorithm Club article.

The post Swift Algorithm Club: Swift Linked List Data Structure appeared first on Ray Wenderlich.

iOS 10 by Tutorials: 11 Chapters Now Available!

$
0
0

Good news – the third early access release of iOS 10 by Tutorials is now available!

The book is now almost done! This release has 11/14 chapters ready:

  • Chapter 2: Xcode 8 Debugging Improvements: Learn about the powerful new debugging tools in Xcode 8, including the new Thread Sanitizer and Memory Graph Debugger.

malloc-example

  • Chapter 3: Xcode 8 Source Editor Extensions (NEW!): Learn how to integrate your own text tools into the Xcode UI by creating a fun ASCII art extension.

successful-figlet-test

  • Chapter 4: Beginning Message Apps: Learn how to create your own sticker pack for Messages – with a custom user interface.

Custom Sticker Packs

  • Chapter 5: Intermediate Message Apps: Learn how to send custom, updatable messages, by creating a simple picture drawing and guessing game integrated into Messages.

Custom Message App

  • Chapter 6: SiriKit (NEW!): Learn how to integrate Siri into your app and process voice commands as you build a Uber clone for hot air balloons.

CustomUI1

  • Chapter 7, Speech Recognition (NEW!): Learn how to transcribe live or pre-recorded audio from over 50 languages and use that data in your app:

intro-teaser-image

  • Chapter 8: User Notifications: Learn how to use the new iOS 10 User Notifications framework, and create Notification Content extensions and Notification Service app extensions.

content-extension-presented

  • Chapter 9: UIView Property Animator: Learn about a new way of animating in iOS 10, which allows you to easily pause, reverse, and scrub through animations part-way through.

Animalation3

  • Chapter 10: Measurements and Units: Learn about some new Foundation classes that help you work with measurements and units in an easy and type-safe way.
let cycleRide = Measurement(value: 25, unit: UnitLength.kilometers)
let swim = Measurement(value: 3, unit: UnitLength.nauticalMiles)
let marathon = Measurement(value: 26, unit: UnitLength.miles)
    + Measurement(value: 385, unit: UnitLength.yards)
  • Chapter 11: What’s New with Core Data (NEW!): Learn how the new convenience methods, classes, code generation and other new features in Core Data will make your life easier.
Learn about the new way to handle managed object contexts in Core Data.

Learn about the new way to handle managed object contexts in Core Data.

  • Chapter 13: What’s New with Search (NEW!): Learn how to tie your app into the Core Search Spotlight API and perform deep searches using your app, and how to surface your app to respond to location-based searches as well.

location-feature-preview

The final three chapters of the book will be coming along with the final release of iOS 10 by Tutorials, which is scheduled for Wednesday, Oct. 26 as part of the iOS 10 Feast.

Where to Go From Here?

Here’s how you can get your hands on the early access release of the book:

  • If you’re a raywenderlich.com subscriber, good news — you get free access while you are subscribed! Just visit your My Loot page to download the third early access release of the book (v0.3) immediately.
  • If you haven’t subscribed to raywenderlich.com yet, you should subscribe! You will get access to the book, the screencasts, and access to our entire video tutorial library.
  • If you just want the book, you can buy the book separately. This includes permanent access to the book, including early access versions and free updates, but no screencasts or videos.

Thanks again to all raywenderlich.com subscribers — you are what makes this site possible. We hope you enjoy the iOS 10 book and screencasts, and stay tuned for more early access releases soon! :]

The post iOS 10 by Tutorials: 11 Chapters Now Available! appeared first on Ray Wenderlich.


React Native Tutorial: Integrating in an Existing App

$
0
0
react native tutorial

Adding React Native to an existing Swift app

The React Native framework lets you build native apps using React concepts. With React Native, you can build responsive native apps while enjoying the benefits of various web development paradigms, such as viewing your changes without recompiling the code.

Have you wanted to see how React Native fits into your app and development workflow, but without completely converting your existing app? Good news – you can incrementally add React Native to your app by inserting one or more React Native views into your app and letting React Native drive that experience.

This React Native tutorial will guide you through integrating React Native into an existing Swift application. You’ll build upon an existing application called Mixer that displays mixer events and gives you an opportunity to rate them.

Before you get started, be sure to check out the React Native Tutorial: Building Apps with JavaScript that walks you through the basics. You’ll also need some familiarity with CocoaPods to work through this tutorial.

Getting Started

Download the starter project for this tutorial and unzip it. Run the project found in the Mixer-Starter/ios folder to see what you have to work with.

The home screen displays a list of mixers. Tap the first one to see the mixer details, then tap Add Rating to see the view where you can add your own rating. It’s presently empty – and waiting for your upcoming implemention using React Native! :]

Starter app overview

Add a React Native View

Close the Mixer project in Xcode. Before you start coding, you’ll have to install the prerequisites for React Native.

If you don’t have CocoaPods installed, execute the following command in Terminal:

gem install cocoapods

Next, install Homebrew using the instructions on the Homebrew website.

Then, execute the following command in Terminal to install Node.js through Homebrew:

brew install node

Finally, use the following command to instruct Homebrew to install watchman, which is used by React Native to detect file changes:

brew install watchman

You now have the basic React Native scaffolding in place. Now you can install the modules your project will use.

In Terminal, navigate to the starter project’s js subdirectory and create a file named package.json. Add the following to the file:

{
  "name": "mixer",
  "version": "1.0.0",
  "private": true,
  "description": "Mixer",
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "dependencies": {
    "react": "~15.3.1",
    "react-native": "~0.34.0"
  }
}

This lists the dependencies for your app and sets up the start script. Run the following command to install the required Node.js modules:

npm install

You should see a new node_modules subdirectory that contains the React and React Native modules. The React Native module includes code needed to integrate with a native app. You’ll use CocoaPods to make Xcode aware of these libraries.

In Terminal, navigate to the starter project’s ios subdirectory and create a file named Podfile with the following content:

use_frameworks!
target 'Mixer'
pod 'React', :path => '../js/node_modules/react-native', :subspecs => [
  'Core',
  'RCTImage',
  'RCTNetwork',
  'RCTText',
  'RCTWebSocket',
]

The above configuration specifies a subset of libraries to load from the React Podspec. The specs listed here allow you to work with features such as views, text, and images.

Run the following command to install the dependencies:

pod install

Your output should look like this:

Analyzing dependencies
Fetching podspec for `React` from `../js/node_modules/react-native`
Downloading dependencies
Installing React (0.34.0)
Generating Pods project
Integrating client project
 
[!] Please close any current Xcode sessions and use `Mixer.xcworkspace` for this project from now on.
Pod installation complete! There are 5 dependencies from the Podfile and 1 total pod installed.

Open Mixer.xcworkspace, and run your app to verify you don’t have any build errors. The app should look exactly the same as it did before:

Mixer Home

Creating a Simple View

Using your favorite text editor, create a new file named index.ios.js and save it in the js directory. Add the following content to that file:

'use strict';
// 1
import React from 'react';
import ReactNative, {
  AppRegistry,
  StyleSheet,
  Text,
  View,
} from 'react-native';
 
// 2
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'green',
  },
  welcome: {
    fontSize: 20,
    color: 'white',
  },
});
 
// 3
class AddRatingApp extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>We're live from React Native!!!</Text>
      </View>
    )
  }
}
 
// 4
AppRegistry.registerComponent('AddRatingApp', () => AddRatingApp);

Here’s a step-by-step of what’s going on above:

  1. This loads the react and react-native modules, and the destructuring assignments let you drop the React/ReactNative prefixes when calling methods in those modules.
  2. Next, you define the stylesheets used in the UI.
  3. The next bit defines AddRatingApp with render() to display some welcome text.
  4. AddRatingApp serves as the application entry point’s root component.

In Xcode, open AddRatingViewController.swift and add the following import directive:

import React

Next, add the following instance variable that represents the React Native root view:

var addRatingView: RCTRootView!

Then, add the following code to the end of viewDidLoad():

addRatingView = RCTRootView(
    bundleURL: URL(string: "http://localhost:8081/index.ios.bundle?platform=ios"),
    moduleName: "AddRatingApp",
    initialProperties: nil,
    launchOptions: nil)
self.view.addSubview(addRatingView)

This initializes an instance of RCTRootView with the app bundle served up from index.ios.js. It configures AddRatingApp as module name to run initially.

Finally, set the root view’s frame in viewDidLayoutSubviews by adding the following code to the end:

addRatingView.frame = self.view.bounds

For security reasons, Apple by default blocks HTTP access to URLs. You’ll have to make an exception so you can load your bundle from the development server running on http://localhost.

In Xcode, open Info.plist and perform the following:

  1. Add the NSAppTransportSecurity key as a Dictionary type.
  2. Add the NSExceptionDomains key with Dictionary type under NSAppTransportSecurity.
  3. Add a key named localhost of type Dictionary under NSExceptionDomains.
  4. Add the NSTemporaryExceptionAllowsInsecureHTTPLoads key with the value YES under localhost.

When done, your Info.plist should look like the following:

Mixer Info.plist

In Terminal, go to your js directory and execute the following command to start the React Native development server:

npm start

After a few seconds, you should see something like the following:

Development server running

Note: If you receive an error stating that none of the files are listed in global config root_files then run the following command to initialize Git: git init.

If you still see the same error, run the following command to initialize a watchman configuration file in the js directory: echo "{}" > .watchmanconfig

Run the project in Xcode. Tap any mixer, tap Add Rating, and you should see the welcome text view:

Mixer first React Native app

The first time you run your app, it may take a little time for the packager to load the bundle.

Setting Up the View with an Existing Bridge

Your JavaScript and native code communicate back and forth in React Native via a brige created using the JavaScriptCore Framework.

mixer-theory-bridge-3

The bridge communication is batched and asynchronous so it remains performant and that message calls are serialized across the bridge. To see how this plays out, take a look at how you display the initial React Native view:

    1. Native: initializes the bridge.
    2. Sends a message through the bridge to the JavaScript code to run the application.
    1. JavaScript: runs the initial AddRatingApp component that was registered.
    2. Calls render for the component which displays a View and Text node.
    3. Batches and sends a message through the bridge to the Native code responsible for creating and displaying the views.

mixer-theory-bridge-msgs-1

The view layout is first computed using css-layout, then displayed using UIKit that translates View into UIView and Text into UILabel.

The following threads and queues manage the code execution to ensure responsiveness:

  • Main Thread: This thread handles the display of native views through UIKit.
  • Shadow Queue: This GCD queue computes the native view layout.
  • JavaScript Queue: This queue manages the execution of the JavaScript code.
  • Modules Queue: By default, each custom native module gets its own GCD queue. You’ll learn about native modules shortly.

In the previous section, you created a view by calling RCTRootView(_:moduleName:initialProperties:launchOptions). This is fine if you’re only going to have one RCTRootView in your app. But when working with multiple React Native views, it’s best to first create an RCTBridge instance that you can reuse to set up additional views.

Create an empty Swift file named MixerReactModule.swift and populate it with the following:

import Foundation
import React
 
class MixerReactModule: NSObject {  
  static let sharedInstance = MixerReactModule()
}

This uses the singleton pattern to lazily create a MixerReactModule instance when first accessed.

Add the following variable to the class:

var bridge: RCTBridge?

Then, add the required RCTBridgeDelegate delegate method sourceURL(for:) as an extension to the end of the file:

extension MixerReactModule: RCTBridgeDelegate {
  func sourceURL(for bridge: RCTBridge!) -> URL! {
    return URL(string: "http://localhost:8081/index.ios.bundle?platform=ios")
  }
}

Now, add the following methods to the class:

func createBridgeIfNeeded() -> RCTBridge {
  if bridge == nil {
    bridge = RCTBridge.init(delegate: self, launchOptions: nil)
  }
  return bridge!
}
 
func viewForModule(_ moduleName: String, initialProperties: [String : Any]?) -> RCTRootView {
  let viewBridge = createBridgeIfNeeded()
  let rootView: RCTRootView = RCTRootView(
    bridge: viewBridge,
    moduleName: moduleName,
    initialProperties: initialProperties)
  return rootView
}

viewForModule(_:initialProperties) calls createBridgeIfNeeded() to create an RCTBridge instance if one doesn’t exist. It then calls RCTRootView(_:moduleName:initialProperties) to create an RCTRootView instance with this bridge. Creating additional root views will reuse the existing bridge.

Now, open AddRatingViewController.swift and replace the addRatingView assignment in viewDidLoad() with the following:

addRatingView = MixerReactModule.sharedInstance.viewForModule(
  "AddRatingApp",
  initialProperties: nil)

Run your app; you should see no changes, but at this point you’ve set things up to let you easily add additional React Native views.

Mixer first React Native app

Adding a Navigator to your View

If you hadn’t noticed, your new view is a little green and in desperate need of some navigation.

Create a new file named AddRatingApp.js in your js directory. Add the following code to it:

'use strict';
 
import React from 'react';
import ReactNative, {
  StyleSheet,
  Text,
  View,
} from 'react-native';
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'green',
  },
  welcome: {
    fontSize: 20,
    color: 'white',
  },
});
 
class AddRatingApp extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>We're live from React Native!!!</Text>
      </View>
    )
  }
}
 
module.exports = AddRatingApp;

If you’re astute, you’ll notice that this looks suspiciously like the content in index.ios.js. Moving things around simply makes it easier to add the navigation.

Open index.ios.js and replace its content with the following:

'use strict';
 
import {AppRegistry} from 'react-native';
 
const AddRatingApp = require('./AddRatingApp');
 
AppRegistry.registerComponent('AddRatingApp', () => AddRatingApp);

The modified code now requires the AddRatingApp component to set up the initial view.

Hit Cmd+R in your simulator to reload the app. There should be no changes.

You’ll be using the Navigator component to set up your navigation bar. To set it up, you provide it with a renderScene function that renders your scene. You can set up your own navigation bar by passing in a navigationBar prop that consists
of a Navigator.NavigationBar component.

To customize Navigator.NavigationBar, you pass in a routeMapper prop that defines the left button, title, and right button.

Open AddRatingApp.js and add the following imports for Navigator and TouchableOpacity:

  ...
  Navigator,
  TouchableOpacity,
} from 'react-native';

You’ll use TouchableOpacity to implement touch handlers for the navigation item buttons.

Replace styles with the following:

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  content: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'green',
  },
  welcome: {
    fontSize: 20,
    color: 'white',
  },
  navBar: {
    backgroundColor: '#25507b',
  },
  navBarText: {
    fontSize: 16,
    marginVertical: 10,
  },
  navBarTitleText: {
    color: 'white',
    fontWeight: '500',
    marginVertical: 9,
  },
  navBarLeftButton: {
    paddingLeft: 10,
  },
  navBarRightButton: {
    paddingRight: 10,
  },
  navBarButtonText: {
    color: 'white',
  },
});

In just a moment, you’ll add private methods inside the AddRatingApp class to render the navigation items. But first, add the following method to render the navigation content:

_renderScene(route, navigator) {
  return (
    <View style={styles.content}>
      <Text style={styles.welcome}>We're live from React Native!!!</Text>
    </View>
  );
}

This displays the welcome text you know and love. Next, add the render method for the navigation bar’s title:

_renderNavTitle(route, navigator, index, navState) {
  return <Text style={styles.navBarTitleText}>{route.title}</Text>;
}

This simply returns the title property the route passed in.

Next, add the render method for the navigation bar’s left item:

_renderNavLeftItem(route, navigator, index, navState) {
  return (
    <TouchableOpacity
      onPress={() => console.log('Cancel button pressed')}
      style={styles.navBarLeftButton}>
      <Text style={[styles.navBarText, styles.navBarButtonText]}>
        Cancel
      </Text>
    </TouchableOpacity>
  );
}

This wraps a Text component inside a TouchableOpacity component so it can handle touch events. onPress of the touch handler logs the fact that the button was pressed.

Do the same for the navigation bar’s right item:

_renderNavRightItem(route, navigator, index, navState) {
  return (
    <TouchableOpacity
      onPress={() => console.log('Save button pressed')}
      style={styles.navBarRightButton}>
      <Text style={[styles.navBarText, styles.navBarButtonText]}>
        Save
      </Text>
    </TouchableOpacity>
  );
}

Finally, modify render as shown below to return a Navigator component:

render() {
  return (
    <Navigator
      debugOverlay={false}
      style={styles.container}
      initialRoute={{title: 'Add Rating'}}
      renderScene={this._renderScene.bind(this)}
      navigationBar={
        <Navigator.NavigationBar
          routeMapper={{
            LeftButton: this._renderNavLeftItem.bind(this),
            RightButton: this._renderNavRightItem.bind(this),
            Title: this._renderNavTitle.bind(this),
          }}
          style={styles.navBar}
        />
      }
    />
  );
}

The initialRoute prop sets up the properties for the initial scene – specifically, the title that’s used in _renderNavTitle.

Reload the app from the simulator. You should now see the navigator bar on top of the view:

Mixer add navigation

Now to test the navigation buttons. Tap the left one and Xcode should log something like the following:

2016-09-21 17:20:13.085 [info][tid:com.facebook.react.JavaScript] Cancel button pressed

Tapping the right one should log something like this:

2016-09-21 17:20:27.838 [info][tid:com.facebook.react.JavaScript] Save button pressed

Note: You can also view app logs by running the following command in Terminal: react-native log-ios.

Communicating With the React Native View

When you instantiate RCTRootView, you can pass data into the initialProperties parameter. This data is then passed on as initial props to the root component. You can also update the properties later on by passing new values to your RCTRootView instance’s appProperties property.

Open AddRatingViewController.swift and modify the addRatingView assignment to pass in the mixer identifier and current rating as follows:

addRatingView = MixerReactModule.sharedInstance.viewForModule(
      "AddRatingApp",
      initialProperties: ["identifier": mixer.identifier, "currentRating": currentRating])

Since you’ve made a native code change, rebuild and run the app from Xcode. Pick the first mixer and tap Add Rating.

Check the Xcode console. You should see the data you sent reflected in the logs:

2016-09-21 17:49:23.075 [info][tid:com.facebook.react.JavaScript] Running application "AddRatingApp" with appParams: {"rootTag":1,"initialProps":{"currentRating":0,"identifier":1}}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF

Communicating With the Native App

Earlier in this React Native tutorial, you saw how the bridge helped manage communications between native and JavaScript. For example, the View component made JavaScript calls through the bridge to an Objective-C class that eventually displayed a UIView. React Native provides you with hooks to build custom native modules that JavaScript can call.

You’ll have noticed that once the add rating view presents itself, you’re stuck since the Cancel and Save buttons don’t do much. You’ll fix the Cancel button action so it will dismiss the React Native view. To do this, you’ll create a custom native module that will be invoked when you press the button.

Create a new, empty Swift file named AddRatingManager.swift and save it in the Mixer directory. Replace its contents with the following:

import Foundation
import React
 
@objc(AddRatingManager)
class AddRatingManager: NSObject {
 
  var bridge: RCTBridge!
 
  @objc func dismissPresentedViewController(_ reactTag: NSNumber) {
    DispatchQueue.main.async {
      if let view = self.bridge.uiManager.view(forReactTag: reactTag) {
        let presentedViewController: UIViewController! = view.reactViewController()
        presentedViewController.dismiss(animated: true, completion: nil)
      }
    }
  }
}

This class contains dismissPresentedViewController(_:), which takes in a tag tied to the root view. The method then executes code on the main thread, looks for a view registered to that tag and finds the corresponding view controller. It then dismisses the view controller.

Now you’ll create a bridge implementation to make your module known to the bridge.

Create a new, empty Objective-C file named AddRatingManagerBridge.m and save it in the Mixer folder. When prompted, do not create an Objective-C bridging header file.

Add the following content to AddRatingManagerBridge.m:

#import "RCTBridgeModule.h"
 
@interface RCT_EXTERN_MODULE(AddRatingManager, NSObject)
 
RCT_EXTERN_METHOD(dismissPresentedViewController:(nonnull NSNumber *)reactTag)
 
@end

When the bridge initializes, it looks for custom native modules declared via RCT_EXTERN_MODULE and registers them. In the code above, AddRatingManager ends up on a NativeModules list. The bridge is aware of exported methods via the RCT_EXTERN_METHOD declaration.

Your next task is to edit AddRatingApp.js to wire up the Cancel button to calls the native code.

First, import the NativeModules library, like so:

  ...
  NativeModules,
} from 'react-native';

Then, import the AddRatingManager native module by adding the following statement just before the spot where you define the styles:

const { AddRatingManager } = NativeModules;

Modify _renderNavLeftItem to call the dismiss method defined in the native module. Replace onPress with the code below:

onPress={() => {
  AddRatingManager.dismissPresentedViewController(this.props.rootTag);
}}

Note that you’re passing in the rootTag prop that was previously passed during the root view set up. It provides a handle back to the React Native view that’s displayed.

Rebuild the app in Xcode and navigate to your React Native view. Tap Cancel and the React Native view should dismiss:

Mixer cancel action

Adding Save Logic

Create a new JavaScript file named Rating.js and save it in the js directory. Add the following content to the file:

'use strict';
 
import React from 'react';
import ReactNative, {
  StyleSheet,
  Text,
  View,
  Image,
  TouchableOpacity,
} from 'react-native';
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: 100,
    alignItems: 'center',
    backgroundColor: 'white',
  },
  instructions: {
    fontSize: 20,
    color: 'black',
    marginBottom: 20,
  },
  ratings: {
    flexDirection: 'row',
  },
  icon: {
    width: 52,
    height: 58,
    margin: 5
  },
});
 
class Rating extends React.Component {
  // 1
  _onPress(rating) {
    console.log("Rating selected: " + rating);
  }
 
  render() {
    // 2
    var ratings = [];
    for (var k = 1; k <= 5; k++) {
      var key = 'rating-'+k;
      // 3
      var ratingImage = (k <= this.props.rating) ?
        <Image style={styles.icon} source={require('./images/star_on.png')} /> :
        <Image style={styles.icon} source={require('./images/star_off.png')} />;
      // 4
      var rating =
        <TouchableOpacity key={key} onPress={this._onPress.bind(this, k)}>
          {ratingImage}
        </TouchableOpacity>;
      ratings.push(rating);
    }
    // 5
    return (
      <View style={styles.container}>
        <Text style={styles.instructions}>What did you think about this mixer?</Text>
        <View style={styles.ratings}>
          {ratings}
        </View>
      </View>
    );
  }
}
 
module.exports = Rating;

Here are the interesting parts in the above code:

  1. Create a handler that’s invoked when you tap a rating image.
  2. Loop through the maximum number of ratings.
  3. Based on the passed in rating, decide whether to show a highlighted rating image.
  4. Wrap the image in TouchableOpacity to handle touch events.
  5. Return the view with a call to action and the rating images.

Open AddRatingApp.js; you need to configure it to use the new Rating component.

First, import the component:

const Rating = require('./Rating');

Then, replace _renderScene() with the following code:

_renderScene(route, navigator) {
  return (
    <Rating
      title={route.title}
      navigator={navigator}
      rating={this.props.currentRating}
    />
  );
}

Navigate to an add rating view in your simulator. Reload the app and you should see the star ratings. Tap any of the stars:

Mixer add rating view

While you won’t see any star ratings light up, you can verify that Xcode logs the correct selection:

2016-09-21 18:16:03.391 [info][tid:com.facebook.react.JavaScript] Rating selected: 2

Once you’ve finished adding the save logic, the stars should change color to reflect your selection.

You’ll first work on the native side of the save logic by exporting a method you can call from JavaScript.

Open AddRatingManager.swift and add the following code just before the end of the class:

@objc func save(_ reactTag: NSNumber, rating: Int, forIdentifier identifier: Int) -> Void {
  // Save rating
  UserDefaults.standard.set(rating, forKey: "currentRating-\(identifier)")
  dismissPresentedViewController(reactTag)
}

This saves the passed in rating to NSUserDefaults with a key tied to the mixer’s identifier. It then dismisses the view controller by calling the previously defined dismissPresentedViewController(_:).

Next, open AddRatingManagerBridge.m and add the following before the end:

RCT_EXTERN_METHOD(save:(nonnull NSNumber *)reactTag rating:(NSInteger *)rating forIdentifier:(NSInteger *)forIdentifier)

Since you’ve made changes to the native side, rebuild and run the app in Xcode. There should be no changes in behavior at this point.

Mixer Home

Now that the native side is all set up, it’s time to turn your attention to the JavaScript side. Open AddRatingApp.js and add a constructor to store the mixer identifier and rating in the component’s state:

constructor(props) {
  super(props);
  this.state = {
    identifier: props.identifier,
    currentRating: props.currentRating,
  }
}

Next, create the following handler to store the user-selected rating in the currentRating state variable:

onRatingSelected(selectedRating) {
  this.setState({
    currentRating: selectedRating,
  });
}

You’ll call this new method shortly from the Rating component.

In _renderScene, update Rating to look like the following:

<Rating
  title={route.title}
  navigator={navigator}
  rating={this.state.currentRating}
  ratingSelectionHandler={this.onRatingSelected.bind(this)}
/>

Instead of passing in currentRating from props to the Rating component, you pass it in from state. You also pass the in a new prop for the handler that will trigger on a rating selection.

Open Rating.js and modify _onPress to call the newly passed in prop with the selected rating:

_onPress(rating) {
  if (this.props.ratingSelectionHandler) {
    this.props.ratingSelectionHandler(rating);
  }
}

The code passes back the selected rating which updates the current rating state in the parent component. This in turn re-renders the Rating component.

In the simulator, navigate to a mixer and tap Add Rating. Now when you make a selection, the corresponding number of stars are illuminated. Yay!

Mixer add rating active

Time to finish the save action. Open AddRatingApp.js and replace the code that renders the Save button with the following:

_renderNavRightItem(route, navigator, index, navState) {
  if (this.state.currentRating > 0) {
    return (
      <TouchableOpacity
        onPress={() => {
          AddRatingManager.save(
            this.props.rootTag,
            this.state.currentRating,
            this.state.identifier
          );
        }}
        style={styles.navBarRightButton}>
        <Text style={[styles.navBarText, styles.navBarButtonText]}>
          Save
        </Text>
      </TouchableOpacity>
    );
  }
  return null;
}

The Save button will now be hidden until you make a rating selection. The onPress handler now calls the AddRatingManager native module’s save() to save the selection in the native side.

Reload the app, and you’ll notice that the Save button is initially hidden:

Mixer save hidden initially

Select a rating and the Save button should appear. Tap Save, and the view should be dismissed and the rating reflected in the detail view:

Mixer save

Sending Events from the Native App

You can also communicate from native apps to JavaScript through events. The bridge has a dispatcher that can send arbitrary messages to JavaScript. Your JavaScript code can listen to events it’s interested in by subscribing to them.

You’ll send an event back to the React Native app when the save occurs. Open AddRatingManager.swift and modify its subclass:

class AddRatingManager: RCTEventEmitter {

Subclassing RCTEventEmitter allows you to customize the data you send with an event.

Delete the bridge variable declaration to use the superclass variable instead.

Next, override supportedEvents():

override func supportedEvents() -> [String]! {
  return ["AddRatingManagerEvent"]
}

This defines the events that this module will emit.

Then, add the following statement to the end of save(_:rating:forIdentifier):

self.sendEvent(
  withName: "AddRatingManagerEvent",
  body: ["name": "saveRating", "message": rating, "extra": identifier])

This calls the event emitter’s sendEvent(_:body:), passing in a name for the event and JSON representing the message to send. The message you’re sending back corresponds to information about the view and the save parameters.

Open AddRatingApp.js and add code to subscribe to the event. You’ll add the code to the root component even though any component can subscribe to events. First import NativeEventEmitter which handles event subscriptions:

  ...
  NativeEventEmitter,
} from 'react-native';

In the constructor, initialize a private _subscription variable that you’ll use to add and remove event listeners by adding the following after the super(props) statement:

this._subscription = null;

Subscribe to the event in componentDidMount() by adding the following code to the class definition:

componentDidMount() {
  const AddRatingManagerEvent = new NativeEventEmitter(AddRatingManager);
  this._subscription = AddRatingManagerEvent.addListener(
    'AddRatingManagerEvent',
    (info) => {
      console.log(JSON.stringify(info));
    }
  );
}

This code logs the event’s message.

Then, unsubscribe to the event in componentWillUnmount():

componentWillUnmount() {
  this._subscription.remove();
}

Rebuild the project in Xcode. Navigate to a mixer, rate it, and save. Check your Xcode console log, and you should see something similar to the following:

2016-09-21 23:15:45.703 [info][tid:com.facebook.react.JavaScript] {"name":"saveRating","extra":2,"message":3}

Running in Production Mode

There are a few things you need to do to get your app ready for production. So far in this React Native tutorial you’ve been serving the bundle that represents your JavaScript code from a development server. In production, you should use an offline bundle to represent your app.

In Xcode, add a Run Script to the build phase of your target. Go to Targets\Mixer\Build Phases. Click + and select New Run Script Phase. In the newly created Run Script, add the following to the shell script content:

export NODE_BINARY=node
../js/node_modules/react-native/packager/react-native-xcode.sh

Your Xcode project should look like this:

Mixer add run script

Open MixerReactModule.swift and modify sourceURLForBridge(_:) to use the offline bundle that will be generated as part of the build:

func sourceURL(for bridge: RCTBridge!) -> URL! {
  return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
}

From the Xcode menu, go to Product\Scheme\Edit Scheme… and change Build Configuration from Debug to Release.

Run your app; the build may take a little longer than usual to complete. In Xcode, tap the Report Navigator menu in the project bar and select your last build to view the build results. You should see something like the following that shows the offline bundle being written out to main.jsbundle:

Mixer building offline bundle

To test the offline script, kill the development server by hitting Ctrl-C in the Terminal window where you had previously started it. Then in the simulator, navigate to the add rating view for a mixer.

Notice that the bundle loading indicator doesn’t show up and no debug logs are shown in Xcode. You’ll also notice that the load time of the view is much faster.

Mixer running with offline bundle

Where to Go From Here?

Congratulations! as you’ve worked through this React Native tutorial, you’ve learnt how to add React Native to your existing Swift app. You can download the completed project to see the finished version. To run the project, execute the following commands in Terminal:

cd Mixer-Completed/js
npm install
npm start

In another Terminal window run the following:

cd Mixer-Completed/ios
pod install
open Mixer.xcworkspace
Run the project

Note: If you get a build error saying that Xcode can’t find the React lib, then make sure that the Framework Search Path only has $(inherited) set.

Mixer lib error fix step 1

As you get more comfortable with React Native you can look into adding more views.

Up for a challenge? Add a new React Native view to the mixer details view that displays a message when a rating is saved. A few hints to get you going:

  • Think about creating the view using the same methods you did for the add rating view.
  • Consider setting up a new React Native module for this view.
  • You can register the additional module in index.ios.js.

Check out the challenge solution and look for “Challenge” to view the changes.

For more details check out Facebook’s Integrating with Existing Apps tutorial, as well as additional information on Native Modules and Native UI Components.

There’s lots more to learn about communicating between native and React Native and Tadeu Zagallo has an in-depth article on Bridging in React Native that’s worth a read.

If you have any questions or comments, please join the discussion below!

The post React Native Tutorial: Integrating in an Existing App appeared first on Ray Wenderlich.

RWDevCon 2016 Inspiration Talk – The Weird and Wacky World of App Marketing by Jeremy Olson

$
0
0

Note from Ray: At our recent RWDevCon tutorial conference, in addition to hands-on tutorials, we also had a number of “inspiration talks” – non-technical talks with the goal of giving you a new idea, some battle-won advice, and leaving you excited and energized.

We recorded these talks so that you can enjoy them, even if you didn’t get to attend the conference. Here’s our next talk – The Weird and Wacky World of App Marketing by Jeremy Olson – I hope you enjoy!

Transcript

Note: The audio is missing for the first 20 seconds.

Let’s talk about getting featured.

A lot of people say, that getting featured is about luck; you kind of get lucky. I don’t agree with that.

I think that getting featured is something that’s repeatable, it’s something that you can do again and again and that you can learn how to do. That’s because every app that I’ve released has been featured, both by Apple and the press.

So what is the key?

Screen Shot 2016-07-31 at 9.00.04 PM

Let’s go through a few candidates:

  1. Build a great product, so that’s the easy one. The one that everyone’s going to say, you need to build a great product that has great design, that solves a real problem.

    That’s very true, if you don’t then you’re not going to get featured, so that’s true.

  2. Here’s another candidate: utilize new technologies and solve trending problems. Apple loves it when apps use their latest technologies and you’re more likely to get featured. Also trending problems, so you know there’s something going on in the industry and your app addresses that. The press loves that stuff, they eat it up. So that’s another candidate.
  3. Another candidate is make a big marketing splash. You’re building up buzz over time, this is something I talk about a lot. It’s actually in Ken’s book, he’s going next, the Marketing Crescendo. How do you build up to your launch and have a lot of buzz and come out with a big splash? When you do that, you do your own job at marketing, then the press and Apple are just going to make a bigger wave for you. That’s true too.
  4. Let’s talk about another candidate: create great looking screenshots and helpful press kit. This is a practical one, yeah you need to do this. If you don’t do this you’re probably not going to get featured.
  5. Here’s another one: localize. Something interesting, a really interesting data point, I just launched an app last Friday, which I was not planning to do right before this conference but it turned out that way. It’s been getting lots of downloads lately because of the launch, but I looked at the numbers and guess what percentage came from the US? It was just 15%.

    A lot of that was because we had localized the app, so we were getting featured in app stores across the world. You might not get a homepage feature or even the press talking about you, but you still have a chance to get featured all over the world. We’re in a global marketplace, so localize your app.

What’s Really the Key?

These are all important, but they’re not the key. The key that I want to talk about is one word.

Screen Shot 2016-07-31 at 9.00.21 PM

I’m going to give you one word right away; first let’s talk about Star Wars. This is actually really relevant – I didn’t just throw this in to mention Star Wars! (Well, I might have.)

The moral of Star Wars: what is it?

If you analyze it, I think everyone would agree that the empire is trying to re-brand themselves but they’re having setbacks blowing up planets and stuff. They have some major PR problems. Everybody thinks about this empire like this black box, they’re not relatable. The storm troopers, you can’t even see their face! That’s a problem.

The moral of Star Wars is that you need to go deeper. You need to see behind the mask, see who these people are. These are real people with feelings, with hopes, with dreams and they’re people just like us, right? What you really need to do is get to know them a little bit. That’s the moral of Star Wars.

What is the key? The key to getting featured is people.

What about TechCrunch?

Now that seems really simple, but let me explain. When you think of TechCrunch, what do you think about? What comes to your mind?

Screen Shot 2016-09-09 at 3.10.39 PM

Is it the big conferences they hold, is it the craziness of being on TechCrunch? What is it that you think of?

Well when I think of TechCrunch I think of the people I know at TechCrunch, the inside of TechCrunch. Who are the people that make TechCrunch, TechCrunch? Sarah Perez is one of them, Matthew Panzarino is the editor of TechCrunch. I’ve gotten to know them over the years.

Screen Shot 2016-07-31 at 9.01.42 PM

Those are the people that I think of when I think of TechCrunch. You’re not going to get featured unless you get featured by real people. You’re not trying to get featured by TechCrunch, what you need to start thinking about is:

  • how do I make something that will appeal to
  • how do I really get to know

these great people who are in our industry? Actually they’re really fun to know. So, that’s the key.

What about Apple?

What comes to your head when you think of Apple? Maybe it’s their cool new campus coming out, their sleek devices or mistakes. When we think of Apple, they actually haven’t helped with this image that they’re this black box, you cannot get access to, it’s so hard to get in touch with a real human being.

When I think of Apple, I think about the people of Apple.

Screen Shot 2016-07-31 at 9.02.24 PM

I made sure that I protect my contacts there. I’m kidding, these aren’t my contacts. But there’s people at Apple and that’s who you need to get the attention of. They have goals, they have careers and they want to do their job really well.

Think About the Role of People When Marketing Your App

Remember that there’s an app store team, and one of the things they do is they want to find the very best apps that are coming out. They are looking for you, but are you going to be find-able by them?

How is it that one of these people in Apple is going to see your product and like it and want to feature it?

  • Is it because maybe someone at Apple, maybe a developer that you met, maybe they heard about it and they told them?
  • Is it because they saw it on a news website, it’s been getting a lot of buzz?
  • Is it because you had been building buzz for a long time and the industry’s been talking about it?

How is it that these individual people are going to find your app? Maybe it’s because you actually have a personal connection with one of them, that’s the best.

I think a lot of people at this point would say, especially if you’re cynical, will say, “Okay well that’s kind of lame.” If this is about people then I don’t know those people, I don’t have those connections so I’m done for when it comes to marketing. I hear this a lot, “I have no chance.”

Actually my point isn’t about trying to get connected immediately, though you do want to ultimately make the connections with the people themselves directly.

What I’m talking about is more a way of thinking, the way you’re thinking about the problem of getting featured. Are you thinking about it generically getting featured by Apple? Are you thinking about the things you need to do to get in front of the people at Apple and win over their hearts?

How to Make Connections with People

Let’s talk about the key to the key.

Screen Shot 2016-07-31 at 9.02.55 PM

Let’s talk about the key to people. How do you get to know people? How do you get into their hearts? The biggest key to me is respect. How do you get respect from these great people in our industry?

You don’t build these relationships with these people by schmoozing, you don’t do it by begging, you don’t do it by stalking. You do it by building genuine respect over time. This often takes time to do.

You might think, “okay I’m not part of this club,” but you can actually do something about it. You can gain their respect over time.

There’s a lot of ways to do this:

  • You can just build truly great apps. People see that over time.
  • You can make a list of the people you want to know and you can start interacting with them on Twitter, engaging with them. Think about what’s on their minds. How can you help them? How can you make their lives better?

Really focus on the people that you’re trying to engage with.

Just Like You, I Started Out Not Knowing Anyone

There’s lots of ways to do that but I want to focus on one in particular. To do that I want to tell a story. The story of how I got started.

Screen Shot 2016-07-31 at 9.03.14 PM

I think there’s a risk that you can say, “Well this isn’t something that I could do. This isn’t for me, I’m just getting started.” I want to tell the story of how I got started.

I was going to school in Charlotte, North Carolina and I was a sophomore with no connections. I didn’t even know Objective-C, I was blank slate, nothing. I was in Charlotte, North Carolina not Silicon Valley, I didn’t know anybody.

I had this idea for Grades, my first app. It’s a way to be able to see what you need to be aiming for in your next exams and to get the grades that you want. I started working on this product. I started looking in the industry and people were calling the app store a gold rush.

I really didn’t like that because it meant if it’s a gold rush, that means I’m just trying to throw something up there and see if it sticks, and it’s just about luck. I knew there had to be something to it because I saw companies like tap tap tap who were big back then, or tapbots consistently release successful apps. I thought, “There’s got to be something to this.”

I started looking at what were they doing. I quickly found out design is was big part of it, something that you consistently see in successful apps – really good design. Marketing too; these companies really marketed their products.

Then a really pivotal moment happened when I read a book by Seth Godin which I highly recommend called Tribes. This is where the talk gets to the core. In this book there is a quote, “If you think leadership is only for other people, you’re wrong. We need you to lead us.”

Screen Shot 2016-07-31 at 9.03.36 PM

I figured, okay this isn’t for me, I’m just getting started. I kind of brushed it aside and that made it worse because of this thing called imposter syndrome. A lot of us, especially the introverts among us, we have this, where you’re about to release something, like you said it’s scary to speak. We have this thing where we think, we have something but is it really good enough or is it just really obvious to people, do I really need to share it?

This imposter syndrome it plagues us, it plagues us into inactivity, it plagues us into not sharing. That’s a problem.

Overcoming Imposter Syndrome

I was finally able to overcome this. Seth Godin’s words started to get to me, I figured maybe at least I’ll try. I started a website, Tapity.com. This was the original, you see the old WordPress template here:

Screen Shot 2016-07-31 at 9.04.16 PM

I just started talking about the things I was learning from other people. I synthesized them into principles and wrote about how am I applying them with my own app.

Again, I hadn’t actually released an app yet. One of the most audacious things I did though is I called this blog, “Tapity, How to Build Successful iPhone Apps.”

I’d never released an app before, but I was determined to figure it out and to help others do it. I wanted to lead, I wanted to at least try and see if this was something that I could do.

The posts went on. I didn’t get a lot of traffic in the first few months, mainly a few Twitter followers here and there, and some comments. A little while later, it got noticed by some of the people that I had been talking about like tap tap tap and started to build more of an audience. They wrote a post just about “You need to check out Jeremy’s blog.”

Screen Shot 2016-07-31 at 9.05.10 PM

So I finally released Grades based on the stuff I’d been learning, and actually it did pretty well. It got featured in some press and there were a lot of Tweets about it. Ultimately Apple featured it which was amazing. I actually found out later that some people at Apple were reading my blog. Grades was featured in a lot of other places and ultimately won an Apple design award, so that was really cool.

Screen Shot 2016-07-31 at 9.06.05 PM

The point is, all of this would not be possible if I did not market by leading. I want this idea to reach you because this is for you. This isn’t for somebody else, this is for you.

We desperately need this as an industry right now. There’s all this talk and negativity about the app store, how it’s impossible to build sustainable app businesses. Really, it’s just gotten harder.

It really has gotten harder to build sustainable app businesses. It’s not as easy, you cannot just put a 99 cent app on the app store anymore and make a million dollars, it doesn’t work that way anymore. There’s a lot more we need to figure out.

It’s not going to happen unless we have leaders. Leaders who experiment, who innovate, who don’t just complain but who actually do something about it and figure it out. Who pave a way to let others do the same.

That’s how we’re going to go on as an industry. I want to encourage you – I believe in this community. We’re going to do great things and we have a bright future, but we need YOU to lead us. Thank you.

About the Speaker: Jeremy Olson is the founder and lead designer at Tapity. His first app, Grades, won an Apple Design Award. His second app, Languages, won an App Store Best of 2012 award. His latest app, Hours, ranked #1 in Business, and was recently acquired by Five Pack Creative.

A few notes from Ray:

Would you like Jeremy to help you market your app? Tapity just launched a new service called Rocket to do just that!

If you enjoyed this talk, you should join us at RWDevCon 2017! We’ve sold out for the past two years, so don’t miss your chance.

The post RWDevCon 2016 Inspiration Talk – The Weird and Wacky World of App Marketing by Jeremy Olson appeared first on Ray Wenderlich.

Unity Games by Tutorials – 11 Chapters Now Available!

$
0
0

We’re excited to tell you that the second early access release of Unity Games by Tutorials is out with a full 11 chapters and a brand new game: Robot Rampage!

Check out the trailer for the first-person-shooter you’ll learn how to build in this release:

This is a free update for those who have pre-ordered Unity Games by Tutorials.

If you haven’t pre-ordered Unity Games by Tutorials yet, read on to see how to get your copy!

What’s New in this Release?

We’ve added three new chapters that teach you how to build a great FPS game:

  • Chapter 9, Making a First Person Shooter: Learn how to set up your FPS game, create the map, add the player and build some kick-ass weaponry for mowing down enemies.
  • We have weapons, we have a map — all we need are some robots to mow down!

    Learn how to create the map, add the player to the game, and add some kick-ass guns — lots of guns!

  • Chapter 10, Adding Enemies: Learn how to create powerups, add enemies to your game, and create some damage mechanics for you and your robot nemeses.
  • Now we've got some baddies to take care of!

    Learn how to add baddies, powerups, and missiles to your game!

  • Chapter 11, Unity UI: Learn how to add in-game UI, craft waves of robot enemies, add enhanced sound and music, and give your player the right weapon for the right job.
  • Learn how to add a full UI to your game and give it some final polish!

    Learn how to add a full UI to your game and give it some final polish!

What is Unity?

Unity is a a professional game engine used to create games like City Skylines, Hearthstone, the Long Dark, and more.

Unity’s aim is to “democratize” game development, by providing a AAA-level engine to independent game developers in a way that is both affordable and accessible.

Here are our top 5 reasons why Unity is great:

  1. It’s free to use. If you’re an indie game developer, you can download and start using Unity for free, which is great when you’re just learning.
  2. It’s cross-platform. With Unity, you make your game once and you can build it for a variety of platforms, including Windows, macOS, Linux, iOS, and more.
  3. It has a visual editor. Unlike other game platforms where you have to type tons of code before you see anything on the screen, with Unity you can simply import an asset and drag and drop. This visual style of development is great for beginners and professionals alike, and makes game development fast and fun.
  4. Live debugging. With Unity you can click a button to preview your game instantly in the editor, and you can even modify game objects on the fly. For example, you can drag new enemies onto the level as you play it, tweak gameplay values and more, allowing for an iterative game design process.
  5. Unity is fun! You can think of Unity like a box of legos: the only limits are those of your own imagination.
Unity vs. Sprite Kit and Scene Kit: You might wonder which you should use: Unity, or one of the Apple game frameworks like Sprite Kit or Scene Kit.

Here’s our recommendation:

  • If you are an experienced iOS developer making a simple game and want to target iOS devices only, you may want to consider using one of Apple’s game frameworks. They are very easy to learn and leverage much of your existing iOS development experience.
  • If you want to target non-iOS devices, or if you want to make games at a professional level, you may want to consider using Unity. Unity is much more powerful than the Apple game frameworks, and does not lock you into the iOS ecosystem, and that’s well worth the increased learning curve.

What Is Unity Games by Tutorials?

Unity Games by Tutorials is for complete beginners to Unity, or for those who’d like to bring their Unity skills to a professional level. Its goal is to teach you everything you need to know to make your own Unity games – via hands-on experience.

In Unity Games by Tutorials, you’ll learn how to build four games:

  1. A twin-stick shooter
  2. A first-person shooter
  3. A tower defense game (with VR support!)
  4. A 2D platfomer

Here’s a sneak peek of what you can look forward to in the full release:

Section I: Hello, Unity!

This section covers everything you need to know to get started with Unity. You’ll learn your way around the UI, how to work with game assets and physics, and create a 3D twin-stick combat game: Bobblehead Wars.

Bobblehead Wars

Here’s what you’ll cover while saving the world from creepy-crawly aliens:

  • Chapter 1, Hello Unity: Learn how the Unity interface works and how to import assets into your project.
  • Chapter 2, GameObjects: Learn about GameObjects and Prefabs by adding and laying out the initial objects for Bobblehead Wars.
  • Chapter 3, Components: Learn how to use components to give your hero the ability to walk and blast away at the oncoming horde.
  • Chapter 4, Physics: Learn the basics of game physics by adding collision detection and giving the hero the ability to turn on a dime.
  • Chapter 5, GameManager and Pathfinding: Learn how to create the tools that spawn the aliens, and then make them chase after the hero.
  • Chapter 6, Animations: Learn how to add animations to the marine and the aliens. It’s time for some shooting and chomping!
  • Chapter 7, Sounds: Learn how to bring your game to life by adding background music and a variety of sound effects.
  • Chapter 8, Finishing Touches: Learn how to add a winning and losing condition, and wrap up the game with some classy touches.

Section II: First-Person Games

Now that you’re up to speed with Unity game development, you can move on to more complex game development topics, such as adding in-game UI elements, advanced camera techniques and using multiple weapons.

In this section, you’ll build a fast-paced first-person shooter: Robot Rampage.

Robot Rampage

Here’s a brief outline of what this section has in store:

  • Chapter 9, Making a First Person Shooter: Learn how to set up your FPS game, create the map, add the player and build some kick-ass weaponry for mowing down enemies.
  • Chapter 10, Adding Enemies: Learn how to create powerups, add enemies to your game, and create some damage mechanics for you and your robot nemeses.
  • Chapter 11, Unity UI: Learn how to add in-game UI, craft waves of robot enemies, add enhanced sound and music, and give your player the right weapon for the right job.

Section III: Unity 2D Games

3D games are undeniably awesome, but you just can’t beat a classic 2D platformer game.

In this section, you’ll build a game to test your reflexes as you help your hero battle his way to a well-deserved lunch in Super Soy Boy:

Super Soy Boy

Here’s the chapters and topics you’ll cover while helping Super Soy Boy avoid those terrifying buzzsaws:

  • Chapter 12, Beginning Unity 2D: Learn the 2D workflow in Unity and begin creating the building blocks for your 2D platformer game.
  • Chapter 13, More Unity 2D: Learn how to work with 2D physics, how to build levels with sprites and colliders, and how to work with raycasting, animation controllers, and more.
  • Chapter 14, Saving Data: Learn some great ways to store and retrieve player data for your games.

Section IV: Tower Defense

Combining strategy and action results in compelling games that are easy to pick up — and hard to put down.

In this section, you’ll create a 3D tower defense game — Runestrife — with such beautifully-rendered enemies it almost seems a shame to shoot them:

Runestrife

When you’re not busy defending your towers from advancing waves of enemies, here’s what you’ll learn:

  • Chapter 15, Making a Tower Defense Game: Learn how to build a title screen, create a map using 3D tiles, make enemies move around on a path, and make your life easier with utility scripts.
  • Chapter 16, Waves of Enemies: Learn how to create multiple waves of advancing enemies, make your towers shoot projectiles, add winning and losing game states, and craft a UI to bind everything together.
  • Chapter 17, Virtual Reality: Learn how to bring your game into the 21st century and make your game VR-compatible with the virtual reality hardware now available.
  • Chapter 18, Publishing your Game: Learn how to prepare your game for publication and how to make it available on the various online game portals.

The book also will also include a few bonus chapters beyond this – but our lips are sealed! :]

Where To Go From Here?

Here’s how to get your hands on this new release:

If you pre-order Unity Games by Tutorials now, you’ll get receive an automatic $10 early access discount.

To take advantage of the special discount and get early access to the book, order your copy now.

The Unity team and I hope you enjoy the book, and we can’t wait to see your future games!

The post Unity Games by Tutorials – 11 Chapters Now Available! appeared first on Ray Wenderlich.

iOS 10 Screencast: Collection View Data Prefetching

Extend the Unity3d Editor

$
0
0

CustomizedEditorPreview2

The Unity Editor is (undeniably) an awesome engine for building games, but often gets this close to actually fitting your needs. At times, you probably find yourself wishing for a “do what I mean” button.

Although Unity isn’t quite capable of interpreting brainwaves through some mystical button, there ways to extend this tool to make it more useful and user-friendly.

In this Unity tutorial, you’ll learn how to extend and customize the engine. Aside from the accomplishment of being master of your domain, your changes should have the domino effect of speeding up game development by making the process a little (or a lot) leaner. :]

In particular, you’ll focus on how to use the following:

  • Attributes for basic customization
  • ScriptableObject to create custom assets and reduce memory usage
  • Customize the Inspector using PropertyDrawer and Editor
  • Create a new editor window to provide your own editing tools
  • Draw in a scene with gizmos

Are you doubtful that customizing Unity can be helpful? On Gamasutra, you can find a cool article about Unity and editor tools that show a number of examples of how a custom editor can benefit your game design and creation efforts.

Prerequisites

You need Unity version 5.3 or newer to load the starter project successfully. If you don’t have it, then download Unity from Unity3D.com.

This tutorial is intended for advanced Unity developers, meaning you should know basic scripting and feel comfortable working in the Unity editor. If you’re not quite there, try honing your knowledge with our other Unity tutorials.

Getting Started

Download the starter project, unpack the zip file and open the resulting TowerDefenseEditor folder with Unity. As the folder name suggests, the project is a tower defense game.

Note: If you’d like to see where it comes from and how to build it, here’s the Unity tutorial: How to Create a Tower Defense Game in Unity, Part 1.

Although the game runs smoothly, it’s difficult to edit. For example, the process for adding new open spots for the purpose of placing new “towers” is a bit annoying. Also, you can’t see the path the enemies follow.

In this tutorial, you’ll extend the Unity Editor to add those and a few more features so that it looks a lot like this:

The Editor After Customization

The Editor After Customization

This tutorial goes from smaller to bigger changes. Each section is self-contained, so you may skip around and work with the Editor extensions that pique your interest.

Basic Customizations with Attributes

These give you the power to drastically reorganize the Unity3D editor, but all you need are some minor adjustments, e.g., hiding a field, showing a tooltip or adding a headline. Hiding unwanted fields and protecting others are two strong use cases for attributes.

Hide Public Fields – [HideInInspector]

As per the default, all public fields of a MonoBehavior are exposed in Inspector, but sometimes you don’t want all that. Sometimes, you just want to:

  • Remove clutter from Inspector
  • Avoid accidental changes to a variable that could mess up your game

broken

Time to get your hands dirty!

Go to the Assets/Prefabs folder and select Bullet1, and then look at Inspector. You should see the following:

Before: Many fields you shouldn't touch

Before: Lots of fields you should not touch

You can see the fields Target, Start Position and Target Position. Those variables are public because they need to be assigned by the ShootEnemies script.

However, they don’t need to be editable in Inspector because bullets always fly from the monster to its target; you don’t need these fields exposed.

Open the script BulletBehavior.cs in the Assets/Scripts folder. Import using System; by inserting it into line 2.

Before:

using UnityEngine;
using System.Collections;

After:

using UnityEngine;
using System;
using System.Collections;

Add the [HideInInspector] and [NonSerialized] attributes before the lines that contain the definitions of the fields target, startPosition and targetPosition. [NonSerialized] is part of the System NameSpace, hence why you inserted using System; above.

Before:

public GameObject target;
public Vector3 startPosition;
public Vector3 targetPosition;

After:

[HideInInspector]
[NonSerialized]
public GameObject target;
[HideInInspector]
[NonSerialized]
public Vector3 startPosition;
[HideInInspector]
[NonSerialized]
public Vector3 targetPosition;

Save your changes.

HideInInspector removes the field from Inspector and successfully hides the field. However, if you edit the value of the variable in Inspector before you hide things, Unity will remember the value you set. This can be confusing.

To avoid this, use the attribute [NonSerialized] to stop Unity from remembering the value of the variable.

After: Fields are hidden

After: Fields are hidden

Note: Using attributes is simple. You just put them above the field, method or class they should influence, and you also have the option to combine several attributes.

[RequireComponent]: Avoid Accidentally Removing a Component

Try this on the OpenSpot prefab to make the placement always work as expected.

Open the file PlaceMonster.cs from the folder Assets/Scripts. Edit the code as follows:

Before:

using System.Collections;
 
public class PlaceMonster : MonoBehaviour {

After:

using System.Collections;
 
[RequireComponent (typeof (AudioSource))]
[RequireComponent (typeof(Collider2D))]
 
public class PlaceMonster : MonoBehaviour {

Save your changes.

In Inspector, try to remove either the Collider2D or the AudioSource component from the OpenSpot prefab found in the Assets/Prefabs folder. A dialog appears when you try to delete it because of RequireComponent, it says you can’t remove it and also tells you which script requires it. Neat!

RequireComponentWarning

Other Interesting Tags

In addition to the ones you worked with above, there are plenty more cool attributes. Here’s a short overview:

Usage Attribute
Add items to Unity’s menus MenuItem, AddComponentMenu, ContextMenu, ContextMenuItem, CreateAssetMenu
Show or hide information HeaderHelpURL, Tooltip, HideInInspector
Layout display in Inspector Multiline, Range, Space, TextArea
Define constraints for component usage RequireComponent, DisallowMultipleComponent
Specify whether to remember the value of a field or not NonSerialized, Serializable
Execute callback functions even when Editor isn’t in playmode ExecuteInEditMode
Configure the usage of the ColorPicker ColorUsage

ScriptableObject: Create Custom Assets and Reduce Memory Usage

In this section, you’ll store a bullet’s configuration in a ScriptableObject instead of inside the GameObject.

You’re probably thinking: But it worked perfectly fine with GameObject! Why should I change it? To understand why you would want to do this, simply look into the basics of scriptable objects.

Scriptable Objects: The Basics

The intended use case for ScriptableObject is for those times you need to reduce memory usage by avoiding copies of values.

How does it do that? When you create a GameObject, Unity stores all primitive types connected with it by value.

Your bullet has two integer fields: speed and damage. One integer requires 2 bytes, so you need 4 bytes total. Now imagine you have 100 bullets — you would already need at least 400 bytes. This is with no change to speed or damage. It’s a great way to quickly consume resources.


Clean up that waste

Clean up that waste

ScriptableObject stores these values by reference, so you could use it when:

  • You store a lot of data for a particular type of object.
  • Values are shared between many GameObjects.

While the major value of ScriptableObject is that it reduces memory requirements, you can also use it to define custom assets for dialogs, level data, tile sets and more. This allows you to decouple data and GameObjects.

Implement a ScriptableObject

In this subsection, you’ll create your first scriptable object.

Inside of Unity and in the Project Browser, open the Assets/Scripts folder, right-click it and select Create > C# Script. Name the script BulletConfig, open it and change its contents as follows:

using UnityEngine;
 
[CreateAssetMenu(menuName = "Bullet", fileName = "Bullet.asset")]
public class BulletConfig : ScriptableObject {
  public float speed;
  public int damage;
}

And save.

CreateAssetMenu makes the creation of a ScriptableObject available under the Assets main menu option.

Switch back to Unity, go to the Project Browser and navigate to the Assets/Bullets folder. Click on Assets/Create/Bullet from the main menu. Name the created asset Bullet1 — now you have a nice ScriptableObject.

create scriptable object

bullet asset

Create two more bullets named Bullet2 and Bullet3.

Now click on Bullet1 and go into Inspector. Set speed to 10 and damage to 10.

Set bullet properties

Repeat for Bullet2 and Bullet3 with the following values:

  • Bullet2: speed = 10, damage = 15
  • Bullet3: speed = 10, damage = 20

Now you’ll replace the speed and damage fields in BulletBehavior with BulletConfig. Open BulletBehavior. Remove the fields for speed and damage and add this field:

Before:

public float speed = 10;
public int damage;

After:

public BulletConfig bulletConfig;

Since all your other scripts still need read access to speed and damage, you need to create a getter for both variables.

Before the closing brace of the class, add the following:

private int damage {
  get {
    return bulletConfig.damage;
  }
}
 
private float speed {
  get {
    return bulletConfig.speed;
  }
}

Save those changes.

Without touching any other script, you improved memory usage by replacing the variables inside the bullets with a scriptable object.

Lastly, you need to assign your scriptable object to your bullet prefabs.

In the Project Browser, select Prefabs / Bullet1. In Inspector, set Bullet Config to the Bullet1.asset. Repeat this for Bullet2 and Bullet3 accordingly. Done!

Adding the scriptable objects to the bullet prefabs.

Adding the scriptable objects to the bullet prefabs.

Run the game to make sure everything still works.

Customize Inspector With PropertyDrawer and Editor

So far, you’ve only changed minor things, but could redraw almost everything in Inspector. This section is all about flexing your redrawing muscles.

Editor

With Editor, you can replace Inspector GUI for a MonoBehavior. The implementation is to display the health of each enemy with a slider, where the slider’s length depends on the maximum health of the enemy, making it impossible to enter invalid values.

Inspector for MoveEnemy before and after applying a custom editor

Inspector for MoveEnemy before and after applying a custom editor

Inside of the Assets / Editor folder, create a new C# script named EnemyDataEditor. Open the script in MonoDevelop. Replace the contents with the following:

using UnityEditor;
 
// 1
[CustomEditor(typeof(MoveEnemy))]
// 2
public class EnemyDataEditor : Editor {
  // 3
  public override void OnInspectorGUI() {
  // 4
    MoveEnemy moveEnemy = (MoveEnemy)target;
    HealthBar healthBar = moveEnemy.gameObject.GetComponentInChildren<HealthBar> ();
  // 5
    healthBar.maxHealth = EditorGUILayout.FloatField ("Max Health", healthBar.maxHealth);
  // 6
    healthBar.currentHealth = EditorGUILayout.Slider("Current Health",   healthBar.currentHealth, 0, healthBar.maxHealth);
  // 7
    DrawDefaultInspector ();
  }
}

Save your changes.

This is how each step works:

  1. Specify that Unity should create a CustomEditor for MoveEnemy in this attribute.
  2. Make EnemyDataEditor a subclass of Editor so it can access methods such as OnInspectorGUI.
  3. Add OnInSpectorGUI, a method that’s called when drawing the editor.
  4. In Editor, you can access the instance that’s currently selected, either in the Project Browser or Hierarchy, via target. Then you cast it to MoveEnemy. Finally, you get its only child — the HealthBar.
  5. Use EditorGUILayout.FloatField to add a field for maxHealth that only accepts float values.
  6. Create a slider for the actual current health with EditorGUILayout.Slider. This differs from simply using the attribute [Range] because it allows adjustments to the slider’s range depending on a variable. In this case, it’s healthBar.maxHealth.
  7. A custom editor replaces everything that would usually appear in the Inspector. In cases where you only want to extend the existing view, you call DrawDefaultInspector. It draws any remaining default content in the location where you’ve added the call.

Switch back to Unity and select Prefabs / Enemy in the Project Brower and enjoy your result.

MoveEnemy

Run the game to make sure everything still works. You should now be able to select an Enemy(Clone) in the Hierarchy and change its health on the slider in the Inspector while the game is in progress. Cheating was never so much fun. :]

Slide or slider? - I know what I will choose :]

Slide or slider? – I know what I will choose :] (Published under CC0 on pixabay)

Property Drawers

With property drawers, you can:

  • Customize the GUI for every instance of a Serializable class, such as Wave.
  • Customize the GUI for fields with custom PropertyAttributes, such as Range.

In Hierarchy, select Road. Check out all the different waves in the Inspector.

Pretend that in addition to the Enemy Prefab, you also want to show a preview image of that prefab. You can do this with a PropertyDrawer for Wave.

Customized Inspector with Property Drawer – before and after.

Customized Inspector with property drawer – before and after.

Start by creating a new C# Script named WavePropertyDrawer inside the Assets/Editor folder in the Project Browser. Open it in MonoDevelop and replace the entire contents as follows:

//1
using UnityEditor;
using UnityEngine;
 
//2
[CustomPropertyDrawer(typeof(Wave))]
//3
public class WavePropertyDrawer : PropertyDrawer {
  //4
  private const int spriteHeight = 50;
}

Save your changes.

In this code block, you:

  1. Import UnityEditor to enable access to the Editor’s libraries.
  2. Use an Attribute to tell your class it’s a CustomPropertyDrawer for Wave.
  3. Make it a subclass of PropertyDrawer.
  4. Create a constant to store the height at which the enemy sprite should be drawn.

Go back in Unity and select Road in the Hierarchy. You should see the message No GUI implemented in Inspector.

noguiimplementednew

Change this and add OnGUI to WavePropertyDrawer:

public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
  EditorGUI.PropertyField(position, property, label, true);
}

Overriding OnGUI lets you implement your own GUI. The method takes three arguments:

  • position: A rectangle to draw the GUI into
  • property: The property you want to draw
  • label: A text that labels the property

You draw the default GUI for the property with the method EditorGUI.PropertyField; it takes all the variables you get from OnGUI.

The last parameter is a bool variable that tells Unity to draw the property itself as well as its children.

In addition to OnGUI, you need to implement GetPropertyHeight():

public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
  return EditorGUI.GetPropertyHeight (property);
}

This method returns the height for the property, and is especially important for properties that can change height depending on whether the property is expanded or not.

If you don’t implement it then Unity assumes the default height. In cases where your property is higher, it will draw on top of other fields in Inspector. This is usually not what you want. :]

Keep calm and implement GetPropertyHeight

Now that you have the basics in place, you’re free to customize the GUI. Add the following code to OnGUI():

// 1
if (property.isExpanded) {
  // 2
  SerializedProperty enemyPrefabProperty = property.FindPropertyRelative ("enemyPrefab");
  GameObject enemyPrefab = (GameObject) enemyPrefabProperty.objectReferenceValue;
  // 3
  if (enemyPrefab != null) {
    SpriteRenderer enemySprite = enemyPrefab.GetComponentInChildren<SpriteRenderer> (); 
    // 4
    int previousIndentLevel = EditorGUI.indentLevel;
    EditorGUI.indentLevel = 2;
    // 5
    Rect indentedRect = EditorGUI.IndentedRect (position);
    float fieldHeight = base.GetPropertyHeight (property, label) + 2;
    Vector3 enemySize = enemySprite.bounds.size;
    Rect texturePosition = new Rect (indentedRect.x, indentedRect.y + fieldHeight * 4, enemySize.x / enemySize.y * spriteHeight, spriteHeight);
    // 6
    EditorGUI.DropShadowLabel(texturePosition, new GUIContent(enemySprite.sprite.texture));
    // 7
    EditorGUI.indentLevel = previousIndentLevel;
  }
}

Save your changes.

Step-by-step breakdown:

  1. You only want to draw the enemy sprite when the details of the property should be shown, for instance, when the user unfolds it in Inspector. In this case, property.isExpanded is true.
  2. Get the enemyPrefab. It calls FindPropertyRelative first to retrieve the property linked to another property by name. Then it gets the objectReference that stores the actual GameObject that contains the enemyPrefab.
  3. If the enemyPrefab is set, then you get its sprite.
  4. Set the indentLevel properly. This level specifies how far to the right Unity should start drawing. Since you’ll need to restore its original value after you’re done drawing, you store it in a variable first, and then you set indentLevel to 2.
  5. Get the position of the indentedRect so you can calculate the coordinates required to draw inside a property drawer. Then you get the height of one normal field with GetPropertyHeight. You need the size of the enemy sprite too. With these three values, you can calculate the rect for drawing the texture. The origin is that of the indentedRect. However, you add four times the fieldHeight to reserve space for the fields drawn. Because the height should be the same, no matter the actual size of the enemy, you scale the width accordingly.
  6. Call DropShadowLabel to draw a texture within the specified position.
  7. In the end, you reset the indent level.

Go back to Unity to see the enemy sprite in the proper position. It’s there, but it overlaps with the next property. Why?

ohohoverlap

Solution Inside SelectShow>

Go back to Unity and select Road in the Hierarchy. In Inspector, you can now see the sprite of the enemy properly.

Aside from adding one or more sprites, you can change Inspector more significantly. One example is directly from the PropertyDrawer documentation.

CustomPropertyDrawer_Class

Property drawers allow you to customize how Inspector is drawn for any property of a Serializable class. They allow you to dictate how one specific property of your MonoBehavior is drawn without a change to the rest of Inspector.

Property drawers are especially helpful when you want to redraw items in an array.

However, they come with some serious drawbacks. Namely, you don’t have access to EditorGUILayout(), leaving you to calculate each field’s position by yourself. Additionally, you can only access info stored in the property or its children.

Create a New Editor window to provide Your Own Editing Tools

In Unity, you have several cool Editor window, such as Animator, Inspector and Hierarchy. And of course, you can even make your own.

In this section, you’ll implement an Editor view that allows you to place and delete OpenSpots with one click.

LevelEditorView

In the Project Browser, navigate to Assets/Editor and create a new C# Script named LevelEditorWindow. Open it in MonoDevelop. Then replace the entire contents with this:

using UnityEngine;
//1
using UnityEditor;
 
//2
public class LevelEditorWindow : EditorWindow {
  //3
  int selectedAction = 0;
}
  1. Import UnityEditor
  2. Make LevelEditorWindow a subclass of EditorWindow to allow access to methods and fields available to EditorWindow.
  3. Keep track of which action the user currently has selected with the variable selectedAction.

Right now, you couldn’t open this level editor inside of Unity even if your life depended on it, so add the following method to change that:

[MenuItem ("Window/Level Editor")]
static void Init () {
  LevelEditorWindow window = (LevelEditorWindow)EditorWindow.GetWindow (typeof (LevelEditorWindow));
  window.Show();
}

In the first line, you tell Unity to create a MenuItem named Level Editor below the Window menu, and you just click it to run Init.

With GetWindow inside, you get an existing open window or create a new one if none exists. Then you display it with Show.

Save your changes and go back to Unity. You’ll see a warning. That’s alright — you’ll fix it in a minute. Just go and click on the main menu Window/Level Editor to bring up an empty editor view.

empty level editor window copy

Now to fill that empty view with content. Switch back to MonoDevelop and add the following code to LevelEditorWindow:

void OnGUI() {
  float width = position.width - 5;
  float height = 30;
  string[] actionLabels = new string[] {"X", "Create Spot", "Delete Spot"};
  selectedAction = GUILayout.SelectionGrid (selectedAction, actionLabels, 3, GUILayout.Width (width), GUILayout.Height (height));
}

First you get the width and height. For the width, you deduct 5 to leave a little bit space. Next, you create a string array with the label for each action — “X” stands for no action. This lets you select GameObjects instead of creating or deleting open spots.

From this array, you create a SelectionGrid, which creates a grid of buttons where only one can be selected at once. The return value of SelectionGrid is the index of the currently selected button, so you store it in selectedAction.

Save your changes and have a look at your creation in Unity.

LevelEditorWindow

You want to have a different reaction to clicks that’s dependent on selected button in the scene, so add the following method:

void OnScene(SceneView sceneview) {
  Event e = Event.current;
  if (e.type == EventType.MouseUp) {
    Ray r = Camera.current.ScreenPointToRay (new Vector3 (e.mousePosition.x, Camera.current.pixelHeight -e.mousePosition.y));
    if (selectedAction == 1) {
      // TODO: Create OpenSpot
      Debug.Log("Create OpenSpot");
    } else if (selectedAction == 2) {
      // TODO: Delete OpenSpot
	Debug.Log("Delete OpenSpot");
    }
  }
}

First you store the current event in a variable. If its type is equal to MouseUp, you react to the mouse click.

For this, you need to figure out which GameObject was selected, and you’re using Raycasting to do it. But first, you need to create a ray that points towards the scene.

To make a ray, you use the helper method ScreenPointToRay(), which you call with the mouse position stored in the event. As the y-coordinate is reversed between camera and event coordinate system, you transform it by deducting the original y-coordinate from the height of the current view.

Lastly, you split by selectedAction and log a short message.

Make sure the method is actually called with:

void OnEnable() {
  SceneView.onSceneGUIDelegate -= OnScene;
  SceneView.onSceneGUIDelegate += OnScene;
}

This adds OnScene as delegate.

Save your changes and switch back to Unity. Select the Create Spot or Delete Spot button inside of the LevelEditorWindow, and then click inside of the Scene. The log message will appear in the Console.

Log on click

In the Hierarchy, select Background. In the Inspector, set its Layer to Background. Then add a component Physics 2D / Box Collider 2D.
background
Set the Layer of Prefabs/Openspot to Spots. Now you can determine where a ray collided with the background or an open spot.

openspot

Go back to MonoDevelop and replace TODO: Create OpenSpot in LevelEditorWindow with this code:

RaycastHit2D hitInfo2D = Physics2D.GetRayIntersection (r, Mathf.Infinity, 1 << LayerMask.NameToLayer ("Background"));
if (hitInfo2D) {
  GameObject spotPrefab = AssetDatabase.LoadAssetAtPath<GameObject> ("Assets/Prefabs/Openspot.prefab");
  GameObject spot = (GameObject)PrefabUtility.InstantiatePrefab (spotPrefab);
  spot.transform.position = hitInfo2D.point;
  Undo.RegisterCreatedObjectUndo (spot, "Create spot");
}

Here’s what you’re doing in there:

It starts by checking whether the ray intersects with the background image. If yes, then hitInfo2D isn’t null and you create a new OpenSpot at the position.

You get the prefab with LoadAssetPath. From this instance, you create a new instance of this prefab with PrefabUltility.InstantiatePrefab.

InstantiatePrefab() creates a prefab connection, which makes it more suitable in this case then Instantiate. Then you set the position of the spot to the point of the collision.

Unity always lets you undo actions, so remember that when you create custom editor scripts. To allow undo with a custom action, simply register the action used to create a GameObject with Undo.RegisterCreatedObjectUndo.

Now to handle deleting spots. Replace // TODO: Delete OpenSpot with this:

RaycastHit2D hitInfo2D = Physics2D.GetRayIntersection (r, Mathf.Infinity, 1 << LayerMask.NameToLayer ("Spots"));
if (hitInfo2D) {
  GameObject selectedOpenSpot = hitInfo2D.collider.gameObject;
  Undo.DestroyObjectImmediate (selectedOpenSpot);
}

This detects collisions between your ray and any object inside the Spots layer. If yes, you get the GameObject that was hit. Then you delete it with Undo.DestroyObjectImmediate, which removes the spot and registers the action with the undo manager.

Go back to Unity, and check out how quickly you can create and delete spots. Best of all, undo and redo work as expected.

UsingTheEditor2

Draw Into the Scene with Gizmos

Gizmos let you to draw into the Scene view, allowing you to provide information to use as a visual debugging or editing aid.

For example, the game contains a set of waypoints for the enemies to follow, but they are invisible during the game and while editing. You simply can’t tell which path they’ll follow without doing some serious investigative work. If you wanted to create another level, you still wouldn’t be able to see the path.

Before: Without - After: A line between the spots shows how enemies will move

Before: Without – After: A line between the spots shows how enemies will move

Fortunately, Gizmos allow you to see this invisible path.

Open Assets/Scripts/SpawnEnemy, which stores all waypoints, and add the following method:

// 1
void OnDrawGizmos() {
  // 2
  Gizmos.color = Color.green;
  // 3
  for (int i = 0; i < waypoints.Length - 1; i++) {
    // 4
    Vector3 beginPosition = waypoints[i].transform.position;
    Vector3 endPosition = waypoints[i+1].transform.position;
    // 5
    Gizmos.DrawLine(beginPosition, endPosition);
    // 6
    UnityEditor.Handles.Label (beginPosition, i + "");
  }
  // 7
  Vector3 lastWaypointPosition = waypoints[waypoints.Length - 1].transform.position;
  UnityEditor.Handles.Label (lastWaypointPosition, (waypoints.Length - 1) + "");
}

Here are explanations for each step:

  1. You can only use methods for gizmo drawing in OnDrawGizmos() or in OnDrawGizmosSelected(). You always want the path to draw, so you implement OnDrawGizmos().
  2. You set the color for the gizmo’s drawing. Every draw call will use this color until you change it.
  3. Iterate over all waypoints, except the last one that doesn’t have successor to draw a line to, so you stop at waypoints.Length - 1.
  4. You store the position of waypoint i, and its successor i+1. into a variable with every iteration.
  5. With the static method Gizmos.Drawline(Vector3, Vector3), you draw a line from the current waypoint to the next waypoint.
  6. With UnityEditor.Handles.Label(string), you write the index of the waypoint adjacent to it to display the path and visualize the direction that enemies move.
  7. You get the position of the last waypoint and display its index next to it.

Switch back to Unity to see the path. Play with it a bit by moving waypoints around and you’ll see the path follows along.

Aside from DrawLine(), Gizmos provide several drawing functions. You can find the full list in the documentation. There is even a DrawRay:

Hm, I do not think that this is what it does...

Hmmm, that’s definitely a Ray, but something seems off…

Where To Go From Here?

In this tutorial, you learned how to make parts of the Unity Editor your own. To see the final result, download the finished project here.

As you probably guessed, there are more ways to extend Unity. Here are some additional directions you could go:

Now that you’ve made it to the bottom, you’ve got a good bit of starting knowledge about how to to customize Unity for your own purposes and project.

Join us in the forums to toss around ideas and examples, as well as ask questions about customizing Unity. I’m looking forward to seeing what you come up with!

The post Extend the Unity3d Editor appeared first on Ray Wenderlich.

Viewing all 4373 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>