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

Building iOS Apps with Xamarin and Visual Studio

$
0
0

Xamarin and Visual Studio

When creating iOS apps, developers typically turn to the languages and IDE provided by Apple: Objective-C / Swift and Xcode. However, this isn’t the only option—you can create iOS apps using a variety of languages and frameworks.

One of the most popular options is Xamarin, a cross-platform framework that allows you to develop iOS, Android, OS X and Windows apps using C# and Visual Studio. The major benefit here is Xamarin can allow you to share code between your iOS and Android app.

Xamarin has a big advantage over other cross-platform frameworks: with Xamarin, your project compiles to native code, and can use native APIs under the hood. This means a well written Xamarin app should be indistinguishable from an app made with Xcode. For more details, check out this great Xamarin vs. Native App Development article.

Xamarin had a big disadvantage too in the past too: its price. Because of the steep licensing cost of $1,000 per platform per year, you’d have to give up your daily latte or frappuccino to even think about affording it … and programming without coffee can get dangerous. Because of this steep price, until recently Xamarin appealed mostly to enterprise projects with big budgets.

However, this recently changed when Microsoft purchased Xamarin and announced that it would be included in all new versions of Visual Studio, including the free Community Edition that’s available to individual developers and small organizations.

Free? Now that’s a price to celebrate!

More money for coffee!

More money for coffee!

Besides cost (or lack thereof), Xamarin’s other virtues include allowing programmers to:

  • Leverage existing C# libraries and tools to create mobile apps.
  • Reuse code between apps on different platforms.
  • Share code between ASP.Net backends and customer-facing apps.

Xamarin also offers a choice of tools, depending on your needs. To maximize cross-platform code reuse, use Xamarin Forms. This works especially well for apps that don’t need platform-specific functionality or a particularly custom interface.

If your app does require platform-specific features or designs, use Xamarin.iOS, Xamarin.Android and other platform-specific modules to get direct interaction with native APIs and frameworks. These modules provide the flexibility to create very custom user interfaces, yet still allow sharing of common code across platforms.

In this tutorial, you’ll use Xamarin.iOS to create an iPhone app that displays a user’s photo library.

This tutorial doesn’t require any prior iOS or Xamarin development experience, but to get the most from it you’ll need a basic understanding of C#.

Getting Started

To develop an iOS app with Xamarin and Visual Studio, you’ll ideally need two machines:

  1. A Windows machine to run Visual Studio and write your project’s code.
  2. A Mac machine with Xcode installed to act as a build host. This doesn’t have to be a dedicated computer for building, but it must be network accessible during development and testing from your Windows computer.

It greatly helps if your machines are physically near each other, since when you build and run on Windows, the iOS Simulator will load on your Mac.

I can hear some of you saying, “What if I don’t have both machines?!”

  • For Mac-only users, Xamarin does provide an IDE for OS X, but in this tutorial we will be focusing on the shiny new Visual Studio support. So if you’d like to follow along, you can run Windows as a virtual machine on your Mac. Tools such as VMWare Fusion or the free, open-source VirtualBox make this an effective way to use a single computer.

    If using Windows as a virtual machine, you’ll need to ensure that Windows has network access to your Mac. In general, if you can ping your Mac’s IP address from inside Windows, you should be good to go.

  • For Windows-only users, go buy a Mac right now. I’ll wait! :] If that’s not an option, hosted services such as MacinCloud or Macminicolo provide remote Mac access for building.

This tutorial assumes you’re using separate Mac and Windows computers, but don’t worry—the instructions are basically the same if you’re using Windows inside a virtual machine on your Mac.

Installing Xcode and Xamarin

If you don’t have it already, download and install Xcode on your Mac. This is just like installing any other app from the App Store, but since it’s several gigabytes of data, it may take a while.

Installing Xcode? Perfect time for a cookie break!

Perfect time for a cookie break!

After Xcode is installed, download Xamarin Studio onto your Mac. You’ll need to provide your email, but the download is otherwise free. Optional: do a happy dance for all the coffees you can still afford.

Once the download is complete, open the installer package and double click Install Xamarin.app. Accept the terms and conditions and continue.

The installer will search for already-installed tools and check for current platform versions. It will then show you a list of development environments. Make sure Xamarin.iOS is checked, then click Continue.

Xamarin Installer

Next you’ll see a confirmation list summarizing the items to be installed. Click Continue to proceed. You will be given a summary and an option to launch Xamarin Studio. Instead, click Quit to complete the installation.

Installing Visual Studio and Xamarin

For this tutorial you can use any version of Visual Studio, including the free Community Edition. Some features are absent in the Community Edition, but nothing that will prevent you from developing complex apps.

Your Windows computer should meet the Visual Studio minimum system requirements. For a smooth development experience, you’ll want at least 3 GB of RAM.

If you don’t already have Visual Studio installed, download the Community Edition installer by clicking the green Download Community 2015 button on the Community Edition web site.

Run the installer to begin the installation process, and choose the Custom installation option. In the features list, expand Cross Platform Mobile Development, and select C#/.NET (Xamarin v4.0.3) (where v4.0.3 is the current version when this tutorial was written, but will likely be different in the future).

vs-installer

Click Next and wait for the installation to complete. This will likely take a while; go take a walk to burn off all the cookies you ate while installing Xcode. :]

If you already have Visual Studio installed but don’t have the Xamarin tools, go to Programs and Features on your Windows computer and find Visual Studio 2015. Select it, click Change to access its setup, then select Modify.

You’ll find Xamarin under Cross Platform Mobile Development as C#/.NET (Xamarin v4.0.3). Select it and click Update to install.

Whew—that’s a lot of installations, but now you’ve got everything you need!

Install_Powers

Creating the App

Open Visual Studio and select File\New\Project. Under Visual C# expand iOS, select iPhone and pick the Single View App template. This template creates an app with a single view controller, which is simply a class that manages a view in an iOS app.

NewProject

For both the Name and the Solution Name, enter ImageLocation. Choose a location on your computer for your app and click OK to create the project.

Visual Studio will prompt you to prepare your Mac to be the Xamarin build host:

  1. On the Mac, open System Preferences and select Sharing.
  2. Turn on Remote Login.
  3. Change Allow access to Only these users and add a user that has access to Xamarin and Xcode on the Mac.
    Setup Mac as Build Host
  4. Dismiss the instructions and return to your Windows computer.

Back in Visual Studio, you will be asked to select the Mac as the build host. Select your Mac and click Connect. Enter the username and password, then click Login.

You can verify you’re connected by checking the toolbar.

Connected_Indicator

Select iPhone Simulator from the Solution Platform dropdown—this will automatically pick a simulator from the build host. You can also change the device simulator by clicking the small arrow next to the current simulator device.

Change_Simulator

Build and run by pressing the green Debug arrow or the shortcut key F5.

Build_and_Run

Your app will compile and execute, but you won’t see it running on Windows. Instead, you’ll see it on your Mac build host. This is why it helps to have your two machines nearby :]

At the recent Evolve conference, Xamarin announced iOS Simulator Remoting that will soon allow you to interact with apps running in Apple’s iOS Simulator as though the simulator were running on your Windows PC. For now, however, you’ll need to interact with the simulator on your Mac.

You should see a splash screen appear on the simulator and then an empty view. Congratulations! Your Xamarin setup is working.

Template App

Stop the app by pressing the red stop button (shortcut Shift + F5).

Creating the Collection View

The app will display thumbnails of the user’s photos in a Collection View, which is an iOS control for displaying several items in a grid.

To edit the app’s storyboard, which contains the “scenes” for the app, open Main.storyboard from the Solution Explorer.

Main Storyboard

Open the Toolbox and type collection into the text box to filter the list of items. Under the Data Views section, drag the Collection View object from the toolbox into the middle of the empty view.

Add Collection View

Select the collection view; you should see hollow circles on each side of the view. If instead you see T shapes on each side, click it again to switch to the circles.

Resizing the Collection View

Click and drag each circle to the edge of the view until blue lines appear. The edge should snap to this location when you release the mouse button.

Now you’ll set up Auto Layout Constraints for the collection view; these tell the app how the view should be resized when the device rotates. In the toolbar at the top of the storyboard, click on the green plus sign next to the CONSTRAINTS label. This will automatically add constraints for the collection view.

Add_Constraints

The generated constraints are almost correct, but you’ll need to modify some of them. On the Properties window, switch to the Layout tab and scroll down to the Constraints section.

The two constraints defined from the edges are correct, but the height and width constraints are not. Delete the Width and Height constraints by clicking the X next to each.

Delete Constraints

Notice how the collection view changes to an orange tint. This is an indicator that the constraints need to be fixed.

Click on the collection view to select it. If you see the circles as before, click again to change the icons to green T shapes. Click and drag the T on the top edge of the collection view up to the green rectangle named Top Layout Guide. Release to create a constraint relative to the top of the view.

Lastly, click and drag the T on the left side of the collection view to the left until you see a blue dotted line. Release to create a constraint relative to the left edge of the view.

At this point, your constraints should look like this:

Constraints

Configuring the Collection View Cell

You may have noticed the outlined square inside the collection view, inside of which is a red circle containing an exclamation point. This is a collection view cell, which represents a single item in the collection view.

To configure this cell’s size, which is done on the collection view, select the collection view and scroll to the top of the Layout tab. Under Cell Size, set the Width and Height to 100.

cell-size

Next, click the red circle on the collection view cell. A pop-up will inform you that you haven’t set a reuse identifier for the cell, so select the cell and go to the Widget tab. Scroll down to the Collection Reusable View section and enter ImageCellIdentifier for the Identifier. The error indicator should vanish.

Set_Reuse_Identifier

Continue scrolling down to the Interaction Section. Set the Background Color by selecting Predefined and blue.

Set Cell Background Color

The scene should look like the following:

Collection Cell with Color

Scroll to the top of the Widget section and set the Class as PhotoCollectionImageCell.

Set Cell Class

Visual Studio will automatically create a class with this name, inheriting from UICollectionViewCell, and create PhotoCollectionImageCell.cs for you. Sweet, I wish Xcode did that! :]

Creating the Collection View Data Source

You’ll need to manually create a class to act as the UICollectionViewDataSource, which will provide data for the collection view.

Right-click on ImageLocation in the Solution Explorer. Select Add \ Class, name the class PhotoCollectionDataSource.cs and click Add.

Open the newly added PhotoCollectionDataSource.cs and add the following at the top of the file:

using UIKit;

This gives you access to the iOS UIKit framework.

Change the definition of the class to the following:

public class PhotoCollectionDataSource : UICollectionViewDataSource
{
}

Remember the reuse identifier you defined on the collection view cell earlier? You’ll use that in this class. Add the following right inside the class definition:

private static readonly string photoCellIdentifier = "ImageCellIdentifier";

The UICollectionViewDataSource class contains two abstract members you must implement. Add the following right inside the class:

public override UICollectionViewCell GetCell(UICollectionView collectionView,
    NSIndexPath indexPath)
{
    var imageCell = collectionView.DequeueReusableCell(photoCellIdentifier, indexPath)
       as PhotoCollectionImageCell;
 
    return imageCell;
}
 
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
    return 7;
}

GetCell() is responsible for providing a cell to be displayed within the collection view.

DequeueReusableCell reuses any cells that are no longer needed, for example if they’re offscreen, which you then simply return. If no reusable cell is available, a new one is created automatically.

GetItemsCount tells the collection view to display seven items.

Next you’ll add a reference to the collection view to the ViewController class, which is the view controller that manages the scene containing the collection view. Switch back to Main.storyboard, select the collection view, then select the Widget tab. Enter collectionView for the Name.

Set Collection View Name

Visual Studio will automatically create an instance variable with this name on the ViewController class.

Note: You won’t see this instance variable within ViewController.cs itself. To see the the instance variable, click the disclosure indicator to the left of ViewController.cs to reveal ViewController.designer.cs inside. This contains the collectionView instance variable automatically generated by Visual Studio.

Open ViewController.cs from the Solution Explorer and add the following field right inside the class:

private PhotoCollectionDataSource photoDataSource;

At the end of ViewDidLoad(), add these lines to instantiate the data source and connect it to the collection view.

photoDataSource = new PhotoCollectionDataSource();
collectionView.DataSource = photoDataSource;

This way the photoDataSource will provide the data for the collection view.

Build and run. You should see the collection view with seven blue squares.

App Running with collection view

Nice – the app is really coming along!

Blue Squares!

Showing Photos

While blue squares are cool, you’ll next update the data source to actually retrieve photos from the device and display them on the collection view. You’ll use the Photos framework to access photo and video assets managed by the Photos app.

To start, you’ll add a view to display an image on the collection view cell. Open Main.storyboard again and select the collection view cell. On the Widget tab, scroll down and change the Background color back to the default.

Set_Default_Cell_Background_Color

Open the Toolbox, search for Image View, then drag an Image View onto the collection view Cell.

Drag Image View

The image view will initially be much larger than the cell; to resize it, select the image view and go to the Properties \ Layout tab. Under the View section, set both the X and Y values to 0 and the Width and Height values to 100.

Set Image View Size

Switch to the Widget tab for the image view and set the Name as cellImageView. Visual Studio will automatically create a field named cellImageView for you.

Set Image View Name

Scroll to the View section and change the Mode to Aspect Fill. This keeps the images from becoming stretched.

Set Image View Mode

Note: Again, if you open PhotoCollectionImageCell.cs, you won’t see the new field. Instead the class is declared as partial, which indicates that the field is in another file.

In the Solution Explorer, select the arrow to the left of PhotoCollectionImageCell.cs to expand the files. Open PhotoCollectionImageCell.designer.cs to see cellImageView declared there.

Expand PhotoCollectionImageCell

This file is automatically generated; do not not make any changes to it. If you do, they may be overwritten without warning or break links between the class and storyboard, resulting in runtime errors.

Since this field isn’t public, other classes cannot access it. Instead, you’ll need to provide a method to be able to set the image.

Open PhotoCollectionImageCell.cs and add the following method to the class:

public void SetImage(UIImage image)
{
    cellImageView.Image = image;
}

Now you’ll update PhotoCollectionDataSource to actually retrieve photos.

Add the following at the top of PhotoCollectionDataSource.cs:

using Photos;

Add the following fields to the PhotoCollectionDataSource:

private PHFetchResult imageFetchResult;
private PHImageManager imageManager;

The imageFetchResult field will hold an ordered list of photo entity objects, and you’ll get this photos list from the imageManager.

Right above GetCell(), add the following constructor:

public PhotoCollectionDataSource()
{
    imageFetchResult = PHAsset.FetchAssets(PHAssetMediaType.Image, null);
    imageManager = new PHImageManager();
}

This constructor gets a list of all image assets in the Photos app and stores the result in the imageFetchResult field. It then sets the imageManager, which the app will query for more information about each image.

Dispose of the imageManager object when the class finishes by adding this destructor below the constructor.

~PhotoCollectionDataSource()
{
    imageManager.Dispose();
}

To make the GetItemsCount and GetCell methods use these resources and return images instead of empty cells, change GetItemsCount() to the following:

public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
    return imageFetchResult.Count;
}

Then replace GetCell with the following:

public override UICollectionViewCell GetCell(UICollectionView collectionView,
    NSIndexPath indexPath)
{
    var imageCell = collectionView.DequeueReusableCell(photoCellIdentifier, indexPath)
        as PhotoCollectionImageCell;
 
    // 1
    var imageAsset = imageFetchResult[indexPath.Item] as PHAsset;
 
    // 2
    imageManager.RequestImageForAsset(imageAsset,
        new CoreGraphics.CGSize(100.0, 100.0), PHImageContentMode.AspectFill,
        new PHImageRequestOptions(),
         // 3
         (UIImage image, NSDictionary info) =>
        {
           // 4
           imageCell.SetImage(image);
        });
 
    return imageCell;
}

Here’s a breakdown of the changes above:

  1. The indexPath contains a reference to which item in the collection view to return. The Item property is a simple index. Here you get the asset at this index and cast it to a PHAsset.
  2. You use imageManager to request the image for the asset with a desired size and content mode.
  3. Many iOS framework methods use deferred execution for requests that can take time to complete, such as RequestImageForAsset, and take a delegate to be called upon completion. When the request completes, the delegate will be called with the image and information about it.
  4. Lastly, the image is set on the cell.

Build and run. You’ll see a prompt requesting permission access.

Permission_Prompt

If you select OK, however, the app … doesn’t do anything. So disappointing!

Why_no_work

iOS considers access to users’ photos to be sensitive information, and prompts the user for permission. However, the app must also register to be notified when the user has granted this permission, so it can reload its views. You’ll do this next.

Registering for Photo Permission Changes

First, you’ll add a method to the PhotoCollectionDataSource class to inform it to re-query for photo changes. Add the following to the end of the class:

public void ReloadPhotos()
{
    imageFetchResult = PHAsset.FetchAssets(PHAssetMediaType.Image, null);
}

Next, open ViewController.cs and add the following framework to the top of the file:

using Photos;

Then add this code to the end of ViewDidLoad():

// 1
PHPhotoLibrary.SharedPhotoLibrary.RegisterChangeObserver((changeObserver) =>
{
    //2
    InvokeOnMainThread(() =>
    {
        // 3
        photoDataSource.ReloadPhotos();
        collectionView.ReloadData();
    });
});

Here’s what this does:

  1. The app registers a delegate on the shared photo library to be called whenever the photo library changes.
  2. InvokeOnMainThread() ensures that UI changes are always processed on the main thread; otherwise a crash may result.
  3. You call photoDataSource.ReloadPhotos() to reload the photos and collectionView.ReloadData() to tell the collection view to redraw.

Finally, you’ll handle the initial case, in which the app has not yet been given access to photos, and request permission.

In ViewDidLoad(), add the following code right before setting photoDataSource:

if (PHPhotoLibrary.AuthorizationStatus == PHAuthorizationStatus.NotDetermined)
{
    PHPhotoLibrary.RequestAuthorization((PHAuthorizationStatus newStatus) =>
    { });
}

This checks the current authorization status, and if it’s NotDetermined, explicitly requests permission to access photos.

In order to trigger the photos permission prompt again, reset the iPhone simulator by going to Simulator \ Reset Content and Settings.

Build and run the app. You’ll be prompted for photo permission, and after you press Ok the app will show the collection view with thumbnails for all the device’s photos!

Final Project Running

Where to Go From Here?

You can download the completed Visual Studio project from here.

In this tutorial, you learned a bit about how Xamarin works and how to use it to create iOS apps.

The Xamarin Guides Site provides several good resources to learn more about the Xamarin platform. To better understand building cross-platforms apps, view the Xamarin tutorials on building the same app for iOS and Android.

Microsoft’s purchase of Xamarin introduced many exciting changes. The announcements at Microsoft’s Build conference and Xamarin Evolve can give you guidance on Xamarin’s new direction. Xamarin also released videos of the sessions from the recent Evolve Conference that provide more information on working with Xamarin and the future direction of the product.

Do you think you’ll try Xamarin when building apps? If you have any questions or comments about this tutorial, please feel free to post in the comments section below.

The post Building iOS Apps with Xamarin and Visual Studio appeared first on Ray Wenderlich.


Screencast: Beginning C# Part 4: Operators

$
0
0

Links

MSDN Operator Precendence <- Great resource for overall precedence as well as a nice overview of available operators. (hint, there's a lot of them! :]) If you've never used Unity or if you are still really green, read this article: Introduction to Unity: Getting Started

Previous Video: Types

Challenge

Create a tip calculator. Do this by creating a new script, calling it TipCalculator. It should have two public fields: balance, tipAmount. Make these floats.

The formula for finding the tip should be: balance * tipAmount

Have OnDisable() print out the result.

Challenge Solution

Resources

Download Start Challenge
Download Finished Challenge
Download Starter Demo
Download Finished Demo

The post Screencast: Beginning C# Part 4: Operators appeared first on Ray Wenderlich.

iOS 10 Screencast: Thread Sanitizer

Video Tutorial: Intermediate Realm on iOS: Introduction

Video Tutorial: Intermediate Realm on iOS Part 1: Bundled Realm Files

NSScanner Tutorial for OS X

$
0
0

NSScannerFeatureImage

Update note: This tutorial has been updated to Swift by Hai Nguyen. The original tutorial was written by Vincent Ngo.

In these days of big data, data is stored in a multitude of formats, which poses a challenge to anyone trying to consolidate and make sense of it. If you’re lucky, the data will be in an organized, hierarchical format such as JSON, XML, or CSV. Otherwise, you might have to struggle with endless if/else cases. Either way, manually extracting data is no fun.

Thankfully, Apple provides a set of tools that you can use to analyze string data in any form, from natural to computer languages, such as NSRegularExpression, NSDataDetector, NSScanner…Each of them has its own advantages, but NSScanner is by far the easiest to use yet powerful and flexible. In this tutorial, you’ll learn how to extract information from email messages with its methods, in order to build an OS X application that works like Apple Mail’s interface as shown.

Completed-Final-Screen

Although you’ll be building an app for Mac, NSScanner is also available on iOS. By the end of this tutorial, you will be ready to parse text on either platform.

Before getting things started, let’s first see what NSScanner is capable of!

NSScanner Overview

NSScanner‘s main functionality is to retrieve and interpret substring and numeric values.

For example, NSScanner can analyze a phone number and break it down into components like this:

// 1.
let hyphen = NSCharacterSet(charactersInString: "-")
 
// 2.
let scanner = NSScanner(string: "123-456-7890")
scanner.charactersToBeSkipped = hyphen
 
// 3.
var areaCode, firstThreeDigits, lastFourDigits: NSString?
 
scanner.scanUpToCharactersFromSet(hyphen, intoString: &areaCode)          // A
scanner.scanUpToCharactersFromSet(hyphen, intoString: &firstThreeDigits)  // B
scanner.scanUpToCharactersFromSet(hyphen, intoString: &lastFourDigits)    // C
 
println(areaCode, firstThreeDigits, lastFourDigits)
// 123 - area code
// 456 - first three digits
// 7890 - last four digits

Here’s what this code does:

  1. Creates an instance of NSCharacterSet named hyphen. This will be used as the separator between string components.
  2. Initializes a NSScanner object and changes its charactersToBeSkipped default value (whitespace and linefeed) to hyphen, so the returning strings will NOT include any hyphens.
  3. areaCode, firstThreeDigits and lastFourDigits will store parsed values that you get back from the scanner. Since you cannot port Swift native String directly to AutoreleasingUnsafeMutablePointer;, you have to declare these variables as optional NSString objects in order to pass them into the scanner’s method.
    1. Scans up to the first character and assigns the values in front of the hyphen character into areaCode.
    2. Continues scanning to the second and grabs the next three digits into firstThreeDigits. Before you invoke scanUpToCharactersFromSet(_:intoString:), the scanner’s reading cursor was at the position of the first found -. With the hyphen ignored, you get the phone number’s second component.
    3. Finds the next -. The scanner finishes the rest of the string and returns a successful status. With no hyphen left, it simply puts the remaining substring into lastFourDigits.

That’s all NSScanner does. It’s that easy! Now, it’s time to get your application started!

Getting Started

Download the starter project and extract the the contents of the ZIP file. Open EmailParser.xcodeproj in Xcode.

You’ll find the following:

  • DataSource.swift contains a pre-made structure that sets up the data source/delegate to populate a table view.
  • PostCell.swift contains all the properties that you need to display each individual data item.
  • Support/Main.storyboard contains a TableView with a custom cell on the left hand-side and a TextView on the other.

You’ll be parsing the data of 49 sample files in comp.sys.mac.hardware folder. Take a minute to browse though to see how it’s structured. You’ll be collecting items like Name, Email, and so on into a table so that they are easy to see at a glance.

Note: The starter project uses table views to present the data, so if you’re unfamiliar with table views, check out our OS X NSTableView Tutorial.

Build and run the project to see it in action.

Starter-Initial-Screen

The table view currently displays placeholder labels with [Field]Value prefix. By the end of the tutorial, those will be replaced with parsed data.

Understanding the Structure of Raw Samples

Before diving straight into parsing, it’s important to understand what you’re trying to achieve. Below is one of the sample files, with the data items you’ll be retrieving highlighted.

Data-Structure-Illustration

In summary, these data items are:

  • From field: this consists of the sender’s name and email. Parsing it can be tricky since the name may come before the email or vice versa; it might even contain one piece but not the other.
  • Subject, Date, Organization and Lines fields: these have values separated by colons.
  • Message segment: this can contain cost information and some of these following keywords: apple, macs, software, keyboard, printer, video, monitor, laser, scanner, disks, cost, price, floppy, card, and phone.

NSScanner is awesome; however, working with it can feel a bit cumbersome and far less “Swifty”, so you’ll convert the built-in methods like the one in the phone number example above to ones that return optionals.

Navigate to File\New\File… (or simply press Command+N). Select OS X > Source > Swift File and click Next. Set the file’s name to NSScanner+.swift, then click Create.

Open NSScanner+.swift and add the following extension:

extension NSScanner {
 
  func scanUpToCharactersFrom(set: NSCharacterSet) -> String? {
    var result: NSString?                                                                  // 1.
    return scanUpToCharactersFromSet(set, intoString: &result) ? (result as? String) : nil // 2.
  }
 
  func scanUpTo(string: String) -> String? {
    var result: NSString?
    return scanUpToString(string, intoString: &result) ? (result as? String) : nil
  }
 
  func scanDouble() -> Double? {
    var double: Double = 0
    return scanDouble(&double) ? double : nil
  }
}

These helper methods encapsulate some of the NSScanner methods you’ll use in this tutorial so that they return an optional String. These three methods share the same structure:

  1. Defines a result variable to hold the value returned by the scanner.
  2. Uses a ternary operator to check whether the scan is successful. If it is, converts result to String and returns it; otherwise simply returns nil.
Note: You can do the same to other NSScanner methods like you did above and save them to your arsenals:
  • scanDecimal:
  • scanFloat:
  • scanHexDouble:
  • scanHexFloat:
  • scanHexInt:
  • scanHexLongLong:
  • scanInteger:
  • scanInt:
  • scanLongLong:

Simple, right? Now go back to the main project and start parsing!

Creating the Data Structure

Navigate to File\New\File… (or simply press Command+N). Select OS X > Source > Swift File and click Next. Set the file’s name to HardwarePost.swift, then click Create.

Open HardwarePost.swift and add the following structure:

struct HardwarePost {
  // MARK: Properties
 
  // the fields' values once extracted placed in the properties
  let email: String
  let sender: String
  let subject: String
  let date: String
  let organization: String
  let numberOfLines: Int
  let message: String
 
  let costs: [Double]         // cost related information
  let keywords: Set<String>   // set of distinct keywords
}

This code defines HardwarePost structure that stores the parsed data. By default, Swift provides you a free constructor based on its properties, but you’ll come back to this later to implement your own custom initializer.

Are you ready for parsing in action with NSScanner? Let’s do this.

Creating the Data Parser

Navigate to File\New\File… (or simply press Command+N), select OS X > Source > Swift File and click Next. Set the file’s name to ParserEngine.swift, then click Create.

Open ParserEngine.swift and create ParserEngine class by adding the following code:

final class ParserEngine {
 
}

Extracting Metadata Fields

Consider the following sample metadata segment:

Metadata-Segment

Here’s where NSScanner comes in and separates the fields and their values. The image below gives you a general visual representation of this structure.

Field-Structure-Illustraion

Make sure ParserEngine.swift open and implement this code inside ParserEngine class:

// 1.
typealias Fields = (sender: String, email: String, subject: String, date: String, organization: String, lines: Int)
 
/// Returns a collection of predefined fields' extracted values
func fieldsByExtractingFrom(string: String) -> Fields {
  // 2.
  var (sender, email, subject, date, organization, lines) = ("", "", "", "", "", 0)
 
  // 3.
  let scanner = NSScanner(string: string)
  scanner.charactersToBeSkipped = NSCharacterSet(charactersInString: " :\n")
 
  // 4.
  while !scanner.atEnd {                    // A
    let field = scanner.scanUpTo(":") ?? "" // B
    let info = scanner.scanUpTo("\n") ?? "" // C
 
    // D
    switch field {
    case "From": (email, sender) = fromInfoByExtractingFrom(info) // E
    case "Subject": subject = info
    case "Date": date = info
    case "Organization": organization = info
    case "Lines": lines = Int(info) ?? 0
    default: break
    }
  }
 
  return (sender, email, subject, date, organization, lines)
}

Don’t panic! The Xcode error of an unresolved identifier will go away right in the next section.

Here’s what the above code does:

  1. Defines a Fields type alias for the tuple of parsed fields.
  2. Creates variables that will hold the returning values.
  3. Initializes a NSScanner instance and changes its charactersToBeSkipped property to also include a colon beside the default values – whitespace and linefeed.
  4. Obtains values of all the wanted fields by repeating the process below:
    1. Uses while to loop through string‘s content until it reaches the end.
    2. Invokes one of the helper functions you created earlier to get field‘s title before :.
    3. Continues scanning up to the end of the line where the linefeed character \n is located and assigns the result to info.
    4. Uses switch to find the matching field and stores its info property value into the proper variable.
    5. Analyzes From field by calling fromInfoByExtractingFrom(_:). You’ll implement the method after this section.

Remember the tricky part of From field? Hang tight because you’re going to need help from regular expression to overcome this challenge.

Note: Regular expressions are a great tool to manipulate strings with patterns, and this NSRegularExpression Tutorial gives a good overview of how to use them.

At the end of ParserEngine.swift, add the following String extension:

private extension String {
 
  func isMatched(pattern: String) -> Bool {
    return NSPredicate(format: "SELF MATCHES %@", pattern).evaluateWithObject(self)
  }
}

This extension defines a private helper method to find whether the string matches a given pattern using regular expressions.

It creates a NSPredicate object with a MATCHES operator using the regular expression pattern. Then it invokes evaluateWithObject(_:) to check if the string matches the conditions of the pattern.

Note: You can read more about NSPredicate in the official Apple documentation.

Now add the following method inside the ParserEngine implementation, just after fieldsByExtractingFrom(_:) method:

private func fromInfoByExtractingFrom(string: String) -> (email: String, sender: String) {
  let scanner = NSScanner(string: string)
 
  // 1.
  /*
   * ROGOSCHP@MAX.CC.Uregina.CA (Are we having Fun yet ???)
   * oelt0002@student.tc.umn.edu (Bret Oeltjen)
   * (iisi owner)
   * mbuntan@staff.tc.umn.edu ()
   * barry.davis@hal9k.ann-arbor.mi.us (Barry Davis)
   */
  if string.isMatched(".*[\\s]*\\({1}(.*)") { // A
    scanner.charactersToBeSkipped = NSCharacterSet(charactersInString: "() ") // B
 
    let email = scanner.scanUpTo("(")  // C
    let sender = scanner.scanUpTo(")") // D
 
    return (email ?? "", sender ?? "")
  }
 
  // 2.
  /*
   * "Jonathan L. Hutchison" <jh6r+@andrew.cmu.edu>
   * <BR4416A@auvm.american.edu>
   * Thomas Kephart <kephart@snowhite.eeap.cwru.edu>
   * Alexander Samuel McDiarmid <am2o+@andrew.cmu.edu>
   */
  if string.isMatched(".*[\\s]*<{1}(.*)") {
    scanner.charactersToBeSkipped = NSCharacterSet(charactersInString: "<> ")
 
    let sender = scanner.scanUpTo("<")
    let email = scanner.scanUpTo(">")
 
    return (email ?? "", sender ?? "")
  }
 
  // 3.
  return ("unknown", string)
}

After examining the 49 data sets, you end up with three cases to consider:

  • email (name)
  • name <email>
  • email with no name

Here’s what the code does:

  1. Matches string with the first pattern – email (name). If not, continues to the next case.
    1. Looks for zero or more occurrences of any character – .*, followed by zero or more occurrence of a space – [\\s]*, followed by one open parenthesis – \\({1} and finally zero or more occurrences of a string – (.*).
    2. Sets the NSScanner object’s charactersToBeSkipped to include: “(“, “)” and whitespace.
    3. Scans up to ( to get the email value.
    4. Scans up to ), which gives you the sender name. This extracts everything before ( and after ).
  2. Field-Value-Illustration

  3. Checks whether the given string matches the pattern – name <email>. The if body is practically the same as the first scenario, except that you deal with angle brackets.
  4. Finally, if neither of the two patterns is matched, this is the case where you only have an email. You’ll simply return the string for the email and “unknown” for sender.

At this point, you can build the project. The previous compile error is gone.

Starter-Initial-Screen

Note: NSDataDetector would be a better solution for known-data types like phone number, address, and email. You can check out this blog about email validation with NSDataDetector.

You’ve been working with NSScanner to analyze and retrieve information from a patterned string. In the next two sections, you’ll learn how to parse unstructured data.

Extracting Cost-Related Information

A good example of parsing unstructured data is to determine whether the email’s body contains cost-related information. To do this, you’ll use NSScanner to search for an occurrence of a dollar character: $.

Still working on ParserEngine.swift, add the following implementation inside ParserEngine class:

func costInfoByExtractingFrom(string: String) -> [Double] {
  // 1.
  var results = [Double]()
 
  // 2.
  let dollar = NSCharacterSet(charactersInString: "$")
 
  // 3.
  let scanner = NSScanner(string: string)
  scanner.charactersToBeSkipped = dollar
 
  // 4.
  while !scanner.atEnd && scanner.scanUpToCharactersFromSet(dollar, intoString: nil) {
    results += [scanner.scanDouble()].flatMap { $0 }
  }
 
  return results
}

The code is fairly straightforward:

  1. Defines an empty array to store the cost values.
  2. Creates a NSCharacterSet object with a $ character.
  3. Initializes a NSScanner instance and configures it to ignore the $ character.
  4. Loops through string‘s content and when a $ is found, grabs the number after $ with your helper method and appends it to results array.

Parsing the Message

Another example of parsing unstructured data is finding keywords in a given body of text. Your search strategy is to look at every word and check it against a set of keywords to see if it matches. You’ll use the whitespace and newline characters to take the words in the message as scanning.

Keywords-Parser-Illustration

Add the following code at the end of ParserEngine class:

// 1.
let keywords: Set<String> = ["apple", "macs", "software", "keyboard",
                             "printers", "printer", "video", "monitor",
                             "laser", "scanner", "disks", "cost", "price",
                             "floppy", "card", "phone"]
 
/// Return a set of keywords extracted from
func keywordsByExtractingFrom(string: String) -> Set<String> {
  // 2.
  var results: Set<String> = []
 
  // 3.
  let scanner = NSScanner(string: string)
 
  // 4.
  while !scanner.atEnd, let word = scanner.scanUpTo(" ")?.lowercaseString {
    keywords.contains(word) ? results.insert(word) : ()
  }
 
  return results
}

Here’s what this code does:

  1. Defines the keywords set that you’ll match against.
  2. Creates a Set of String to store the found keywords.
  3. Initializes a NSScanner instance. You’ll use the default charactersToBeSkipped, which are the whitespace and newline characters.
  4. For every word found, checks whether it’s one of the predefined keywords. If it is, appends it into results.

There — you have all of the necessary methods to acquire the desired information. Time to put them to good use and create HardwarePost instances for the 49 data files.

Connecting the Parser With Data Samples

Open HardwarePost.swift and add this initializer into HardWarePost structure:

init(fromData data: NSData) {
  // 1.
  let parser = ParserEngine()
 
  // 2.
  let string = String(data: data, encoding: NSUTF8StringEncoding) ?? ""
 
  // 3.
  let scanner = NSScanner(string: string)
 
  // 4.
  let metatdata = scanner.scanUpTo("\n\n") ?? ""
  let (sender, email, subject, date, organization, lines) = parser.fieldsByExtractingFrom(metatdata)
 
  // 5.
  self.sender = sender
  self.email = email
  self.subject = subject
  self.date = date
  self.organization = organization
  self.numberOfLines = lines
 
  // 6.
  let startIndex = string.startIndex.advancedBy(scanner.scanLocation)                         // A
  let message = string[startIndex..<string.endIndex]                                          // B
  self.message = message.stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet()) // C
 
  // 7.
  costs = parser.costInfoByExtractingFrom(message)
  keywords = parser.keywordsByExtractingFrom(message)
}

Here’s how HardwarePost initializes its properties:

  1. Simply creates a ParserEngine object named parser.
  2. Converts data into a String.
  3. Initializes an instance of NSScanner to parse the Metadata and Message segments, which are separated by “\n\n”.
  4. Scans up to the first \n\n to grab the metadata string, then invokes the parser‘s fieldsByExtractingFrom method to obtain all of the metadata fields.
  5. Assigns the parsing results to the HardwarePost properties.
  6. Prepares the message content:
    1. Gets the current reading cursor from scanner with scanLocation and converts it to String.CharacterView.Index, so you can substitute string by range.
    2. Assigns the remaining string that scanner has yet to read into the new message variable.
    3. Since message value still contains \n\n where the scanner left off from the previous reading, you need to trim it and give the new value back to the HardwarePost instance’s message property.
  7. Invokes the parser‘s methods with message to retrieve values for cost and keywords properties.

At this point, you can create HardwarePost instances directly from the files’ data. You are only few more steps from displaying the final product!

Displaying Parsed Data

Open PostCell.swift and add the following method inside the PostCell class implementation:

func configure(post: HardwarePost) {
 
  senderLabel.stringValue = post.sender
  emailLabel.stringValue = post.email
  dateLabel.stringValue = post.date
  subjectLabel.stringValue = post.subject
  organizationLabel.stringValue = post.organization
  numberOfLinesLabel.stringValue = "\(post.numberOfLines)"
 
  // 1.
  costLabel.stringValue = post.costs.isEmpty ? "NO" : post.costs.map { "\($0)" }.lazy.joinWithSeparator("; ")
 
  // 2.
  keywordsLabel.stringValue = post.keywords.isEmpty ? "No keywords found" : post.keywords.joinWithSeparator("; ")
}

This code assigns the post values to the cell labels. costLabel and keywordsLabel require special treatment because they can be empty. Here’s what happens:

  1. If the costs array is empty, it sets the costLabel string value to NO; otherwise, it concatenates the cost values with “; ” as a separator.
  2. Similarly, sets keywordsLabel string value to No words found for an empty set of post.keywords.

You’re almost there! Open DataSource.swift and add the following code into DataSource class:

let hardwarePosts: [HardwarePost] // 1.
 
override init() {
  self.hardwarePosts = NSBundle.mainBundle()                                    // 2.
    .URLsForResourcesWithExtension(nil, subdirectory: "comp.sys.mac.hardware")? // 3.
    .flatMap(NSData.init).lazy                                                  // 4.
    .map(HardwarePost.init) ?? []                                               // 5.
 
  super.init()
}

This is what the code does:

  1. Stores the HardwarePost instances.
  2. Obtains a reference to the application’s main Bundle.
  3. Retrieves urls of the sample files inside the comp.sys.mac.hardware directory.
  4. Lazily acquires an array of NSData instances by reading file contents with NSData failable initializer and flatMap. The idea of using flatMap is to get back a subarray containing only elements that are not nil.
  5. Finally, transforms the NSData results to a HardwarePost object and assigns them to DataSource‘s the hardwarePosts property.

Now you need to set up the table view’s data source and delegate so that your app can show your hard work.

Open DataSource.swift. Find numberOfRowsInTableView(_:) and replace it with the following:

func numberOfRowsInTableView(tableView: NSTableView) -> Int {
  return hardwarePosts.count
}

numberOfRowsInTableView(_:) is part of the table view’s data source protocol; it sets the number of rows of the table view.

Next, find tableView(_:viewForTableColumn:row:) and replace the comment that says: //TODO: Set up cell view with the code below:

cell.configure(hardwarePosts[row])

The table view invokes its delegate tableView(_:viewForTableColumn:row:) method to set up every individual cell. It gets a reference to the post for that row and invokes PostCell‘s configure() method to display the data.

Now you need to show the post in the text view when you select a post on the table view. Replace the initial implementation of tableViewSelectionDidChange(_:) with the following:

func tableViewSelectionDidChange(notification: NSNotification) {
  guard let tableView = notification.object as? NSTableView else {
    return
  }
  textView.string = hardwarePosts[tableView.selectedRow].message
}

tableViewSelectionDidChange(_:) is called when the table view’s selection has changed. When that happens, this code gets the hardware post for the selected row and displays the message in the text view.

Build and run your project.

starter-final

All of the parsed fields are now neatly displayed on the table. Select a cell on the left, and you’ll see the corresponding message on the right. Good Job!

Where to Go From Here?

Here’s the source code for the completed project.

There is so much more you can do with the data you have parsed. You could write a formatter that converts a HardwarePost object into JSON, XML, CSV or any other formats. With your new-found flexibility to represent data in different forms, you can share your data across different platforms.

If you’re interested in the study of computer languages and how they are implemented, take a class in comparative languages. Your course will likely cover formal languages and BNF grammars—all important concepts in the design and implementation of parsers.

For more information on NSScanner and other parsing theory, check out the following resources:

If you have any questions or comments, please join the discussion below!

The post NSScanner Tutorial for OS X appeared first on Ray Wenderlich.

iOS 10 by Tutorials: First 3 Chapters Now Available!

$
0
0

Good news – the first early access release of iOS 10 by Tutorials is now available!

This release has 3/14 chapters ready:

  • 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 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

This is the first of many early access releases for the book – stay tuned for some more early access releases soon!

iOS 10 by Tutorials Screencasts

As you probably know, in addition to making the book this year, we are also covering the content in screencast form! That way, you can choose how you prefer to learn: either written or video form.

To celebrate the first early access release of the book, we’re releasing a free iOS 10 screencast so you can see what they’re like.

The free screencast is on the iOS 10 Thread Sanitizer, which makes finding, diagnosing, and fixing threading issues much easier. Enjoy!

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 first early access release of the book (v0.1) immediately.
  • If you haven’t subscribed to raywenderlich.com yet, you should subscribe now! You will get access to the book, the screencasts, and access to our entire video tutorial library with over 500 videos.
  • If you just want the book, you can buy the book separately. This includes permanent access to the book, including early access versions and 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: First 3 Chapters Now Available! appeared first on Ray Wenderlich.

Video Tutorial: Intermediate Realm on iOS Part 2: Multiple Realm Files


Video Tutorial: Intermediate Realm on iOS Part 3: Encrypted Realm Files

Making A Mac App Scriptable Tutorial

$
0
0

Making a mac app scriptable tutorial: feature image

As an app developer, it’s near impossible to think of all the ways people will want to use your app. Wouldn’t it be cool to let your users create scripts to customize your app to their own personal needs?

With Applescript and Javascript for Automation (JXA), you can! In this making a Mac app scriptable tutorial you will discover how to add scripting capabilities to a sample application. You’ll start by learning how to control existing apps with scripting, and then extend a sample app to allow custom script actions.

Getting Started

Download the sample project, open it in Xcode and build and run to see how it looks:

Making a mac app scriptable tutorial: Scriptable Tasks app

The app shows a list of tasks with due dates in the next few days and the tags associated with each task. It uses an outline view to group the tasks by due date.

Note: Want to know more about outline views? Check out the NSOutlineView on OS X Tutorial on this site.

You might have noticed that you can’t add, edit, or delete any tasks. That’s by design – these actions will be handled by your user automation scripts.

Take a a look at the files in the project:

Making a mac app scriptable tutorial: Scriptable Tasks Project

  • There are 2 model class files: Task.swift and Tag.swift. These are the classes that you will be scripting.
  • The ViewController group handles the display and watches for changes in the data.
  • The Data group has a file with the sample tasks and a DataProvider that reads those tasks and handles any edits that arrive.
  • The AppDelegate uses a DataProvider object to keep a record of the app’s tasks.
  • The ScriptableTasks.sdef file is a crucial file…which you will explore in detail later.

There are sample scripts for this tutorial as well; download them here. There are two folders in this package: one for AppleScript and one for JavaScript. Since this tutorial isn’t focused on how to write scripts, you’ll be using each of the downloaded scripts to test the functionality that you’ll add to Scriptable Tasks.

Enough with the talk – time to move on to the scripting! :]

Using the Script Editor

Open up the Script Editor app, found in Applications/Utilities, and open a new document:

Making a mac app scriptable tutorial: Script Editor

You’ll see a set of four buttons in the top toolbar: Record, Stop, Run, and Compile. Compile checks that your scripting is syntactically correct, and Run does pretty much what what you’d expect.

At the bottom of the window, you’ll see three icons which switch between views. Description lets you add some information about your script, while Result shows the final result of running a script. The most useful option is the third button: Log.

The Log offers a further four options: Result, Messages, Events and Replies. Replies is the most informative, as it shows a log of every command and the return value of that command. When testing any scripts, I highly recommend the Log in Replies mode.

Note: If you ever open an AppleScript file and find it contains code like this: «class TaSk» whose «class TrFa» is false and «class CrDa», click Compile and it will be translated to readable AppleScript, provided you have the target app installed.

There are two scripting languages you’ll cover in this tutorial. The first is AppleScript, introduced with Mac System 7 in 1991, and uses an English-like syntax to make it usable by coders and non-coders alike.

The second is JavaScript for Automation (JXA), introduced by OSX Yosemite, which lets coders use the familiar JavaScript syntax to build their automation tasks.

The scripts in this tutorial will be presented in both AppleScript and JXA, so you’re free to wander down the path of whichever language you’d like to explore. :]

Note: Throughout this tutorial, the scripting code snippets are presented in AppleScript first, and immediately followed by the equivalent JavaScript version.

Exploring App Scripting With TextEdit

There’s a great little app already installed on your Mac that supports scripting: TextEdit. In the Script Editor, select Window/Library and look for the TextEdit entry. If it’s not there, click the Plus button at the top, navigate to your Applications folder and add TextEdit. Then double-click the TextEdit entry to open the TextEdit dictionary:

Making a mac app scriptable tutorial: Text Edit Dictionary

Every scriptable app has a dictionary, stored in a scripting definition (SDEF) file. The dictionary tells you what objects the app has, what properties the objects have and what commands the app responds to. In the above screen shot, you can see that TextEdit has paragraphs, and paragraphs have color and font properties. You will use this information to style some text.

Open 1. TextEdit Write.scpt from either the AppleScript or the JavaScript folder. Run the script; you’ll see TextEdit create and save a document.

You now have a new document, but it needs a bit of styling. Open 2. TextEdit Read Edit.scpt, run this script and you’ll see the document re-opened and styled as per the script.

Although delving into the actual script is beyond the scope of this tutorial, feel free to read the scripts in detail to see how they act on the TextEdit document.

As mentioned in the introduction, all apps are scriptable to some extent. To see this in action, ensure Scriptable Tasks is running. Next, open a new script window in Script Editor and enter one of the following scripts, depending on which language you’re using:

-- AppleScript
tell application "Scriptable Tasks" to quit

or

// JavaScript
Application("Scriptable Tasks"). quit();

Click Run and Scriptable Tasks should quit. Change the script to the following and click Run again:

tell application "Scriptable Tasks" to launch

or

Application("Scriptable Tasks").launch();

The app restarts, but doesn’t come to the foreground. To bring the app into focus, change launch to activate in the script above and click Run.

Now that you’ve seen that apps can respond to scripting commands, it’s time to add this ability to your app.

Making Your App Scriptable

The scripting definition file of your app defines what the app can do; it’s a little like an API. This file lives in your app project and specifies several things:

  • Standard scripting objects and commands, such as window, make, delete, count, open and quit.
  • Your own scriptable objects, properties and custom commands.

In order to make classes in your app scriptable, there are a few changes you’ll need to make to the app.

First, the scripting interface uses Key-Value-Coding to get and set the properties of objects. In Objective-C, all objects conformed to the KVC protocol automatically, but Swift objects don’t do so unless you make them subclasses of NSObject.

Next, scriptable classes need an Objective-C name that the scripting interface can recognize. To avoid namespace conflicts, Swift object names are mangled to give a unique representation. By prefixing the class definitions with @objc(YourClassName), you give them a name that can be used by the scripting engine.

Scriptable classes need object specifiers to help locate a particular object within the application or parent object, and finally, the app delegate must have access to the data store so it can return the application’s data to the scripts.

You don’t necessarily have to start your own scripting definition file from scratch, as Apple provides a standard SDEF file that you can use. Look in the /System/Library/ScriptingDefinitions/ directory for CocoaStandard.sdef. Open this file in Xcode and have a look; it’s XML with specific headers, a dictionary and inside that, the Standard Suite.

This is a useful starting point, and you could copy and paste this XML into your own SDEF file. However, in the interest of clean code, it’s not a good idea to leave your SDEF file full of commands and objects that your app does not support. To this end, the sample project contains a starter SDEF file with all unnecessary entries removed.

Close CocoaStandard.sdef and open ScriptableTasks.sdef. Add the following code near the end at the Insert Scriptable Tasks suite here comment:

<!-- 1 -->
<suite name="Scriptable Tasks Suite" code="ScTa" description="Scriptable Tasks suite.">
  <!-- 2 -->
  <class name="application" code="capp" description="An application's top level scripting object.">
    <cocoa class="NSApplication"/>
 
    <!-- 3 -->
    <element type="task" access="r">
      <cocoa key="tasks"/>
    </element>
  </class>
 
  <!-- Insert command here -->
 
  <!-- 4 -->
  <class name="task" code="TaSk" description="A task item" inherits="item" plural="tasks">
      <cocoa class="Task"/>
 
      <!-- 5 -->
      <property name="id" code="ID  " type="text" access="r"
          description="The unique identifier of the task.">
          <cocoa key="id"/>
      </property>
 
      <property name="name" code="pnam" type="text" access="rw"
          description="The title of the task.">
          <cocoa key="title"/>
      </property>
 
      <!-- 6 -->
      <property name="daysUntilDue" code="CrDa" type="number" access="rw"
      description="The number of days before this task is due."/>
      <property name="completed" code="TrFa" type="boolean" access="rw"
      description="Has the task been completed?"/>
 
      <!-- 7 -->
      <!-- Insert element of tags here -->
 
      <!-- Insert responds-to command here -->
 
  </class>
 
  <!-- Insert tag class here -->
 
</suite>

This chunk of XML does a lot of work. Taking it bit by bit:

  1. The outermost element is a suite, so your SDEF file now has two suites: Standard Suite and Scriptable Tasks Suite. Everything in the SDEF file needs a four-character code. Apple codes are nearly always in lower-case and you will use a few of them for specific purposes. For your own suites, classes and properties, it’s best to use a random mix of upper-case, lower-case and symbols to avoid conflicts.
  2. The next section defines the application and must use the code "capp". You must specify the class of the application; if you had subclassed NSApplication, you would use your subclass name here.
  3. The application contains elements. In this app, the elements are stored in an array called tasks in the app delegate. In scripting terms, elements are the objects that the app or other objects can contain.
  4. The last chunk defines the Task class that the application contains. The plural name for accessing multiples is tasks. The class in the app that backs this object type is Task.
  5. The first two properties are special. Look at their codes: "ID " and "pnam". "ID " (note the two spaces after the letters) identifies the unique identifier of the object. "pnam" specifies the name property of the object. You can access objects directly using either of these properties.

    "ID " is read-only, as scripts should not change a unique identifier, but "pnam" is read-write. Both of these are text properties. The "pnam" property maps to the title property of the Task object.

  6. The remaining two properties are a number property for daysUntilDue and a Boolean for completed. They use the same name in the object and the script, so you don’t need to specify the cocoa key.
  7. The “Insert…” comments are placeholders for when you need to add more to this file.

Open Info.plist, right-click in the blank space below the entries and select Add Row. Type an upper-case S and the list of suggestions will scroll to Scriptable. Select it and change the setting to YES.

Repeat this process to select the next item down: Scripting definition file name. Set this to the name of your SDEF file: ScriptableTasks.sdef

If you prefer to edit the Info.plist as source code, you can alternatively add the following entries inside the main dict:

<key>NSAppleScriptEnabled</key>
<true/>
<key>OSAScriptingDefinition</key>
<string>ScriptableTasks.sdef</string>

Now you have to modify the app delegate to handle requests that come via script.

Open AppDelegate.swift file and add the following to the end of the file:

extension AppDelegate {
  // 1
  override func application(sender: NSApplication, delegateHandlesKey key: String) -> Bool {
    return key == "tasks"
  }
 
  // 2
  func insertObject(object: Task, inTasksAtIndex index: Int) {
    tasks = dataProvider.insertNewTask(object, atIndex: index)
  }
 
  func removeObjectFromTasksAtIndex(index: Int) {
    tasks = dataProvider.deleteTask(atIndex: index)
  }
}

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

  1. When a script asks for tasks data, this method will confirm that the app delegate can handle it.
  2. If a script tries to insert, edit or delete data, these methods will pass those requests along to dataProvider.

To make the Task model class available to the scripts, you have to do a bit more coding.

Open Task.swift and change the class definition line to the following:

@objc(Task) class Task: NSObject {

Xcode will immediately complain that init requires the override keyword, so let Fix-It do that. This is required as this class now has a superclass:

override init() {

Task.swift needs one more change: an object specifier. Insert the following method into the Task class:

override var objectSpecifier: NSScriptObjectSpecifier {
  // 1
  let appDescription = NSApplication.sharedApplication().classDescription as! NSScriptClassDescription
 
  // 2
  let specifier = NSUniqueIDSpecifier(containerClassDescription: appDescription,
    containerSpecifier: nil, key: "tasks", uniqueID: id)
  return specifier
}

Taking each numbered comment in turn:

  1. Get a description of the app’s class since the app is the container for tasks.
  2. Get a description of the task by id within the app. This is why the Task class has an id property – so that each task can be correctly specified.

You’re finally ready to start scripting your app!

Scripting Your App

Before you start, make sure to quit any running instance of the app that Script Editor might have opened.

Build and run Scriptable Tasks; right-click on the icon in the Dock and select Options/Show in Finder. Quit the Script Editor app and restart it to let it pick up the changes to your app.

Open the Library window, and drag the Scriptable Tasks app from the Finder into the Library window.

If you get an error saying the app is not scriptable, try quitting Script Editor and starting it again as it sometimes doesn’t register a freshly built app. If it still fails to import, go back and double-check your changes to the SDEF file.

Double-click Scriptable Tasks in the Library to see the app’s dictionary:

Making a mac app scriptable tutorial: Scriptable Tasks Dictionary 1

You’ll see the Standard Suite and the Scriptable Tasks Suite. Click on the Scriptable Tasks suite, and you will see what you put into the SDEF file. The application contains tasks, and a task has four properties.

Change the scripting language in the dictionary to JavaScript using the Language popup in the toolbar. You will see the same information but with one important change. The cases of classes and properties have changed. I have no idea why this is, but it’s one of those “gotchas” you need to watch out for.

In Script Editor, make a new script file and set the editor to show Log/Replies. Test either of the following scripts, making sure to select the appropriate language in the language pop-up:

tell application "Scriptable Tasks"
  get every task
end tell

or

app = Application("Scriptable Tasks");
app.tasks();

In the log, you will see a list of the tasks by ID. For more useful information, edit the scripts as follows:

tell application "Scriptable Tasks"
  get the name of every task
end tell

or

app = Application("Scriptable Tasks");
app.tasks.name();

Making a mac app scriptable tutorial: AppleScript Tasks

Making a mac app scriptable tutorial: JavaScript Tasks

Try out a few more of the sample scripts you downloaded earlier. When running the scripts, make sure you set the Script Editor to show Log/Replies so that you can see the results along the way.

Each script quits the app before running it again; this is to reset the data after any edits so that the sample scripts work as expected. You wouldn’t normally do this in your own scripts.

Note: Script Editor can get very confused as you build updated versions of the app, because it tries to keep a version running at all times if you have an open script that is using the app. This often ends up as an older version of the app, so before every build, quit the app.

If you see two copies of the Scriptable Tasks app running at any time, or if there appears to be a script error in any of the samples, you can be sure that Script Editor has glommed on to the wrong version of the app. The easiest fix is to quit all copies of the app and quit Script Editor. Clean the Xcode build (Product/Clean), then build and run again.

Restart Script Editor and when it opens the script, click Compile and then click Run. And if THAT doesn’t work, delete Derived Data for the app in Xcode/Window/Projects.

Try out the next two sample scripts:

3. Get Tasks.scpt

This script retrieves the number of tasks and the names of tasks using various filters. Make note of the following:

  • JavaScript counts from 0, AppleScript counts from 1.
  • Text searches are case-insensitive.

4. Add Edit Tasks.scpt

This script adds new tasks. Toggle the completed flag on the first task, and try to create a task with the same name as another.

Hmmm… creating a task with the same name worked! Now you have two “Feed the cat” tasks. The cat will be thrilled, but for the purposes of this app, task names should be unique. Trying to add a task with a name that is already in use should have produced an error.

Back in Xcode, look in AppDelegate.swift and you can see that when the script wants to insert an object, the app delegate passes that call to its dataProvider. In DataProvider.swift, look at insertNewTask(_:atIndex:), which inserts an existing task into the array or appends a new task to the end.

Time to add a check here. Replace the function with the following:

mutating func insertNewTask(task: Task, atIndex index: Int) -> [Task] {
  // 1
  if taskWithTitleExists(task.title) {
    // 2
    let command = NSScriptCommand.currentCommand()
    command?.scriptErrorNumber = errOSACantAssign
    command?.scriptErrorString = "Task with the title '\(task.title)' already exists"
  } else {
    // 3
    if index >= tasks.count {
      tasks.append(task)
    } else {
      tasks.insert(task, atIndex: index)
    }
 
    postNotificationOfChanges()
  }
 
  return tasks
}

Here’s what each commented section does:

  1. Use an existing function to check if a task with this name already exists.
  2. If the name is not unique:
    • Get a reference to the scripting command that called this function.
    • Set the command’s errorNumber and errorString properties; errOSACantAssign is one of the standard AppleScript error codes. These will be sent back to the calling script.
  3. If the name is unique:
    • Process the task as before.
    • Post a notification of data changes. The ViewController will see this and update the display.

Quit the app if running, then build and run your app. Run the 4. Add Edit Tasks scripts again. This time you should get an error dialog and no duplicate tasks will be created. Sorry about that, cat…

Making a mac app scriptable tutorial: Hungry Cat

5. Delete Tasks.scpt

This script deletes a task, checks if a particular task exists and deletes it if possible, and finally deletes all completed tasks.

Working With Nested Objects

In the sample app, the second column displays a list of tags assigned to each task. So far, you have no way of working with them via scripts – time to fix that!

Object specifiers can handle a hierarchy of objects. That’s what you have here, with the application owning the tasks and each task owning its tags.

As with the Task class, you need to make the Tag scriptable.

Open Tag.swift and make the following changes:

  • Change the class definition line to @objc(Tag) class Tag: NSObject {
  • Add the override keyword to init.
  • Add the object specifier method:
override var objectSpecifier: NSScriptObjectSpecifier {
  // 1
  guard let task = task else { return NSScriptObjectSpecifier() }
 
  // 2
  guard let taskClassDescription = task.classDescription as? NSScriptClassDescription else {
    return NSScriptObjectSpecifier()
  }
 
  // 3
  let taskSpecifier = task.objectSpecifier
 
  // 4
  let specifier = NSUniqueIDSpecifier(containerClassDescription: taskClassDescription,
    containerSpecifier: taskSpecifier, key: "tags", uniqueID: id)
  return specifier
}

The above code is relatively straightforward:

  1. Check that the tag has an assigned task.
  2. Check that the task has a class description of the correct class.
  3. Get the object specifier for the parent task.
  4. Construct the object specifier for the tag contained inside the task and return it.

Add the following to the SDEF file at the Insert tag class here comment:

<class name="tag" code="TaGg" description="A tag" inherits="item" plural="tags">
  <cocoa class="Tag"/>
  <property name="id" code="ID  " type="text" access="r"
    description="The unique identifier of the tag.">
    <cocoa key="uniqueID"/>
  </property>
  <property name="name" code="pnam" type="text" access="rw"
    description="The name of the tag.">
    <cocoa key="name"/>
  </property>
</class>

This is very similar to the data for the Task class, but a tag only has two exposed properties: id and name.

Now the Task section has to be edited to indicate that it contains tag elements.

Add the following code to the Task class XML, at the Insert element of tags here comment:

<element type="tag" access="rw">
  <cocoa key="tags"/>
</element>

Quit the app, then build and run the app again.

Go back to the Script Editor; if the Scriptable Tasks dictionary is open, close and re-open it. See if it contains information about tags.

If not, remove the Scriptable Tasks entry from the Library and add it again by dragging the app into the window:

Making a mac app scriptable tutorial: Scriptable Tasks Dictionary 2

Try one of the following scripts:

tell application "Scriptable Tasks"
  get the name of every tag of task 1
end tell

or

app = Application("Scriptable Tasks");
app.tasks[0].tags.name();

The app now lets you retrieve tags – but what about adding new ones?

You may have noticed in Tag.swift that each Tag object has a weak reference to its owning task. That helps create the links when getting the object specifier, so this task property must be set when assigning a new tag to a task.

Open Task.swift and add the following method to the Task class:

override func newScriptingObjectOfClass(objectClass: AnyClass, forValueForKey key: String,
  withContentsValue contentsValue: AnyObject?, properties: [String: AnyObject]) -> AnyObject? {
 
    let tag: Tag = super.newScriptingObjectOfClass(objectClass, forValueForKey: key,
      withContentsValue: contentsValue,
      properties: properties) as! Tag
    tag.task = self
 
    return tag
}

This method is sent to the container of the new object, which why you put it into the Task class and not the Tag class. The call is passed to super to get the new tag, and then the task property is assigned.

Quit and build and run your app. Now run the sample script 6. Tasks With Tags.scpt which lists tag names, lists the tasks with a specified tag, and deletes and create tags.

Adding Custom Commands

There is one more step you can take when making an app scriptable: adding custom commands. In earlier scripts, you toggled the completed flag of a task directly. But wouldn’t it be better – and safer – if scripts didn’t change the property directly, but instead used a command to do this?

Consider the following script:

mark the first task as "done"
mark task "Feed the cat" as "not done"

I’m sure you’re already reaching for the SDEF file and you would be correct: the command has to be defined there first.

There are two steps that need to happen here:

  1. Tell the application that this command exists and what its parameters will be.
  2. Tell the Task class that it responds to the command and what method to call to implement it.

Inside the Scriptable Tasks suite, but outside any class, add the following at the Insert command here comment:

<command name="mark" code="TaSktext">
  <direct-parameter description="One task" type="task"/>
  <parameter name="as" code="DFLG" description="'done' or 'not done'" type="text">
    <cocoa key="doneFlag"/>
  </parameter>
</command>

“Wait a minute!” you say. “Earlier you said that codes had to be four characters, and now I have one with eight? What’s going on here?”

When defining a method, you provide a code that combines the codes or types of the parameters – in this case a Task object with some text.

Inside the Task class definition, at the Insert responds-to command here comment, add the following code:

<responds-to command="mark">
  <cocoa method="markAsDone:"/>
</responds-to>

Now head back to Task.swift and add the following method:

func markAsDone(command: NSScriptCommand) {
  if let task = command.evaluatedReceivers as? Task,
    doneFlag = command.evaluatedArguments?["doneFlag"] as? String {
      if self == task {
        if doneFlag == "done" {
          completed = true
        } else if doneFlag == "not done" {
          completed = false
        }
        // if doneFlag doesn't match either string, leave un-changed
    }
  }
}

The parameter to markAsDone(_:) is an NSScriptCommand which has two properties of interest: evaluatedReceivers and evaluatedArguments. From them, you try to get the task and the string parameter and use them to adjust the task accordingly.

Quit and build and run your app again. Check the dictionary in the Script Editor, and delete and re-import it if the mark command is not showing:

Making a mac app scriptable tutorial: Scriptable Tasks Dictionary 3

You should now be able to run the 7. Custom Command.scpt scripts and see your new command in operation.

Where to Go From Here?

You can download the final version of the sample project here.

There wasn’t room to cover inter-app communication in this making a mac app scriptable tutorial, but to see how to work between apps, check out 8. Inter-App Communication.scpt for some examples. This script gathers a list of incomplete tasks due today and tomorrow, inserts them into a new TextEdit file, styles the text and saves the file.

For more information about scriptable apps, the official Apple docs on Scriptable Applications are a good start, as is Apple’s Overview of Cocoa Support for Scriptable Applications.

Interested in learning more about JXA? Check out the Introduction to JavaScript for Automation Release Notes.

I hope you enjoyed this making a mac app scriptable tutorial; if you have any questions or comments, please join the forum discussion below!

The post Making A Mac App Scriptable Tutorial appeared first on Ray Wenderlich.

Video Tutorial: Intermediate Realm on iOS Part 4: Migrations Part 1

Screencast: Beginning C# Part 5: Arrays

$
0
0

Links

If you’ve never used Unity or if you are still really green, read this article: Introduction to Unity: Getting Started

Previous Video: Operators

Challenge

In the HighScore script, add a scores array, put some random values in it, and print out the average to the console.

Challenge Solution

Resources

Download Starter Demo
Download Finished Challenge
Download Start Challenge
Download Finished Challenge

The post Screencast: Beginning C# Part 5: Arrays appeared first on Ray Wenderlich.

Readers’ App Reviews – July 2016

$
0
0

Review-2016-July-feature

I’m sure many of you are hard at work on your iOS 10 releases. Hopefully a couple of you are also working on MacOS Sierra and watchOS 3 releases as well. Remember, although iOS is king it’s not the only platform! ;]

Here at raywenderlich.com, we’re updating our books and tutorials as quickly as we can. And I’m sure all of you are rushing to update your apps as well!

Of course, some of you are are saying “Who has time to wait for iOS 10?!” and are releasing awesome apps today. My inbox has been full with new app submissions and I’ve picked just a few to share with you. Sadly, I never have time to write about them all so be sure to check out the honorable mentions at the end.

This month we have:

  • An app says “keep running – or else!”
  • An app to keep track of your children’s funny quotes
  • An app to help you bask in Github vanity
  • And of course, much more!

Keep reading to see the the latest apps released by raywenderlich.com readers like you.

Read

read
Read is an app for children aged 4-8 that is designed to making learning the alphabet and reading skills fun and progressive.

Read is packed with 20 lessons, 120 exercises, and more than 150 interactive stories. Read is aimed at children learning to read for the very first time and can take them all the way to a 2nd grade reading level.

The neat thing about Read is it focuses on teaching in practical ways. For example, letters are introduced in order of frequency in the English language instead of their order in the alphabet. Read also includes educational tips and detailed contextual help for parents and teachers for each lesson.

Activities include Letter Tracing, Spelling from sounds, Pick the word by listening, Listen and write, Interactive writing pad that reads words aloud, stories and questions, and many many more.

Read is great on the iPhone and even better on the iPad. If you have a young child, definitely download it and give it a try.

FinCalc

FinCalc
FinCalc is an easy to use financial calculator that will help calculate car loans, home loans, and retirement savings.

FinCalc is designed to be very easy to use. Rather than a blank form, each calculator is made up of sliders with relative ranges to what you’d expect. Adjusting any of the sliders results in a live update of the totals and graph.

On the home and auto loan calculators the graph shows both the principle and interest so you can see visually how much interest you’re paying as you adjust the parameters like the rate and repayment term. You can also see a schedule for individual payments with principle contribution amounts.

The retirement calculator makes it easy to adjust your starting and retirement age as well as expected raise and investment return rates. Watching what your retirement nest egg grow or shrink as you adjust the parameters can be very enlightening!

PowerRunner

PowerRunner
Lots of apps help you listen to your music while you run, but only PowerRunner will keep you running.

After you select your preferred music from your iTunes library, you can set your targeted pace for each run. If your current pace drops under your goal, PowerRunner will pause the music. It won’t resume until you get your current pace back over your target. This is a great way to give you direct feedback on your running and help you maintain a solid pace while you run.

PowerRunner will also save each of your runs including a map of where you ran, your average pace, overall distance, and total time. It will even color code your path based on your pace to give you a really cool visualization of your entire run.

Tower of Tasks

toweroftasks
Tower of Tasks is a todo list with personality.

Tower of Tasks helps you get things done like any todo list. However Tower of Tasks includes various graphics and animations to give you a little extra motivation.

By limiting the total number of high priority tasks, Tower of Tasks will help you focus on whats important. And you can set timed reminders on a per task basis.

In addition, the app includes a today extension for high priority tasks, an Apple Watch app, and even iCloud support. Somebody’s been reading their tutorials! :]

Circlestouch

circlestouch
Circlestouch is an addictive reaction game that has a calming effect as you play.

In Circlestouch, various colored circles appear randomly placed on the screen. Some colors are safe to touch, others are not. You need to tap as many of the safe colors as you can while avoiding the dangerous colors.

In each level, those colors change. The colors are on a similar spectrum such as shades of green or red. It can take a little more time than you’d think to mentally process which are safe to touch. There are also various power-ups that will help change the game as they appear.

And of course, Circlestouch supports GameCenter so you can challenge your friends to make it further than you.

Upside Offers

UpsideOffers
Upside Offers brings great coupons right to your phone.

Upside Offers is like your own personal coupon booklet based on your preferences and location. The app makes it easy to discover deals for your favorite brands, find the nearest place that accepts the coupon, and quickly redeem the deal with a barcode. No longer do you need to find, cut, and save coupons to save money. Upside Offers makes it quick and convenient.

Upside Offers is based on your Facebook likes. It uses likes on popular brands to only show you deals you care about. You can easily customize it right in the app by searching for your favorite brands and adding them to your list. Best of all, the manufacturer coupons are already accepted at places you shop like Walmart, Kroger, 7 Eleven, Publix, Fresh Market, and tons more.

Quoties

Quoties
Kids Say the Darndest Things. Quoties is an app for parents to help remember all the crazy things that kids say.

Quoties allows you to save a quote quickly and easily right on your phone. Its easy to have multiple children and make sure quotes are attributed to the right child. Searching is easy with full quote text support. You can easily sort them by date and filter by child.

Quoties also supports native sharing functionality making it super easy to share with all your favorite social apps from Twitter to iMessage.

Octopodium

octopodium
Octopodium is a Github stats app that surfaces all sorts of cool metadata on Github.

Octopodium will show top users for each language based on stars. You can easily filter by country or city as well to get a local look. Its quite fun to check out your own city to see who’s the top. You can also use Octopodium to see trending repositories filtered by language and timeframe. Octopodium also makes it easy to search developers to see details about them such as repos, languages, stars, etc.

Perhaps coolest of all, Octopodium is an open source app. So you can take a peek to see how its built and help improve it on its official repository.

Honorable Mentions

Every month I get way more submissions than I have time to write about. I download every app to try them out, but only have time to review a select few. I love seeing our readers through the apps submitted to me every month.

It’s not a popularity contest or even a favorite picking contest — I just try to share a snapshot of the community through your submissions. Please take a moment and try these other fantastic apps from readers like you.

Catch the Gnat – Augmented Reality Game
Cricket Darts Chalkboard
Fun With Letters
TripTic
RaceRunner – A Run-Tracking App for Runners Who Race
My Smart Album – The best album for your pictures
Shifter Solitaire
Draw & Guess!
XDrops – Color Matching with a Twist!

Where To Go From Here?

Each month, I really enjoy seeing what our community of readers comes up with. The apps you build are the reason we keep writing tutorials. Make sure you tell me about your next one, submit here.

If you saw an app your liked, hop to the App Store and leave a review! A good review always makes a dev’s day. And make sure you tell them you’re from raywenderlich.com; this is a community of makers.

If you’ve never made an app, this is the month! Check out our free tutorials to become an iOS star. What are you waiting for – I want to see your app next month.

The post Readers’ App Reviews – July 2016 appeared first on Ray Wenderlich.

Video Tutorial: Intermediate Realm on iOS Part 5: Migrations Part 2

Video Tutorial: Intermediate Realm on iOS Part 6: Conclusion


How To Create an Uber Splash Screen

$
0
0

Uber-feature

Oh, the wonderful splash screen—a chance for developers to go wild with fun animations as the app frantically pings API endpoints for critical data needed to function. The splash screen (not to be confused with the static, animation-free launch screen) can play an important role in an app: keeping the user interested while waiting for the app to start.

While there are many splash screens out there, you’d be hard pressed to find one as beautiful as Uber’s. In the first quarter of 2016, Uber released a major rebranding strategy led by its CEO. One of the results of this rebranding was a very cool splash screen.

This tutorial aims to replicate Uber’s animation to a very close approximation. It makes heavy use of CALayers and CAAnimations, as well as their subclassed counterparts. Rather than introducing the concepts found in these classes, this tutorial will focus more on using these classes in a production-quality animation. To learn the concepts behind these animations, check out Marin Todorov’s Intermediate iOS Animation video series.

Getting Started

Since there are a significant number of animations to implement in this tutorial, you’ll start with a project that already has the CALayers created for all the lovely animations to come. Download the starter project here.

The starter project is an app called Fuber. Fuber is an on-demand ride sharing service which allows passengers to request Segway drivers to transport them to different locations in urban environments. Fuber has grown rapidly and now serves Segway passengers in over 60 countries, but has faced opposition from numerous governments as well as Segway unions for Fuber’s use of contract Segway drivers. :]

Splash screen

By the end of this tutorial, you will have created a splash screen animation that looks like this:

Fuber Animation

Open and run the starter Fuber project and take a look.

From a UIViewController standpoint, the app launches SplashViewController from its parent view controller, RootContainerViewController, which has the power to transition out child UIViewControllers. It loops through the splash screen animations until the app is ready to launch. This could happen when there is a handshake success to an API endpoint and the app has the necessary data to continue. It’s worth mentioning that in this sample project, the splash screen lives in its own module.

There are two methods implemented in RootContainerViewController: showSplashViewController() and showSplashViewControllerNoPing(). For the majority of this tutorial, you will call showSplashViewControllerNoPing() which only loops through the animations, so that you can focus on animating the subviews in SplashViewController and later on you will use showSplashViewController() to simulate an API delay then transition to the main controller.

Splash Screen Views and Layers Composition

SplashViewController‘s view contains two subviews. The first subview consists of the “ripple grid” background known as the TileGridView, which contains a grid-like layout of subview instances called TileView. The other subview consists of the animating ‘U’ icon, known as the AnimatedULogoView.

Splash Screen Fuber-View-Hierarchy

The AnimatedULogoView contains four CAShapeLayers:

  • circleLayer represents the circular white background for the ‘U’.
  • lineLayer is the straight line that goes from the middle of the circleLayer layout to its outer edge.
  • squareLayer is the square that lies in the center of the circleLayer layer.
  • maskLayer is used as a mask for the view. It is used to collapse all the other layers in one easy animation when its bounds are changed through animation.

Combined, these CAShaperLayers create the Fuber ‘U’.

RiderIconView

Now that you know how these layers are composed, it’s time to generate the animations to make the AnimatedULogoView animatable.

Animating the Circle

When working with animations, it’s best to eliminate visual “noise” and focus on the animation currently being implemented. Navigate to AnimatedULogoView.swift. In init(frame:), comment out all the sublayers being added to the view except the circleLayer layer. You’ll add them back in one at a time as you complete the animations. The code should now look like this:

override init(frame: CGRect) {
  super.init(frame: frame)
 
  circleLayer = generateCircleLayer()
  lineLayer = generateLineLayer()
  squareLayer = generateSquareLayer()
  maskLayer = generateMaskLayer()
 
//  layer.mask = maskLayer
  layer.addSublayer(circleLayer)
//  layer.addSublayer(lineLayer)
//  layer.addSublayer(squareLayer)
}

Find generateCircleLayer() to understand how the circle is created. It’s a simple CAShapeLayer created with a UIBezierPath. Pay attention to this line:

layer.path = UIBezierPath(arcCenter: CGPointZero, radius: radius/2, startAngle: -CGFloat(M_PI_2), endAngle: CGFloat(3*M_PI_2), clockwise: true).CGPath

By default and if you provide 0 as the startAngle, arc bezier paths start from the right (3 o’clock position). By providing -M_PI_2 which is -90 degrees, you start from the top and the endAngle would be 270 degrees or 3*M_PI_2 which again is the top of the circle. Also pay attention that because you want to animate the stroke, you are using the radius value as the lineWidth.

The circleLayer animation needs to be comprised of three CAAnimations: a CAKeyframeAnimation animating a strokeEnd, a CABasicAnimation animating a transform and a CAAnimationGroup used to wrap the two together. You’ll create these animations one at a time.

Navigate to the animateCircleLayer() placeholder and add the following:

  // strokeEnd
  let strokeEndAnimation = CAKeyframeAnimation(keyPath: "strokeEnd")
  strokeEndAnimation.timingFunction = strokeEndTimingFunction
  strokeEndAnimation.duration = kAnimationDuration - kAnimationDurationDelay
  strokeEndAnimation.values = [0.0, 1.0]
  strokeEndAnimation.keyTimes = [0.0, 1.0]

By providing 0.0 and 1.0 as the animation values you instruct the Core Animation framework to start from the startAngle and stroke the circle to the endAngle, creating the cool “clock-like” animation. So as the value of strokeEnd increases, the length of the line segment along the circumference increases, and the circle is gradually “filled in”. For this particular example, if you were to change the values property to [0.0, 0.5], only half of the circle would be drawn because the strokeEnd would only reach half-way around the circle’s circumference during the animation.

Now add the transform animation:

  // transform
  let transformAnimation = CABasicAnimation(keyPath: "transform")
  transformAnimation.timingFunction = strokeEndTimingFunction
  transformAnimation.duration = kAnimationDuration - kAnimationDurationDelay
 
  var startingTransform = CATransform3DMakeRotation(-CGFloat(M_PI_4), 0, 0, 1)
  startingTransform = CATransform3DScale(startingTransform, 0.25, 0.25, 1)
  transformAnimation.fromValue = NSValue(CATransform3D: startingTransform)
  transformAnimation.toValue = NSValue(CATransform3D: CATransform3DIdentity)

This animation performs both a scale transform and a rotational transform about the z-axis. This results in the circleLayer layer growing while rotating clockwise by 45 degrees. This rotation is important because it needs to match the position and speed of the lineLayer when it’s being animated along with the rest of the layers.

Finally, add a CAAnimationGroup to the bottom of animateCircleLayer(). This animation encapsulates the previous two animations, so that you only need to add one animation to the circleLayer layer.

  // Group
  let groupAnimation = CAAnimationGroup()
  groupAnimation.animations = [strokeEndAnimation, transformAnimation]
  groupAnimation.repeatCount = Float.infinity
  groupAnimation.duration = kAnimationDuration
  groupAnimation.beginTime = beginTime
  groupAnimation.timeOffset = startTimeOffset
 
  circleLayer.addAnimation(groupAnimation, forKey: "looping")

This CAAnimationGroup has two notable properties being modified: beginTime and timeOffset. If you are unfamiliar with either one, a great description of these properties and how they’re used can be found here.

This groupAnimation‘s beginTime is being set in reference to the timing of its parent view.

The timeOffset is needed because the animation actually starts halfway through on its first run. When you have more animations completed, try changing the value of startTimeOffset and observe the visual differences.

Add the groupAnimation to circleLayer, then build and run the application to see what it looks like.

Splash Screen CircleIn Animation

Note: Try removing either the strokeEndAnimation or transformAnimation in the groupAnimation.animations array to really get an idea of what each animation does. Try to experiment like this for each animation you create in this tutorial. You’ll be suprised how different combinations of animations can produce unique visuals you would never have anticipated.

Animating the Line

With the animations of circleLayer complete, it’s time to address the lineLayer‘s animations. While still in AnimatedULogoView.swift, navigate to startAnimating() and comment out all the calls to animating methods except animateLineLayer(). The result should look like the code below:

public func startAnimating() {
  beginTime = CACurrentMediaTime()
  layer.anchorPoint = CGPointZero
 
//  animateMaskLayer()
//  animateCircleLayer()
  animateLineLayer()
//  animateSquareLayer()
}

In addition, change the content in init(frame:) so that circleLayer and lineLayer are the only CALayers being used:

override init(frame: CGRect) {
  super.init(frame: frame)
 
  circleLayer = generateCircleLayer()
  lineLayer = generateLineLayer()
  squareLayer = generateSquareLayer()
  maskLayer = generateMaskLayer()
 
//  layer.mask = maskLayer
  layer.addSublayer(circleLayer)
  layer.addSublayer(lineLayer)
//  layer.addSublayer(squareLayer)
}

With the CALayers/animations properly commented out, go to animateLineLayer() and implement the next group of animations:

  // lineWidth
  let lineWidthAnimation = CAKeyframeAnimation(keyPath: "lineWidth")
  lineWidthAnimation.values = [0.0, 5.0, 0.0]
  lineWidthAnimation.timingFunctions = [strokeEndTimingFunction, circleLayerTimingFunction]
  lineWidthAnimation.duration = kAnimationDuration
  lineWidthAnimation.keyTimes = [0.0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0]

This animation is responsible for increasing then decreasing the lineLayer‘s width.

For the next animation, add the following:

  // transform
  let transformAnimation = CAKeyframeAnimation(keyPath: "transform")
  transformAnimation.timingFunctions = [strokeEndTimingFunction, circleLayerTimingFunction]
  transformAnimation.duration = kAnimationDuration
  transformAnimation.keyTimes = [0.0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0]
 
  var transform = CATransform3DMakeRotation(-CGFloat(M_PI_4), 0.0, 0.0, 1.0)
  transform = CATransform3DScale(transform, 0.25, 0.25, 1.0)
  transformAnimation.values = [NSValue(CATransform3D: transform),
                               NSValue(CATransform3D: CATransform3DIdentity),
                               NSValue(CATransform3D: CATransform3DMakeScale(0.15, 0.15, 1.0))]

Much like the circleLayer transform animation, here you’re defining a clockwise rotation about the z-axis. For the line, however, you’re also performing a 25% scale transform, quickly followed by an identity transform before a final scale to 15% of its original size.

Group the animations together using a CAAnimationGroup and add it to lineLayer:

  // Group
  let groupAnimation = CAAnimationGroup()
  groupAnimation.repeatCount = Float.infinity
  groupAnimation.removedOnCompletion = false
  groupAnimation.duration = kAnimationDuration
  groupAnimation.beginTime = beginTime
  groupAnimation.animations = [lineWidthAnimation, transformAnimation]
  groupAnimation.timeOffset = startTimeOffset
 
  lineLayer.addAnimation(groupAnimation, forKey: "looping")

Build and run, and observe the prettiness.

Splash Screen Knockoutline Animation

Note that you used the same -M_PI_4 initial transform value to align the line and the stroke of the circle. You also used [0.0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0] for keyTimes. The first and last elements of the array are obvious: 0 means start and 1.0 means end so to get the middle value you want to calculate when the circle stroke is complete and the second part (shrinking) will happen. Dividing kAnimationDurationDelay by kAnimationDuration gets you to the exact percentage but because it’s a delayed animation, you subtract it from 1.0 because you want to go back by the duration of the delay from when the animation ends.

You’ve now checked off the circleLayer and the lineLayer animations, so it’s time to move on to the square in the center.

Animating the Square

The drill should be getting familiar by now. Go to startAnimating() and comment out the animation method calls except for animateSquareLayer(). In addition, change init(frame:) so it looks like this:

override init(frame: CGRect) {
  super.init(frame: frame)
 
  circleLayer = generateCircleLayer()
  lineLayer = generateLineLayer()
  squareLayer = generateSquareLayer()
  maskLayer = generateMaskLayer()
 
//  layer.mask = maskLayer
  layer.addSublayer(circleLayer)
//  layer.addSublayer(lineLayer)
  layer.addSublayer(squareLayer)
}

Once done, head over to animateSquareLayer() and get cracking on the next set of animations:

  // bounds
  let b1 = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: 2.0/3.0 * squareLayerLength, height: 2.0/3.0  * squareLayerLength))
  let b2 = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: squareLayerLength, height: squareLayerLength))
  let b3 = NSValue(CGRect: CGRectZero)
 
  let boundsAnimation = CAKeyframeAnimation(keyPath: "bounds")
  boundsAnimation.values = [b1, b2, b3]
  boundsAnimation.timingFunctions = [fadeInSquareTimingFunction, squareLayerTimingFunction]
  boundsAnimation.duration = kAnimationDuration
  boundsAnimation.keyTimes = [0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0]

This particular animation changes the CALayer‘s bounds. A keyframe animation is created that goes from two-thirds the length, to the full length, then to zero.

Next, animate the background color:

  // backgroundColor
  let backgroundColorAnimation = CABasicAnimation(keyPath: "backgroundColor")
  backgroundColorAnimation.fromValue = UIColor.whiteColor().CGColor
  backgroundColorAnimation.toValue = UIColor.fuberBlue().CGColor
  backgroundColorAnimation.timingFunction = squareLayerTimingFunction
  backgroundColorAnimation.fillMode = kCAFillModeBoth
  backgroundColorAnimation.beginTime = kAnimationDurationDelay * 2.0 / kAnimationDuration
  backgroundColorAnimation.duration = kAnimationDuration / (kAnimationDuration - kAnimationDurationDelay)

Take note of the fillMode property. Since the beginTime is non-zero, the animation will clamp both the starting and ending CGColors to the animation. This results in no flickering from the animations when added to the parent CAAnimationGroup.

Speaking of which, it’s time to implement that:

  // Group
  let groupAnimation = CAAnimationGroup()
  groupAnimation.animations = [boundsAnimation, backgroundColorAnimation]
  groupAnimation.repeatCount = Float.infinity
  groupAnimation.duration = kAnimationDuration
  groupAnimation.removedOnCompletion = false
  groupAnimation.beginTime = beginTime
  groupAnimation.timeOffset = startTimeOffset
  squareLayer.addAnimation(groupAnimation, forKey: "looping")

Build and run to check your progress. You’ve now taken care of the squareLayer.

Splash Screen Tutorial
Time to combine the animations and see this animation come together!

Note: Remember that animations on the simulator could be a bit jagged since your computer is emulating work typically done on the GPU of your iOS device. If your computer can’t keep up with the animations, try switching to a simulator with a smaller screen or develop on an actual iOS device.

The Mask

First, uncomment all the layers being added in init(frame:) and uncomment all the animations in startAnimating().

With all the animations put together, build and run Fuber.

PreMask Animation

Still looks a bit off, doesn’t it? There’s a sudden jump in the bounds when the circleLayer collapses in size. Fortunately, the mask animation will fix that, shrinking the sublayers all in one smooth go.

Go to animateMaskLayer() and add the following:

  // bounds
  let boundsAnimation = CABasicAnimation(keyPath: "bounds")
  boundsAnimation.fromValue = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: radius * 2.0, height: radius * 2))
  boundsAnimation.toValue = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: 2.0/3.0 * squareLayerLength, height: 2.0/3.0 * squareLayerLength))
  boundsAnimation.duration = kAnimationDurationDelay
  boundsAnimation.beginTime = kAnimationDuration - kAnimationDurationDelay
  boundsAnimation.timingFunction = circleLayerTimingFunction

This is the animation for the bounds. Remember that when the bounds change, the whole AnimatedULogoView will disappear since this layer is the mask that’s applied to all the sublayers.

Now implement a corner radius animation to keep the mask circular:

  // cornerRadius
  let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius")
  cornerRadiusAnimation.beginTime = kAnimationDuration - kAnimationDurationDelay
  cornerRadiusAnimation.duration = kAnimationDurationDelay
  cornerRadiusAnimation.fromValue = radius
  cornerRadiusAnimation.toValue = 2
  cornerRadiusAnimation.timingFunction = circleLayerTimingFunction

Add these two animations to a CAAnimationGroup to complete this layer:

  // Group
  let groupAnimation = CAAnimationGroup()
  groupAnimation.removedOnCompletion = false
  groupAnimation.fillMode = kCAFillModeBoth
  groupAnimation.beginTime = beginTime
  groupAnimation.repeatCount = Float.infinity
  groupAnimation.duration = kAnimationDuration
  groupAnimation.animations = [boundsAnimation, cornerRadiusAnimation]
  groupAnimation.timeOffset = startTimeOffset
  maskLayer.addAnimation(groupAnimation, forKey: "looping")

Build and run.

RiderIconView Animation

Looking good!

The Grid

A digital frontier. Try to picture clusters of UIViews as they move through the TileGridView instance. What do they look like? Well … time to stop making references to Tron and take a look!

The background grid consists of a series of TileViews all attached to the parent TileGridView class. To get a quick visual understanding of this, open up TileView.swift and find init(frame:). Add the following to the bottom of this method:

layer.borderWidth = 2.0

Build and run the application.

Fuber-Grid-View

As you can see, the TileViews are arranged so that they’re stacked together in a grid. The creation of all this logic happens in a method called renderTileViews() in TileGridView.swift. Fortunately, the logic is already created on your behalf for this grid layout. All you need to do is animate it!

Animating the TileView

TileGridView has a single direct child subview called containerView. It adds all the child TileViews. In addition, there is a property called tileViewRows, which is a two-dimensional array containing all the TileViews added to the container view.

Navigate back to TileView‘s init(frame:). Remove the line you added to show the border width and enable the commented-out line that adds the chimeSplashImage to the layer’s contents. The method should now look like this:

override init(frame: CGRect) {
  super.init(frame: frame)
  layer.contents = TileView.chimesSplashImage.CGImage
  layer.shouldRasterize = true
}

Build and run.

Grid Starting

Coooooool … We’re getting there!

However, TileGridView (and all of its TileViews) needs some animation. Open up TileView.swift, go to startAnimatingWithDuration(_:beginTime:rippleDelay:rippleOffset:) and plop down the next chunk of animations:

  let timingFunction = CAMediaTimingFunction(controlPoints: 0.25, 0, 0.2, 1)
  let linearFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
  let easeOutFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
  let easeInOutTimingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
  let zeroPointValue = NSValue(CGPoint: CGPointZero)
 
  var animations = [CAAnimation]()

This code sets up a series of timing functions you’ll use right now. Add this code:

  if shouldEnableRipple {
    // Transform.scale
    let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
    scaleAnimation.values = [1, 1, 1.05, 1, 1]
    scaleAnimation.keyTimes = TileView.rippleAnimationKeyTimes
    scaleAnimation.timingFunctions = [linearFunction, timingFunction, timingFunction, linearFunction]
    scaleAnimation.beginTime = 0.0
    scaleAnimation.duration = duration
    animations.append(scaleAnimation)
 
    // Position
    let positionAnimation = CAKeyframeAnimation(keyPath: "position")
    positionAnimation.duration = duration
    positionAnimation.timingFunctions = [linearFunction, timingFunction, timingFunction, linearFunction]
    positionAnimation.keyTimes = TileView.rippleAnimationKeyTimes
    positionAnimation.values = [zeroPointValue, zeroPointValue, NSValue(CGPoint:rippleOffset), zeroPointValue, zeroPointValue]
    positionAnimation.additive = true
 
    animations.append(positionAnimation)
  }

shouldEnableRipple is a boolean that controls when the transform and position animations are added to the animations array you just created. Its value is set to true for all the TileViews that are not on the perimeter of the TileGridView. This logic is already done for you when the TileViews are created in the renderTileViews() method of TileGridView.

Add an opacity animation:

  // Opacity
  let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity")
  opacityAnimation.duration = duration
  opacityAnimation.timingFunctions = [easeInOutTimingFunction, timingFunction, timingFunction, easeOutFunction, linearFunction]
  opacityAnimation.keyTimes = [0.0, 0.61, 0.7, 0.767, 0.95, 1.0]
  opacityAnimation.values = [0.0, 1.0, 0.45, 0.6, 0.0, 0.0]
  animations.append(opacityAnimation)

This is a pretty self-explanatory animation with some very specific keyTimes.

Now add all these animations to a group:

  // Group
  let groupAnimation = CAAnimationGroup()
  groupAnimation.repeatCount = Float.infinity
  groupAnimation.fillMode = kCAFillModeBackwards
  groupAnimation.duration = duration
  groupAnimation.beginTime = beginTime + rippleDelay
  groupAnimation.removedOnCompletion = false
  groupAnimation.animations = animations
  groupAnimation.timeOffset = kAnimationTimeOffset
 
  layer.addAnimation(groupAnimation, forKey: "ripple")

This will add groupAnimation to the instance of TileView. Note that the group animation could either have one or three animations in the group, depending on the value of shouldEnableRipple.

Now that you’ve written the method to animate each TileView, it’s time to call it from TileGridView. Head over to TileGridView.swift and add the following code to startAnimatingWithBeginTime(_:):

private func startAnimatingWithBeginTime(beginTime: NSTimeInterval) {
  for tileRows in tileViewRows {
    for view in tileRows {
      view.startAnimatingWithDuration(kAnimationDuration, beginTime: beginTime, rippleDelay: 0, rippleOffset: CGPointZero)
    }
  }
}

Build and run.

Grid-1

Hmm … this is definitely looking better, but the radial expansion of AnimatedULogoView should send out a shockwave-like effect to all the TileViews in the grid. That means a delay offset needs to be created based upon the distance from the center of the view to the outlining view, multiplied by a constant.

Right underneath startAnimatingWithBeginTime(_:), add the following new method:

private func distanceFromCenterViewWithView(view: UIView)->CGFloat {
  guard let centerTileView = centerTileView else { return 0.0 }
 
  let normalizedX = (view.center.x - centerTileView.center.x)
  let normalizedY = (view.center.y - centerTileView.center.y)
  return sqrt(normalizedX * normalizedX + normalizedY * normalizedY)
}

This simply gets the distance from the center of the centerTileView to the center of the view passed in.

Head back to startAnimatingWithBeginTime(_:) and replace its contents with the following:

  for tileRows in tileViewRows {
    for view in tileRows {
      let distance = self.distanceFromCenterViewWithView(view)
 
      view.startAnimatingWithDuration(kAnimationDuration, beginTime: beginTime, rippleDelay: kRippleDelayMultiplier * NSTimeInterval(distance), rippleOffset: CGPointZero)
    }
  }

This uses the distanceFromCenterViewWithView(_:) method you’ve just added to determine the delay the animation should have.

Build and run.

Grid-2

Much better! This animation is starting to look respectable now, but there’s still something missing. The TileViews should physically move based on the direction and magnitude of the shockwave.

The best way to do this is to whip out your high-school math (don’t cringe—it will be over before you know it) and normalize the vector based upon the distance of the TileView from the center.

Add another method below distanceFromCenterViewWithView(_:):

private func normalizedVectorFromCenterViewToView(view: UIView)->CGPoint {
  let length = self.distanceFromCenterViewWithView(view)
  guard let centerTileView = centerTileView where length != 0 else { return CGPointZero }
 
  let deltaX = view.center.x - centerTileView.center.x
  let deltaY = view.center.y - centerTileView.center.y
  return CGPoint(x: deltaX / length, y: deltaY / length)
}

Go back to startAnimatingWithBeginTime(_:) and modify the code to look like this:

private func startAnimatingWithBeginTime(beginTime: NSTimeInterval) {
  for tileRows in tileViewRows {
    for view in tileRows {
 
      let distance = self.distanceFromCenterViewWithView(view)
      var vector = self.normalizedVectorFromCenterViewToView(view)
 
      vector = CGPoint(x: vector.x * kRippleMagnitudeMultiplier * distance, y: vector.y * kRippleMagnitudeMultiplier * distance)
 
      view.startAnimatingWithDuration(kAnimationDuration, beginTime: beginTime, rippleDelay: kRippleDelayMultiplier * NSTimeInterval(distance), rippleOffset: vector)
    }
  }
}

This calculates the vector that the TileView should move by and applies it to the rippleOffset.

Build and run.

Grid-3

Very cool! And now for the cherry on top: to get that “zooming-in” feel, a scale animation needs to occur right before there is a change in the mask’s bounds.

At the top of startAnimatingWithBeginTime(_:), add the following code:

  let linearTimingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
 
  let keyframe = CAKeyframeAnimation(keyPath: "transform.scale")
  keyframe.timingFunctions = [linearTimingFunction, CAMediaTimingFunction(controlPoints: 0.6, 0.0, 0.15, 1.0), linearTimingFunction]
  keyframe.repeatCount = Float.infinity;
  keyframe.duration = kAnimationDuration
  keyframe.removedOnCompletion = false
  keyframe.keyTimes = [0.0, 0.45, 0.887, 1.0]
  keyframe.values = [0.75, 0.75, 1.0, 1.0]
  keyframe.beginTime = beginTime
  keyframe.timeOffset = kAnimationTimeOffset
 
  containerView.layer.addAnimation(keyframe, forKey: "scale")

Build and run again.

FuberFinal

Beautiful! You have now created a production-quality animation that many Fuber users will complain about on Twitter. Great job! :]

Note: Try changing the values of kRippleMagnitudeMultiplier and kRippleDelayMultiplier to see some amusing results.

To finish things off, go to RootContainerViewController.swift. In viewDidLoad(), change the final line of code from showSplashViewControllerNoPing() to showSplashViewController().

Do a final build and run to check out your work.

Fuber Animation

Give yourself a pat on the back … That is one cool splash screen!

Where to Go From Here?

You can download the final Fuber project here.

If you would like to learn more about animations, check out iOS Animations by Tutorials.

Have an animation question? Want to post an amusing photo you’ve added to TileView’s contents? Feel free to join the forum discussion below!

The post How To Create an Uber Splash Screen appeared first on Ray Wenderlich.

iOS 10 Screencast: Property Animators: Working Together

iOS 10 Screencast: Measurements & Units

Making RocketFist – An Interview with Unity Dev Daniel Nascimento

$
0
0

Daniel Nascimento

Making games is hard. Making games on your own is even harder, but being successful at it is reserved for the best of the best.

In this interview, we hear from one of the best of the best: Unity developer Daniel Nascimento, who recently released his game Rocket Fist on Steam.

We chat about the process of creating games in Unity, bringing them to market, and whether this indie apocalypse is really a “thing” or not.

Getting Started

What brought you into game development?

I studied 3D Animation at the Vancouver Film School, but not really for games: more for TV and movies. I was having a hard time finding a job as an animator, and at the time I was playing a lot of a game called Dungeon Defenders, which had a level editor based on Unreal Development Kit.

I started using what I have learned about 3D Art in school to play around with it and make my own levels; I also started ripping characters from the game and posing them in funny scenes and posting it in their forums. That ended up leading me to being hired to animate a trailer for their new game.

That opened a whole new world to me, it didn’t even occur to me before that working with games was something I could do. I bought a Unreal Development Kit book and started learning how to make games with it.

What is your game development experience?

I made many games, most of them really small projects made for game jams in under 72 hours. The most famous of them was Go Home You’re Drunk which was played by a lot of the biggest YouTubers and was made in a 15-hour jam with 2 friends.

I also did a Masters of Digital media in which we worked in a couple of game projects and worked in a couple of virtual reality companies later on as a Technical Artist.

How did Rocket Fist come about?

I wanted to make something more complete with a longer development cycle.

I was pursuing a master’s degree in Digital Media, and in one of the projects we were working on, we were tasked with coming up with games for the PS Vita that made good use of it’s physical interface. I was working on a lot of small prototypes in the Vita and had the idea of making a 1 vs. 1 game in which each player held one side of the device.

And that was the first ever version of Rocket Fist. It was just some spheres in an environment made of cubes throwing little cube missiles at one another, and even at that stage it was already pretty damn fun.

Each player only had one button and one analog stick to play with; it was simple and addicting. I started playtesting with my classmates and it was a success. Everyone I introduced this to would play for a long time and would have a hard time giving it back. At that point I was sure that was THE ONE, the project I should pursue to be a long-term commercial project.

About Unity

Daniel Nascimento

Daniel Nascimento

When producing Rocket Fist, why did you choose Unity over other game engines?

When I was starting, I learned Unreal Development Kit for a while, which was Unreal Engine 3, but it was a really awkward engine to use, with lots of annoying problems.

In the first game jam I participated, I worked with a programmer that was using Unity and fell in love with the engine. From there on I started learning C# and using unity and haven’t looked back since.

What Unity features do you really enjoy?

I love how easy it is to create stuff in it, even to change the editor itself and create tools to help in the development! I also love the component-based nature of it.

Conversely, how do you think Unity can be improved?

It really needs a better way to version control scenes, those are always a problem when working with others. Also wish they would focus more on stability and performance for a while instead of just trying to cram more features into it.

How has the experience been for developing multiplatform games on Unity?

It was great! I thought it would be much harder to get the game working in other platforms, but for most of them it was mainly pressing a button and changing one method or another. Conditional compilation also really helped with that; so far I’ve been keeping the same project for all platforms (except for Universal Windows App, which is very picky about a bunch of things).

Did you use Unity’s multiplayer tools, or did you create your own? What was your reasoning and how has it turned out?

I tried many multiplayer tools. Unity’s was too laggy for Rocket Fist, since it’s a very fast-paced game.

I ended up opting to go a P2P route with players hosting their own servers. I built my own framework on top of Steamworks Multiplayer using TNet as a base, but in the end it wasn’t working that well, with inconsistent results. I’m now in the middle of implementing Forge Networking instead, which gave me way better results in my tests.

In general terms, was it difficult to integrate Steam into the game?

It was fairly easy; I used Riley’s great Steamworks.Net and that made the whole process painless and quick.

Indie Game Development

There’s been a lot of talk about the great Indie apocalypse. How do you see the current landscape for the indie developer?

There is a lot of competition that’s for sure. It’s not an easy path. Personally, I make games because I really enjoy making games. So no matter what, I’ll keep making my own games on my free time, even if I have to work a day job alongside with it.

What do you consider the hard aspects of game development?

Online Multiplayer. Still have PTSD from working on it.

Rocket Fist went straight to market in a completed state. What’s your thoughts on programs like early access in terms of being an indie development?

I figured that you really only have one chance at making your initial splash, and I wanted that initial splash to happen when the game was really ready for it. So far I’ve been receiving great feedback and reviews, I think it was a good choice.

I was also concerned with support requests and the added work that comes with it, but looking back, it isn’t as much trouble as I anticipated. I might try some sort of early access for my next project, maybe Itch.io’s refinery.

Rocket Fist also features a flat cost versus being filled with microtransactions or being free to play. Why did you choose the flat price and what advice do you have for an indie developer when determining a price?

I just hate microtransactions very very much, which is one of the reasons I tend to stay away from mobile game development. I don’t really have any good advice about determining a price; what I ended up doing was asking a bunch of people about their opinions and trying to figure it out from it.

Also gotta keep in mind that a lot of people only want to buy games now when there’s a sale, so think that most of your sales will happen when the game is being sold at a smaller price than the one you picked.

What advice do you have for indie game developers?

Playtest a lot, be open to feedback and suggestion, keep on learning, keep on making stuff, have fun with it!

Where to Go From Here?

Many thanks to Daniel for his insights as an indie developer.

There’s a lot consider when developing games, as well as things to consider even after your game goes live. Indie development is hard, but as Teddy Roosevelt once said, “Nothing in the world is worth having or worth doing unless it means effort, pain, difficulty”. I look forward at presenting more stories of Unity pain in the future. :]

If you enjoyed this interview and you are curious about Rocket Fist, definitely check out Rocket Fist over at Steam. With great reviews and a price to match, it’s definitely a game worth trying.

The post Making RocketFist – An Interview with Unity Dev Daniel Nascimento appeared first on Ray Wenderlich.

SceneKit and SpriteKit in iOS 10 – Podcast S06 E05

$
0
0
scenekit

Hear all about the changes to SceneKit and SpriteKit coming in iOS 10!

Join Mic, Jake, and Chris as they discuss the big improvements and key new features coming to SceneKit and SpriteKit in iOS 10. If physically based rendering, image based lighting, or tile maps float your boat then this is definitely the episode for you!

[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 SceneKit and SpriteKit in iOS 10 – Podcast S06 E05 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>