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

Video Tutorial: MapKit and Core Location Part 3: Geocoding


Android RecyclerView Tutorial

$
0
0

RecyclerView-feature
Recycling is one of those things that is good for the planet, and it’s an common sense way to make sure we don’t find ourselves buried in our own rubbish or without sufficient resources in the future.

A few Android engineers thought about the benefits of recycling and realized that an OS can also run more efficiently if it recycles. The result of this inspiration was millions of eco-Warriors and recycling enthusiasts rejoicing when the RecyclerView widget was introduced into Android Lollipop — or something like that. :]

There was even more celebration when Google announced a support library to make this clean, green, recycling machine backwards compatible all the way to Android Eclair (2.2), which was released back in 2010!

In this tutorial, you’re going to experience the power of RecyclerView in action and learn:

  • The purpose of a RecyclerView
  • The components that make up a RecyclerView
  • How to change the layout of a RecyclerView
  • How to add some nice animations to your RecyclerView

You’re also going to blast off into outer space with the sample app Galacticon. You’ll use it to build out a feed of daily astronomy photos from a public NASA API.

Prerequisites: You should have a working knowledge of developing for Android before working through this tutorial. If you need a refresher, take a look at some of our introductory tutorials!

Heading to Cape Canaveral: Getting Started

Download the starter project and open it up in Android Studio. There isn’t much to it yet, nor is the almighty RecyclerView anywhere to be seen.

Click the Run app button at the top and you’ll see something that resembles outer space in all the wrong ways:

1. Empty App

It’s empty, but that’s ok. You wouldn’t learn much if all the work was done for you! Before you can add that amazing astrophotography from NASA, you’ll need to do some set up work.

Obtaining The (API) Keys to the Shuttle

You’ll use the Astronomy Picture of the Day API, one of the most popular web services provided by NASA. To ensure it doesn’t fall victim to unsolicited traffic, the service requires you to have an API key to use it in an application.

Fortunately, getting a key is as simple as putting your name and email address into api.nasa.gov and copying the API key that appears on the screen or in the email you receive a few moments later:

3. API Key Email

Once you’ve acquired your API key, copy it and open the strings.xml file in your project. Paste your API key into the api_key string resource, replacing INSERT API KEY HERE:

4. API_KEY paste

Space Oddity: Learning About RecyclerView

You’re about to blast off into outer space to explore the vastness of RecyclerViews, but no competent commander heads into the unknown without preparation. You have questions, and you need answers before you go any further. Consider this section as your mission brief.

A RecyclerView can be thought of as a combination of a ListView and a GridView. However, there are extra features that separate your code into maintainable components even as they enforce memory-efficient design patterns.

But how could it be better than the tried and tested ListView and GridView you’re used to? Could it be some kind of alien technology? The answers, as always, are in the details.

Why You Need RecyclerView

Imagine you’re creating a ListView where the custom items you want to show are quite complicated. You take time to lovingly create a row layout for these items, and then use that layout inside your adapter.

Inside your getView() method, you inflate your new item layout. You then reference every view within by using the unique ids you provided in your XML to customize and add some view logic. Once finished, you pass that view to the ListView, ready to be drawn on the screen. All is well…or is it?

The truth is that ListViews and GridViews only do half the job of achieving true memory efficiency. They recycle the item layout, but don’t keep references to the layout children, forcing you to call findViewById() for every child of your item layout every time you call getView().

All this calling around can become very processor-intensive, especially for complicated layouts. Furthermore, the situation can cause your ListView scrolling to become jerky or non-responsive as it frantically tries to grab references to the views you need.

ListView-

Android engineers initially provided a solution to this problem on the Android Developers with smooth scrolling, via the power of the View Holder pattern.

When you use this pattern, you create a class that becomes an in-memory reference to all the views needed to fill your layout. The benefit is you set the references once and reuse them, effectively working around the performance hit that comes with repeatedly calling findViewById().

viewholder_new_larger

The problem is that it’s an optional pattern for a ListView or GridView. If you’re unaware of this detail, then you may wonder why your precious ListViews and GridViews are so slow.

First Contact: RecyclerView and Layouts

The arrival of the RecyclerView changed everything. It still uses an Adapter to act as a data source; however, you have to create ViewHolders to keep references in memory.

When you need a new view, it either creates a new ViewHolder object to inflate the layout and hold those references, or it recycles one from the existing stack.

Now you know why it’s called a RecyclerView!

Another perk of using RecyclerViews is that they come with default animations that you don’t have to create or add yourself — they just work.

Thanks to the requirement for a ViewHolder, the RecyclerView knows exactly which animation to apply to which item. Best of all, it just does it as required. You can even create your own animations and apply them as needed.

The last and most interesting component of a RecyclerView is its LayoutManager. This object positions the RecyclerView’s items and tells it when to recycle items that have transitioned off-screen.

Layout Managers come in three default flavors:

  • LinearLayoutManager positions your items to look like a standard ListView
  • GridLayoutManager positions your items in a grid format similar to a GridView
  • StaggerGridLayoutManager positions your items in a staggered grid format.

You can also create your own LayoutManagers to use with a RecyclerView if you want an extra bit of customization.

Hopefully that answers all your questions, commander. Now, onto the mission!

Preparing for Launch: Creating the RecyclerView

To create the RecyclerView, you’ll break the work into four parts:

  1. Declare the RecyclerView in an activity layout and reference it in your activity Java file.
  2. Create a custom item XML layout for your RecyclerView to use for its items.
  3. Create the view holder for your view items, hook up the data source of the RecyclerView and handle the view logic by creating a RecyclerView Adapter.
  4. Attach the adapter to the RecyclerView.

Step one should be familiar. Open up the activity_main.xml layout file, and add the following as a child of the LinearLayout:

<android.support.v7.widget.RecyclerView
  android:id="@+id/recyclerView"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:scrollbars="vertical"/>

Here you’re setting up the layout and telling the RecyclerView to match its parent.

Note: You’re using the v7 support library for backwards compatibility with older devices. The sample project already adds the RecyclerView Support Library as a dependency in your app’s build.gradle file. If you want more information on how to do it yourself, check out the Android developer website.

Open MainActivity.java and declare the following member variables at the top of the class:

private RecyclerView mRecyclerView;
private LinearLayoutManager mLinearLayoutManager;

In onCreate(), add the following lines after setContentView:

mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mLinearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLinearLayoutManager);

Phase one of ignition is complete! You’ve declared and allocated memory for two parts of the puzzle that RecyclerViews need to work: The RecyclerView and its Layout Manager.

Ignition Phase 2: Laying out the RecyclerView Items

Phase two of ignition involves creating a custom layout for the item you want your RecyclerView to use. It works exactly the same as it does when you create a custom layout for a ListView or Gridview.

Head over to your layout folder and create a new layout with the name recyclerview_item_row and set the root element as a LinearLayout. In your new layout, add the following XML elements as children of your LinearLayout:

<ImageView
  android:id="@+id/item_image"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_gravity="center"
  android:layout_marginTop="8dp"
  android:adjustViewBounds="true"
  android:layout_weight="3"/>
<TextView
  android:id="@+id/item_date"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_marginTop="8dp"
  android:layout_gravity="top|start"
  android:layout_weight="1"
  android:text="Some date"/>
<TextView
  android:id="@+id/item_description"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_gravity="center|start"
  android:layout_weight="1"
  android:ellipsize="end"
  android:maxLines="5"/>

No rocket science here: You declared a few views as children of your layout, and can now use them in your adapter.

Adapters: Rocket Fuel for Your RecyclerView

Right-click on the com.raywenderlich.galacticon folder, select New \ Java Class, and name it RecyclerAdapter. At the top of the file below the package declaration, import the support library’s version of RecyclerView:

import android.support.v7.widget.RecyclerView;

Make the class extend RecyclerView.Adapter so it looks like the following:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.PhotoHolder> {
}

Android Studio will detect that you’re extending a class that has required methods and will underline your class declaration with a red squiggle.

To resolve this, click on the line of code to insert your cursor, then press alt + return (or option + return) to bring up a context menu. Select Implement Methods:

5. Implements RecyclerView adapter Methods

Confirm you want to implement the suggested methods by clicking OK:

6. Confirm RecyclerView Implementtion Methods

These methods are the driving force behind your RecyclerView adapter. Note how there is still a compiler error for the moment– this is because your adapter and the required methods are actually defined using your ViewHolder class, PhotoHolder, which doesn’t exist just yet. You’ll get to define your ViewHolder and see what each required method does shortly, so just hang tight, Commander!

As with every adapter, you need to provide the corresponding view a means of populating items and deciding how many items there should be.

Item clicks were previously managed by a ListView’s or GridView’s onItemClickListener. A RecyclerView doesn’t provide methods like this because it has one focus: ensuring the items inside are positioned properly and managed efficiently.

The job of listening for actions is now the responsibility of the RecyclerView item and its children. This may seem like more overhead, but in return, you get fine-grained control over how your item’s children can act.

At the top of your RecyclerAdapter class, add a variable to hold your photos:

private ArrayList<Photo> mPhotos;

Next, add a constructor to set it up:

public RecyclerAdapter(ArrayList<Photo> photos) {
  mPhotos = photos;
}

Nice job, Commander! Your adapter now knows where to look for data. Soon you’ll have an ArrayList of photos filled with the finest astrophotography!

Next, you’ll populate the stubbed methods that were added by Android Studio.

The first method, getItemCount(), is pretty simple and should be familiar from your work with ListViews or GridViews.

The adapter will work out how many items to display. In this case, you want the adapter to show every photo you’ve downloaded from NASA’s API. To do that, add the following line between the method braces:

return mPhotos.size();

Next, you’re going to exploit the ViewHolder pattern to make an object that holds all your view references.

Velcro For All: Keeping Hold Of Your Views

To create a PhotoHolder for your view references, you’ll create a static inner class for your adapter. You’ll add it here rather than in a separate class because its behavior is tightly coupled with the adapter.

It’s a static class, so regardless of how many instances you create, its values are shared amongst all of them — it’s pretty handy for holding references.

Add the following code underneath your adapter class member variables, but before any of the methods:

//1
public static class PhotoHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
  //2
  private ImageView mItemImage;
  private TextView mItemDate;
  private TextView mItemDescription;
  private Photo mPhoto;
 
  //3
  private static final String PHOTO_KEY = "PHOTO";
 
  //4
  public PhotoHolder(View v) {
    super(v);
 
    mItemImage = (ImageView) v.findViewById(R.id.item_image);
    mItemDate = (TextView) v.findViewById(R.id.item_date);
    mItemDescription = (TextView) v.findViewById(R.id.item_description);
    v.setOnClickListener(this);
  }
 
  //5
  @Override
  public void onClick(View v) {
    Log.d("RecyclerView", "CLICK!");
  }
}

So what did you do here?

  1. Made the class extend RecyclerView.ViewHolder, allowing it to be used as a ViewHolder for the adapter.
  2. Added a list of references to the lifecycle of the object to allow the ViewHolder to hang on to your ImageView and TextView, so it doesn’t have to repeatedly query the same information.
  3. Added a key for easier reference to the particular item being used to launch your RecyclerView.
  4. Set up a constructor to handle grabbing references to various subviews of the photo layout.
  5. Implemented the required method for View.OnClickListener since ViewHolders are responsible for their own event handling.

You should now be able to build and run the app again, but it’ll look about the same because you haven’t told the RecyclerView how to associate the PhotoHolder with a view.

Assembling The Pieces

Sometimes there are no ViewHolders available. In this scenario, RecylerView will ask onCreateViewHolder() from RecyclerAdapter to make a new one.

You’ll use the item layout — PhotoHolder — to create a view for the ViewHolder.

Replace the return null line between the curly braces with the following:

View inflatedView = LayoutInflater.from(parent.getContext())
    .inflate(R.layout.recyclerview_item_row, parent, false);
return new PhotoHolder(inflatedView);

Here you add the suggested LayoutInflater import. Then you inflate the view from its layout and pass it in to a PhotoHolder.

And with that, you’ve made it so the object holds onto those references while it’s recycled, but there are still more pieces to put together before you can launch your rocketship.

Start a new activity by replacing the log in ViewHolder’s onClick with this code:

Context context = itemView.getContext();
Intent showPhotoIntent = new Intent(context, PhotoActivity.class);
showPhotoIntent.putExtra(PHOTO_KEY, mPhoto);
context.startActivity(showPhotoIntent);

This grabs the current context of your item view and creates an intent to show a new activity on the screen, passing the photo object you want to show. Passing the context object into the intent allows the app to know what activity it is leaving.

Next thing to do is to add this method inside PhotoHolder:

public void bindPhoto(Photo photo) {
  mPhoto = photo;
  Picasso.with(mItemImage.getContext()).load(photo.getUrl()).into(mItemImage);
  mItemDate.setText(photo.getHumanDate());
  mItemDescription.setText(photo.getExplanation());
}

This binds the photo to the PhotoHolder, giving your item the data it needs to work out what it should show.

It also adds the suggested Picasso import, which is a library that makes it significantly simpler to get images from a given URL.

The last piece of the PhotoHolder assembly will tell it how to show the right photo at the right moment. It’s the RecyclerAdapter’s onBindViewHolder, and it lets you know a new item will be available on screen and the holder needs some data.

Add the following code inside the onBindViewHolder() method:

Photo itemPhoto = mPhotos.get(position);
holder.bindPhoto(itemPhoto);

Here you’re passing in a copy of your ViewHolder and the position where the item will show in your RecyclerView.

And that’s all you needed to do here on the assembly — just use the position where your ViewHolder will appear to grab the photo out of your list, and then pass it to your ViewHolder.

Step three of your ignition check protocol is complete!

Countdown And Liftoff: Hooking up the Adapter And RecyclerView

This is the moment you’ve been waiting for, the final stage before blast off! All you need to do is hook your adapter up to your RecyclerView and make sure it retrieves photos when it’s created so you can explore space — in pictures.

Open MainActivity.java, and add this variable at the top:

private RecyclerAdapter mAdapter;

Next, underneath the creation of your array list in onCreate() add the following:

mAdapter = new RecyclerAdapter(mPhotosList);
mRecyclerView.setAdapter(mAdapter);

Here you’re creating the adapter, passing in the constructors it needs and setting it as the adapter for your RecyclerView.

Although the adapter is connected, there’s one more thing to do to make sure you don’t have an empty screen.

In onStart(), underneath the call to super, add this code:

if (mPhotosList.size() == 0) {
  requestPhoto();
}

This adds a check to see if your list is empty, and if yes, it requests a photo.

Please sir, may I have more stars?

Next, in receivedNewPhoto(), update the method so it looks like the following:

@Override
public void receivedNewPhoto(final Photo newPhoto) {
 
  runOnUiThread(new Runnable() {
    @Override
    public void run() {
      mPhotosList.add(newPhoto);
      mAdapter.notifyItemInserted(mPhotosList.size());
    }
  });
}

Here you are informing the recycler adapter that an item was added after the list of photos was updated.

Now you’re ready to commence the ignition sequence, er…I mean run the app.

Run the app, load up the emulator and before long, Galacticon should look something like this:

7. RecyclerView Working

That’s not all. Tap on the photo, and you should be greeted with a new activity that brings that item into focus:

8. Focus Activity

But that’s still not all! Try rotating your device or emulator (function + control + F11/F12) and you’ll see the image in full screen glory!

9. Landscape focus

Depending on the size of the image and your device screen it may look a little distorted, but don’t worry about that.

Congratulations! You have a working RecyclerView and can take your journey amongst the stars.

Taking A Spacewalk: Adding Scrolling support

If you head back to MainActivity on your device and try to scroll down, you’ll notice something is amiss — your RecyclerView isn’t retrieving any new photos.

10. Scrolling Not Working

Your RecyclerView is doing exactly as it’s told by showing the contents of mPhotosList. The problem is that the app will only retrieve one photo when you load the app. It has no idea when or how to grab more photos.

So next, you’ll retrieve the number of the photos and the last visible photo index while scrolling. Then you’ll check to see if the last photo is visible and if there are no photos already on request. If these are both true, then your app goes away and downloads more pretty photos!

This patch will require a spacewalk, so break out your spacesuit and get ready for a zero gravity experience.

In MainActivity.java, add this method below onStart:

private int getLastVisibleItemPosition() {
  return mLinearLayoutManager.findLastVisibleItemPosition();
}

This uses your RecyclerView’s LinearLayoutManager to get the index of the last visible item on the screen.

Next, you add a method that inserts an onScrollListener to your RecyclerView, so it can get a callback when the user scrolls:

private void setRecyclerViewScrollListener() {
  mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
      super.onScrollStateChanged(recyclerView, newState);
      int totalItemCount = mRecyclerView.getLayoutManager().getItemCount();
      if (!mImageRequester.isLoadingData() && totalItemCount == getLastVisibleItemPosition() + 1) {
        requestPhoto();
      }
    }
  });
}

Now the RecyclerView has a scroll listener attached to it that is triggered by scrolling. During scrolling, the listener retrieves the count of the items in its LayoutManager and calculates the last visible photo index. Once done, it compares these numbers (incrementing the index by 1 because the index begins at 0 while the count begins at 1). If they match and there are no photos already on request, then you request a new photo.

Finally, hook everything to the RecyclerView by calling this method from onCreate, just beneath where you set your RecyclerView Adapter:

setRecyclerViewScrollListener();

Hop back in the ship (build and run the app again). Scroll down and you should see quite an improvement!

11. Scrolling Update

Excellent work, your RecyclerView now updates to show the latest photo requested by your app. The great thing is that receivedNewPhoto() handles most of the work because you told it to notify your adapter about new items.

That earns an intergalactic thumbs up for upcycling code!

Layout Changes

Now that your RecyclerView is up and running, it’s time to trick out your spaceship.

Wouldn’t it be cool if your RecyclerView could change its layout? Good news: RecyclerView’s item positioning is separated into a layout manager.

Add a variable for a GridLayoutManager to the top of MainActivity.java:

private GridLayoutManager mGridLayoutManager;

Note that this is a default LayoutManager, but it could just as easily be custom.

In onCreate(), initialize the LayoutManager below the existing Linear Layout Manager:

mGridLayoutManager = new GridLayoutManager(this, 2);

Just like you did with the previous LayoutManager, you pass in the context the manager will appear in, but unlike the former, it takes an integer parameter. In this case, you’re setting the number of columns the grid will have.

Add this method to MainActivity:

private void changeLayoutManager() {
  if (mRecyclerView.getLayoutManager().equals(mLinearLayoutManager)) {
    //1
    mRecyclerView.setLayoutManager(mGridLayoutManager);
    //2
    if (mPhotosList.size() == 1) {
      requestPhoto();
    }
  } else {
    //3
    mRecyclerView.setLayoutManager(mLinearLayoutManager);
  }
}

This code checks to see what LayoutManager your RecyclerView is using, and then:

  1. If it’s using the LinearLayoutManager, it swaps in the GridLayoutManager
  2. It requests a new photo if your grid layout only has one photo to show
  3. If it’s using the GridLayoutManager, it swaps in the LinearLayoutManager

Next, you need to make some changes to getLastVisibleItemPosition() to help it handle the new LayoutManager. Replace its current contents with the following:

int itemCount;
 
if (mRecyclerView.getLayoutManager().equals(mLinearLayoutManager)) {
  itemCount = mLinearLayoutManager.findLastVisibleItemPosition();
} else {
  itemCount = mGridLayoutManager.findLastVisibleItemPosition();
}
 
return itemCount;

Here you ask the RecyclerView to tell you what its LayoutManager is, then you ask that LayoutManager to tell you the position of the last visible item.

To use the grid layout, make use of the Options menu button that is already available in the app. Add the following code underneath onStart():

@Override
public boolean onOptionsItemSelected(MenuItem item) {
  if (item.getItemId() == R.id.action_change_recycler_manager) {
    changeLayoutManager();
    return true;
  }
  return super.onOptionsItemSelected(item);
}

This checks the ID of the item tapped in the menu, then works out what to do about it. In this case, there should only be one ID that will match up, effectively telling the app to go away and rearrange the RecyclerView’s LayoutManager.

And just like that, you’re ready to go! Load up the app and tap the button at the top right of the screen, and you’ll begin to see the stars shift:

12. Grid Layout

Star Killer

Sometimes you’ll see things you just don’t like the look of, perhaps a galaxy far, far away that has fallen to the dark side or a planet that is prime for destruction. How could you go about killing it with a swipe?

Luckily, Android engineers have provided a useful class named ItemTouchHelper that gives you easy swipe behavior. Creating and attaching this to a RecyclerView requires just a few lines of code.

In MainActivity.java, underneath setRecyclerViewScrollListener() add the following method:

private void setRecyclerViewItemTouchListener() {
 
  //1
  ItemTouchHelper.SimpleCallback itemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder viewHolder1) {
      //2
      return false;
    }
 
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
      //3
      int position = viewHolder.getAdapterPosition();
      mPhotosList.remove(position);
      mRecyclerView.getAdapter().notifyItemRemoved(position);
    }
  };
 
  //4
  ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
  itemTouchHelper.attachToRecyclerView(mRecyclerView);
}

Let’s go through this step by step:

  1. You create the callback and tell it what events to listen for. It takes two parameters, one for drag directions and one for swipe directions, but you’re only interested in swipe, so you pass 0 to inform the callback not to respond to drag events.
  2. You return false in onMove because you don’t want to perform any special behavior here.
  3. onSwiped is called when you swipe an item in the direction specified in the ItemTouchHelper. Here, you request the viewHolder parameter passed for the position of the item view, then you remove that item from your list of photos. Finally, you inform the RecyclerView adapter that an item has been removed at a specific position.
  4. You initialize the ItemTouchHelper with the callback behavior you defined, and then attach it to the RecyclerView.

Add the method to the activity’s onCreate(), underneath setRecyclerViewScrollListener():

setRecyclerViewItemTouchListener();

This will attach the ItemTouchListener to the RecyclerView using the code you just wrote.

Run the app once more and swipe across one of your items, you should see it begin to move. If you swipe the item far enough, you should see it animate and vanish. If other items are visible, then they will reorganize themselves to cover the hole. How cool is that?

13 Swipe Away Item

Where To Go From Here?

Nice job! You’ve been on quite an adventure, but now it’s time to head back to Earth and think about what you’ve learned.

  • You’ve created a RecyclerView and all the components it needs, such as a LayoutManager, an adapter and a ViewHolder.
  • You’ve updated and removed items from an Adapter.
  • You’ve added some cool features like changing layouts and adding swipe functionality.

Above all, you’ve experienced how separation of components — a key attribute of RecyclerViews — provides so much functionality with such ease. If you want your collections to be flexible and provide some excitement, then look no further than the all-powerful RecyclerView.

The final project for this tutorial is available here.

If you want to learn more about RecyclerViews then check out the Android documentation to see what it can do. Take a look at the support library for RecyclerViews to learn how to use it on older devices. If you want to make them fit with the material design spec then check out the list component design specification.

Join us in the forums to discuss this tutorial and your findings as you work with RecylerViews!

Until next time, space traveler!

The post Android RecyclerView Tutorial appeared first on Ray Wenderlich.

Video Tutorial: MapKit and Core Location Part 4: Region Monitoring

Alt-U 25% Off Discount Ending Soon

$
0
0

Alt-U-Thumb

I hope everyone had a wonderful week at WWDC!

This is a quick reminder that the 25% off discount for the upcoming Alt-U classes is ending soon. The discount applies to all three levels of classes:

  • iOS Apprentice (Aug 1-5): For beginners – the class I wrote about here.
  • iOS Journeyman (Aug 22-26): For intermediate developers. Covers asynchronous development, networking, Core Data, and more.
  • iOS Master (Oct 3-7): For advanced developers. Covers functional programming, app architecture, custom Collection View layouts, and more.

I spoke to the guys at Five Pack Creative, and they said all classes will be fully up-to-date with Swift 3 and iOS 10 – w00t!

To get the discount, just add the course to your cart and enter this coupon code: RAYWENDERLICH. The discount expires in just a few days, so definitely snag it while you still can.

I hope you all have safe travels, and get some much-needed post-WWDC rest! :]

The post Alt-U 25% Off Discount Ending Soon appeared first on Ray Wenderlich.

Video Tutorial: MapKit and Core Location Part 5: MapKit

Video Tutorial: MapKit and Core Location Part 6: Annotations

iOS 10 by Tutorials: Free Access for Subscribers!

$
0
0

i10t-feature (1)

There’s a lot of new goodies for developers in Xcode 8 and iOS 10 this year – Message Apps, SiriKit, Memory Debugging, oh my!

As usual, we’re already working on a new book to help get you up-to-speed: iOS 10 by Tutorials.

Our iOS by Tutorials series is extremely popular, and this is our fifth year in a row making a book in this series. But this year, we’re doing things differently:

  • Screencasts first: Starting next week, we will be releasing 2 screencasts a week on iOS 10 topics for raywenderlich.com subscribers. This way we can get some tutorials to you right away.
  • Chapters second: After we finish the screencasts on an iOS 10 topic, we’ll write the corresponding chapters in iOS 10 by Tutorials, providing regular early access releases along the way.
  • Free access for subscribers: Best of all, as a way of saying thanks, subscribers get free access to both the screencasts and book as part of their subscription!

Keep reading to find out about the plans for iOS 10 by Tutorials and how to get your copy.

iOS 10 by Tutorials

After having watched over 55 WWDC 2016 videos, we can safely say there’s a lot iOS 10 material to cover in this book!

Here’s what we’re planning on including so far (note that since the book is in progress, this is subject to change):

  • Migrating to Swift 3: Learn how to migrate your code to Swift 3, including understanding the API naming conventions, updates to Foundation, switching toolchains, and more.
  • Message Apps: Learn how to integrate your own functionality into Apple’s messages apps, including sticker packs, collaborative messages, and more.
  • SiriKit: Learn how to integrate your apps with Siri, including making an extension, handling the conversation, and providing a custom UI.
  • UIView Property Animator: Learn about the new UIView Property Animator API, including using it for basic animations, interactive animations, and view controller transitions.
  • Speech Recognizer: Learn about the new speech recognizer that allows you to easily detect speech from within your apps.
  • Memory Debugger: Learn about Xcode 8’s powerful new memory debugger that allows you to easily detect and fix retain cycles and other memory problems.
  • Other Xcode 8 Improvements: Learn about other handy new features in Xcode 8, such as Auto Layout improvements, the new threading debugger, editor enhancements, and more.
  • User Notifications: Learn about the new user notifications API that consolidates remote and local notifications, and new features such as advanced appearance and in-app presentation.
  • Photography: Learn about some new features with photography in iOS 10, such as capturing and editing live photos, and advanced photo capture techniques.
  • And much, much more: App Transport security changes, UIControl updates, search and proactive improvements, and more.

Although you could teach yourself these items by reading Apple docs and sample code, it would take forever – and let’s face it, we’re all busy developers.

That’s why we create the iOS by Tutorials books each year. We do the hard work of figuring things out and making a nice easy-to-understand tutorial – that way you can quickly get up-to-speed and get back to making great apps.

Where To Go From Here?

Here’s how you can get your hands on the screencasts and books:

  • If you’re a raywenderlich.com subscriber, you’re all set! You’ll start seeing the screencasts appear next week, and early access versions of the book will appear on your My Loot page as they become ready.
  • 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 tons of new Swift 3 video tutorials in the coming year! :]

The post iOS 10 by Tutorials: Free Access for Subscribers! appeared first on Ray Wenderlich.

Video Tutorial: MapKit and Core Location Part 7: Overlays


Screencast: Beginning C# Part 3: Types

$
0
0

Links

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

Next Video: Operators
Previous Video: Variables

Challenge

Create a new script and call it Mad Libs. Add four variables:

myNamestring
favoriteFoodstring
amountdouble
likesToSharebool

Have OnDisable() print out the following sentence:

myName loves to eat amount servings of favoriteFood per day. Likes to share? likesToShare

Challenge Solution

Resources

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

The post Screencast: Beginning C# Part 3: Types appeared first on Ray Wenderlich.

iOS 10 Screencast: Sending Custom Messages in iMessage

Swift Algorithm Club: Swift Tree Data Structure

$
0
0

SwiftAlgorithm-410-transp

The Swift Algorithm Club is an open source project to implement popular algorithms and data structures in Swift.

Every month, Chris Pilcher and I will write a tutorial showing you how to make a cool algorithm or data structure from the project.

This series will be a great way to learn more about algorithms and data structures, and to level up your Swift skills along the way.

In this first tutorial, you’ll learn how to implement a Swift Tree data structure. This is one of the most common and useful data structures, and is a great way to get started!

Note: New to the Swift Algorithm Club? Check out our getting started post first.

Tree Data Structure

The easiest way to understand the tree data structure is is through a picture:

Tree

The above diagram shows a tree with 5 levels. The root is level 0, and as you down the depth of the tree, the level increases by 1.

Trees can help you solve many important problems, including:

  • representing a hierarchical relationship between objects
  • making searches quick and efficient
  • providing a sorted list of data
  • powering prefix matching in text fields

Terminology

First, let’s cover some important terminology you should understand about trees.

Root

The root of a tree refers to the 0th level’s node of a tree. You can also think of it as the entry point to your tree data structure.

root-2

Node

A node is a block of data in the tree structure. The data that a node contains depends on the type of tree you’re making. The root is also a node.

root

Leaf

Sometimes referred as a terminal node, a leaf is simply a node with no children. For example, if the Node object had leftChild and rightChild as nil, then you would refer that node as a leaf.

leaf

Tree Implementation in Swift

In this section, you’ll implement a general-purpose tree. This is a fancy way of saying a tree without any kind of restrictions (like how many children each node may have, or the order of nodes).

Remember that a tree is made up of nodes. So to start, let’s create a basic node class. Create a new Swift playground and add the following empty class:

class Node {
 
}
Note: This tutorial uses Swift 2, since Swift 3 is still in beta. If you are using Swift 3, you may need to slightly tweak some of the code here.

Value

Of course, a node isn’t much use without a value associated with it.

For simplicity, you’ll specialize this tree to manage string data. Update your current implementation of the Node to the following:

class Node {
  var value: String
 
  init(value: String) {
    self.value = value
  }
}

You’ve declared a property named value of type String. You also declare an initializer, which is required for initializing all non-optional stored properties for your class.

Children

In addition to a value, each node needs to have a list of children.

Update your class definition to the following:

class Node {
  var value: String
  var children: [Node] = [] // add the children property
 
  init(value: String) {
    self.value = value
  }
}

You simply declare children as an array of nodes. Each child represents a node that is 1 level deeper than the current node.

Parent

Sometimes it’s handy for each node to have a link to its parent node as well. Children are the nodes below a given node; the parent is the node above. A node may only have one parent, but can have multiple children.

Update the implementation of your Node class to the following:

class Node {
  var value: String
  var children: [Node] = []
  weak var parent: Node? // add the parent property
 
  init(value: String) {
    self.value = value
  }
}

Note that you’ve made parent an optional. This is because not all nodes have parents – such as the root node in a tree.

Insertion

To handle insertion to your tree, you’ll declare a addChild() method in your Node class. Update the implementation of your class to the following:

class Node {
  var value: String
  var children: [Node] = []
  weak var parent: Node?
 
  init(value: String) {
    self.value = value
  }
 
  func addChild(node: Node) {
    children.append(node)
    node.parent = self
  }
}

It’s best to understand how addChild() works by using it on a live playground. Outside the implementation of your class, write the following into your playground:

let beverages = Node(value: "beverages")
 
let hotBeverages = Node(value: "hot")
let coldBeverages = Node(value: "cold")
 
beverages.addChild(hotBeverages)
beverages.addChild(coldBeverages)

Hierarchical structures are natural candidates for tree structures, so here you’ve defined 3 different nodes and organized them into a logical hierarchy. This corresponds to the following structure:

Example

Challenge: Beverage City

Ready for a quick test of knowledge?

Try writing the code to extend your tree to match the following diagram:

Example-2

The solution is provided in the spoiler section down below, but try it yourself first!

Solution Inside: Solution SelectShow>

Printing Trees

Verifying a large tree structure can be hard without any console logging. After defining your tree structure, try to log your result in the console by printing the tree:

print(beverages) // <- try to print it!

You can bring up the console by pressing the following keys in combination: Command-Shift-Y. You should see the name of your class printed onto the console.

Node

How silly! Unfortunately the compiler doesn’t know the best print your custom Swift object, unless you tell it.

To aid the compiler, you’ll need to make Node adopt the CustomStringConvertible protocol. To do this, add the following just below the implementation of your Node class:

// 1
extension Node: CustomStringConvertible {
  // 2
  var description: String {
    // 3
    var text = "\(value)"
 
   // 4
    if !children.isEmpty {
      text += " {" + children.map { $0.description }.joinWithSeparator(", ") + "} "
    }
    return text
  }
}

This code is relatively straight forward:

  1. You’ve declared an extension to your Node class, and you’ve adopted the CustomStringConvertible protocol. This protocol expects you to implement a computed property with the name description, with the String type.
  2. You’ve declared the description property. This is a computed property, a read only property that returns a String.
  3. You’ve declared a text variable. This will hold the entire string. For now, you’ve given it the current node’s value.
  4. In addition to printing the current value of the node, you’ll also need to print the children, children of children, and so on. To do so, you’ll recursively append your children’s description, whilst adding some braces to give the string some context in regards to the structure of the children.

Now, when you call the print your Node classes, you’ll get a nice representation of your tree structure like this:

"beverages {hot {tea {black, green, chai} , coffee, cocoa} , cold {soda {ginger ale, bitter lemon} , milk} } \n"

Note: If you the mapping syntax confuses you, here’s what you could have written instead:

if !children.isEmpty {
  text += " {"
  for child in children {
    text += child.description + ", "
  }
  text += "} "
}

map is a method that acts on a collection of objects, such as arrays. Defined by types that adopt the SequenceType protocol, map allows you to perform operations on every element of the array. In your case, you’re iterating through the children and performing a string append operation.

To learn more about map, you can read about it in this tutorial: Introduction to Functional Programming in Swift.

Search

The general-purpose tree shown here is great for describing hierarchical data, but it really depends on your application in regards to what kind of extra functionality it needs to have. For example, you could use the Node class to determine if the tree contains a particular value.

To facilitate a search algorithm for this general-purpose tree, add the following extension at the bottom of your playground file:

extension Node {
  // 1
  func search(value: String) -> Node? {
    // 2
    if value == self.value {
      return self
    }
    // 3
    for child in children {
      if let found = child.search(value) {
        return found
      }
    }
    // 4
    return nil
  }
}

The code here is relatively straightforward:

  1. The goal of this method is to search if a value exists in the tree. If it does, return the node associated with the value. If it does not exist, you’ll return a nil.
  2. This is the case where you’ve found the value. You’ll return the self, which is the current node.
  3. In this loop, you cycle through the children array. You’ll call each child’s search method, which will recursively iterate through all the children. If any of the nodes have a match, your if let statement will evaluate true and return the node.
  4. You’ll return nil here to signify that you couldn’t find a match.

Let’s give our search method a try! At the bottom of your playground file, write the following:

beverages.search("cocoa") // returns the "cocoa" node
beverages.search("chai") // returns the "chai" node
beverages.search("bubbly") // returns nil

What About Different Types?

Nice work so far! You’ve learned how to implement a general-purpose tree that stores String values. You’ve defined a nice way to print your tree into the console, and also provided searching capabilities to your Node class.

Trees are a great way to lay out your hierarchical structure of strings, but what if you wanted to store integers instead?

You could modify the Node class to take in an Int:

class Node {
  var value: Int
 
  // ...
}

but then your old implementation that accepts String value is lost. Ideally, you’d want to create a Node class that could accept all types of objects, whether it is an Int, Double, Float, or even a custom class of your own. To facilitate the generic usage of your Node class, you’ll have to dive in the world of generics!

Generics

The idea of generics is to abstract away the type requirements from algorithm and data structures. This allows you to keep the idea generalized and reusable. Whether an object would behave well in a tree (or any other data structure) should not be whether it is an Int or a String, but rather something more intrinsic; In the context of trees, any type that behaves well in a hierarchy are good candidates to be used in a tree.

Time to make some breaking changes! Update the implementation of your Node class to the following:

// 1. 
class Node<T> {
  // 2. 
  var value: T
  weak var parent: Node?
  // 3.
  var children: [Node] = []
 
  // 4.
  init(value: T) {
    self.value = value
  }
 
  // 5. 
  func addChild(node: Node) {
    children.append(node)
    node.parent = self
  }
}

Right away, you should see some compiler errors. But fear not, you’ll clear those errors as you finish the generalizing your Node implementation for all types. Here’s what you’ve done here:

  1. You’ve changed the declaration of the Node class to take a generic type T. The <> syntax around T is what alerts the compiler that your intention of using generics.
  2. Your goal is to allow the Node class to take in values of any type, so you’ll constrain your value property to be type T rather than Int or String.
  3. For the same reason as the other points, you’ll now declare that your class has children of type T.
  4. You’ve also updated your initializer to take any type.
  5. You’ve updated your addChild method to take in Node objects of any type matching the current type of Node

So far so good. Next, find the extension that contains the search method and update it to use generics:

// 1. 
extension Node where T: Equatable {
  // 2. 
  func search(value: T) -> Node? {
    if value == self.value {
      return self
    }
    for child in children {
      if let found = child.search(value) {
        return found
      }
    }
    return nil
  }
}

You’ve made two changes here:

  1. You’ve introduced a constraint for this extension so that any type must be Equatable before it can utilize the search method.
  2. You’ve updated the value parameter to be of generic type.

Your code should compile now, so let’s test this out! At the bottom of your playground file, add the following code to verify that your generic tree is working:

let number = Node(value: 5)

Congratulations, you’ve just create a general-purpose tree that works for all types of objects!

Other Trees

You’ve created a very basic tree here, but there are many different ways to construct trees. For example:

  • Sometimes you don’t need to have a parent property at all.
  • Maybe you only need to give each node a maximum of two children – such a tree is called a binary tree.
  • A very common type of tree is the binary search tree (or BST), a stricter version of a binary tree where the nodes are ordered in a particular way to speed up searches.

To learn more about these kinds of trees and more, check out this list of articles in the Swift Algorithm Club repo:

Where To Go From Here?

I hope you enjoyed this tutorial on making a Swift Tree data structure!

It’s in your best interest to know about algorithms and data structures – they’re solutions to many real world problems, and are frequently asked as interview questions. Plus it’s fun!

So stay tuned for many more tutorials from the Swift Algorithm club in the future. In the meantime, if you have any questions on implementing trees in Swift, please join the forum discussion below!

Note: The Swift Algorithm Club is always looking for more contributors. If you’ve got an interesting data structure, algorithm, or even an interview question to share, don’t hesitate to contribute! To learn more about the contribution process, check out our Join the Swift Algorithm Club article.

The post Swift Algorithm Club: Swift Tree Data Structure appeared first on Ray Wenderlich.

iOS 10 Screencast: Property Animators: Timing Functions

Android Networking Tutorial: Getting Started

$
0
0

android-networking-feature

Networking has played a huge role in Android apps since API level 1. Most apps don’t work in isolation; rather, they connect to an online service to retrieve data or perform other networking functions.

In this Android networking tutorial, you will create a simple app which connects to the GitHub API to retrieve and display a list of repositories.

In the process, you will learn about the following:

  • How to check your network connection status.
  • How to perform network operations.
  • How to leverage open source libraries to perform network operations.
Note: This tutorial assumes you’re already familiar with the basics of Android development. If you are completely new to Android development, read through our Android Tutorial for Beginners to familiarize yourself with the basics.

Getting Started

Download the starter project for this tutorial and extract the project. Open the starter project in Android Studio by selecting Open an existing Android Studio project from the Quick Start menu:

quick_start_menu

You can also use File\Open. Navigate to and select the starter project folder.

Open Util.java and look inside; this file contains a helper method which you’ll use to convert a JSON string to a list of repository objects. Repository.java is your model class that represents a Github repository.

Build and run the project to see what you have to work with:

intro_to_net_1

Required Permissions

To perform network operations in Android, your application must include certain permissions.

Open manifest/AndroidManifest.xml, and add the following permissions just before the application tag:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" />

The ACCESS_NETWORK_STATE permission is required for your application to check the network state of the device, while the INTERNET permission is required for your application to access the Internet.

Before you attempt to perform any network operations, it is advisable to check first that the device is connected to a network! :]

Checking the Network Connection

Before adding any Java code, you’ll need to configure Android Studio to automatically insert import statements to save you from having to add each one manually.

Go to Android Studio\Preferences\Editor\General\Auto Import, select the Add unambiguous imports on the fly checkbox and click OK.

Open MainActivity.java, and add the following method to the class:

private boolean isNetworkConnected() {
  ConnectivityManager connMgr = (ConnectivityManager)
   getSystemService(Context.CONNECTIVITY_SERVICE); // 1
  NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); // 2
  return networkInfo != null && networkInfo.isConnected(); // 3
}

isNetworkConnected() checks that the device has an active Internet connection as follows:

  1. Retrieves an instance of the ConnectivityManager class from the current application context.
  2. Retrieves an instance of the NetworkInfo class that represents the current network connection. This will be null if no network is available.
  3. Check if there is an available network connection and the device is connected.

It’s a good idea to show an alert that prompts the user to connect to a network if the device isn’t already connected.

You will need to manually add the following import statements to the top of the file since Android Studio can’t automatically determine which packages to include:

import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;

Replace the showListFragment(new ArrayList()); line in onCreate() with the following code:

if (isNetworkConnected()) {
  mProgressDialog = new ProgressDialog(this);
  mProgressDialog.setMessage("Please wait...");
  mProgressDialog.setCancelable(false);
  mProgressDialog.show();
 
  startDownload();
} else {
  new AlertDialog.Builder(this)
    .setTitle("No Internet Connection")
    .setMessage("It looks like your internet connection is off. Please turn it " +
    "on and try again")
    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
    }
  }).setIcon(android.R.drawable.ic_dialog_alert).show();
}

This displays a progress dialog and calls startDownload() if the device is connected to a network; if it isn’t connected, it displays an alert to the user instead.

Build and run your app; if your device is connected to a network, the app will show a ProgressDialog like so:

intro_to_net_2

Now turn off all network connections on your device and start the app again. The app should now show an alert with the Internet connection error message:

intro_to_net_3

Note: If using the emulator, check out the Android Emulator 2.0 section of our tutorial What’s New in Android Studio 2 for tips on mocking different network states.

Determining the Connection Type

When you have an app that retrieves huge amounts of data, you might want to restrict network connections to particular network types, such as Wi-Fi. You can do this using getType() on the NetworkInfo object.

Add the following method to MainActivity:

private boolean isWifiConnected() {
  ConnectivityManager connMgr = (ConnectivityManager)
              getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
  return networkInfo != null && (ConnectivityManager.TYPE_WIFI == networkInfo.getType()) && networkInfo.isConnected();
}

The above method returns true if the device is connected to a Wi-Fi network.

Replace the isNetworkConnected() call in onCreate() with the following:

isWifiConnected()

Build and run your app; if your device is connected to a wifi network, the app will display the following progress dialog:

intro_to_net_2

Now turn off the Wi-Fi connection on the device (leaving packet data active) and restart the app. You’ll see an alert with the Internet connection error message:

intro_to_net_3

Performing Network Operations

Android best practice is to handle any long-running tasks that may hang your application in a thread separate from the user interface thread. Network operations fall into this category of tasks.

Since Android 3.0 (Honeycomb), the system has been configured to crash with a NetworkOnMainThreadException exception if the network is accessed in the user interface thread.

The AsyncTask class lets you perform asynchronous tasks in the background and publish results on the UI thread without any thread manipulation.

Since AsyncTask is an abstract class, you must subclass it before you can use it.

To create a new class, select the package in which you want to create the class – in this case, reposearch. Next, select File/New/Java Class:

intro_to_net_4.png

In the Create New Class dialog, enter DownloadRepoTask as the name of the class and click OK:

intro_to_net_5

Replace the class declaration with the following code:

public class DownloadRepoTask extends AsyncTask<String, Void, String> {
  // 1
  @Override
  protected String doInBackground(String... params) {
    return null;
  }
 
  // 2
  @Override
  protected void onPostExecute(String result) {
 
  }
}

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

AsyncTask makes it possible to perform (long-running) operations in the background and publish results on the UI thread without having to write threading code. The declaration for DownloadRepoTask contains the keywords extends AsyncTask. The first parameter in the diamond operator determines the type of the input parameter to the doInBackground method while the third parameter in the diamond operator determines the type of the return value of the onPostExecute method. The second parameter determines the type of the return value of the onProgressUpdate method which is not used here so it is set to Void.

  1. doInBackground() is where your background task executes – in this case, the data download task.
  2. When your background task is done, onPostExecute() is called is called with the results.

Add the following code to startDownload() in MainActivity.java:

new DownloadRepoTask().execute("https://api.github.com/repositories");

This calls an instance of the DownloadRepoTask class, with the parameters required to perform the task passed in the execute call.

Connecting to a Network URL

Open DownloadRepoTask.java and add the following method:

private String downloadData(String urlString) throws IOException {
  InputStream is = null;
  try {
    URL url = new URL(urlString);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.connect();
 
    return null;
  } finally {
    if (is != null) {
      is.close();
    }
  }
}

The above code creates and opens a connection to the resource referred to by the URL.

Replace the return statement in doInBackground() with the following:

try {
  return downloadData(params[0]);
} catch (IOException e) {
  e.printStackTrace();
  return null;
}

This calls downloadData() and passes to it the URL you passed in from the task execution.

Retrieving Repositories

Now that you’ve created a connection to the resource, you can retrieve the data. You get the data as an InputStream by calling getInputStream() method on the HttpURLConnection object.

Add the following code to the DownloadRepoTask.java class, just before the return statement:

is = conn.getInputStream();

An InputStream is a readable source of bytes; in this case, it’s the JSON returned from GitHub that you’ll have to decode.

Add the following method to DownloadRepoTask.java:

private String convertToString(InputStream is) throws IOException {
  BufferedReader r = new BufferedReader(new InputStreamReader(is));
  StringBuilder total = new StringBuilder();
  String line;
  while ((line = r.readLine()) != null) {
    total.append(line);
  }
  return new String(total);
}

convertToString() uses the StringBuilder class to build a string from the input stream.

Replace the return statement in downloadData() with the following:

return convertToString(is);

You need to pass the downloaded data on to the Activity that started the task. You can use a listener, a type of interface, for this.

To create a new interface, select the package in which you want to create the interface – in this case, the reposearch package. Next, select File/New/Java Class.

In the Create New Class dialog, enter the name of the interface as DownloadCompleteListener, select the Interface option in the Kind select box and click OK.

Add the following method declaration inside DownloadCompleteListener:

void downloadComplete(ArrayList<Repository> repositories);

Add implements DownloadCompleteListener to the beginning of the class declaration of MainActivity.java so it looks like the following:

public class MainActivity extends AppCompatActivity implements DownloadCompleteListener

Now implement downloadComplete() in MainActivity.java like so:

@Override
public void downloadComplete(ArrayList<Repository> repositories) {
 
}

Add the following DownloadCompleteListener to DownloadRepoTask.java:

DownloadCompleteListener mDownloadCompleteListener;

Next, add a new constructor that takes a DownloadCompleteListener parameter and uses it to set the DownloadCompleteListener field in the class:

public DownloadRepoTask(DownloadCompleteListener downloadCompleteListener) {
  this.mDownloadCompleteListener = downloadCompleteListener;
}

Next, add the following code to onPostExecute:

try {
  mDownloadCompleteListener.downloadComplete(Util.retrieveRepositoriesFromResponse(result));
  }
catch (JSONException e) {
  e.printStackTrace();
}

This converts and passes the results to the DownloadCompleteListener using downloadComplete().

Open MainActivity.java and replace the code in startDownload() with the following:

new DownloadRepoTask(this).execute("https://api.github.com/repositories");

This will start the AsyncTask that you made and direct it to the github repositories URL.

Next, add the following code to downloadComplete():

showListFragment(repositories);
if (mProgressDialog != null) {
  mProgressDialog.hide();
}

The above code receives a list of Repository objects, displays them to the user, then hides the progress dialog.

Make sure the device has a working Internet connection, then build and run:

intro_to_net_6

Cool – your app connected to the GitHub API and retrieved a list of repositories for your perusal!

Performing network operations in Android can be a bit tedious; you have to open and close connections, handle InputStream conversions and ensure you’re performing the network operation in a background thread.

As usual, the geniuses of the open source community have come to the rescue in the form of open source libraries that take care of the tedious details for you.

Open Source To The Rescue

There are a number of open source libraries that simplify a lot of Android networking operations. I’ve put together a few notes on the three most popular ones below.

OkHttp

OkHttp is an efficient HTTP client which supports synchronous and asynchronous calls. It handles the opening and closing of connections along with InputStream-to-string conversion. It’s compatible with Android 2.3 and above.

Implementing OkHttp is pretty simple and reduces the overall amount of code needed to make network calls – but there’s a catch.

Since OkHttp is built as a Java library, and not an Android library, it doesn’t take into consideration the Android Framework limitations of only permitting view updates on the main UI thread. To update any views, or post data back to the main thread, you will need to use runOnUiThread() provided in the Android Activity superclass.

To use OkHttp, you have to first add it as a dependency to your project.

Add the following dependency to build.gradle of the app module:

dependencies {
  ...
  compile 'com.squareup.okhttp3:okhttp:3.1.2'
}

Your build.gradle file should look something like the one shown below:
intro_to_net_7

Now rebuild your project:

Open MainActivity.java and add the following method:

private void makeRequestWithOkHttp(String url) {
  OkHttpClient client = new OkHttpClient();   // 1
  okhttp3.Request request = new okhttp3.Request.Builder().url(url).build();  // 2
 
  client.newCall(request).enqueue(new okhttp3.Callback() { // 3
    @Override
    public void onFailure(okhttp3.Call call, IOException e) {
      e.printStackTrace();
    }
 
    @Override
    public void onResponse(okhttp3.Call call, okhttp3.Response response)
    throws IOException {
      final String result = response.body().string();  // 4
 
      MainActivity.this.runOnUiThread(new Runnable() {
        @Override
        public void run() {
          try {
            downloadComplete(Util.retrieveRepositoriesFromResponse(result));  // 5
          } catch (JSONException e) {
            e.printStackTrace();
          }
        }
      });
    }
  });
}

The code above does the following:

  1. Creates an OkHttpClient object.
  2. Builds an OkHttpClient request with the URL you want to connect to.
  3. Queues the request call.
  4. Retrieves the response as a string.
  5. Converts and passes the results to the main thread.

Replace the code in the startDownload() method with the following:

makeRequestWithOkHttp("https://api.github.com/repositories");

Build and run your app, and everything should work as before:

intro_to_net_6

Pretty simple, right?

Volley

Volley is an Android library for making fast and easy network calls. Just like OkHttp, it does all the dirty work for you. One of its advantages over OkHttp is that it’s compatible with Android 1.6 and above.

By default, Volley makes all calls in an asynchronous manner. Since it’s an Android library, it doesn’t require any extra work to return data to the main thread or update views.

To use Volley, add the following dependency to build.gradle of the app module and rebuild the project:

dependencies {
  ...
  compile 'com.android.volley:volley:1.0.0'
}

Open MainActivity.java and add the following method:

private void makeRequestWithVolley(String url) {
 
  RequestQueue queue = Volley.newRequestQueue(this); // 1
 
  StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
              new com.android.volley.Response.Listener<String>() { // 2
    @Override
    public void onResponse(String response) {
      try {
        downloadComplete(Util.retrieveRepositoriesFromResponse(response)); // 3
      } catch (JSONException e) {
        e.printStackTrace();
      }
    }
  }, new com.android.volley.Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
    }
  });
  queue.add(stringRequest);  // 4
 
}

makeRequestWithVolley does the following:

  1. Creates a new request queue.
  2. Creates a new instance of StringRequest with the URL you want to connect to.
  3. Converts and passes the results to downloadComplete().
  4. Adds the string request to the request queue.

Replace the code in startDownload() with the following code:

makeRequestWithVolley("https://api.github.com/repositories");

Build and run your project; once again, the network requests should work as before:

intro_to_net_6

Retrofit

Retrofit is an Android and Java library which is great at retrieving and uploading structured data such as JSON and XML. Retrofit makes HTTP requests using OkHttp. Unlike the case of Volley, where you have to convert a JSON string to a Repository object, Retrofit does that conversion for you. Retrofit also lets you specify any of the following libraries for the data conversion:

  1. Gson
  2. Jackson
  3. Moshi
  4. Protobuf
  5. Wire
  6. Simple XML
  7. Scalars (primitives, boxed, and String)

To use Retrofit, add the following dependencies to build.gradle of the app module and rebuild the project:

dependencies {
  ...
  compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
  compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
}

Then create a new interface and name it RetrofitAPI; this will define the HTTP operations.

Add the following code to RetrofitAPI.java:

@GET("/repositories")
Call<ArrayList<Repository>> retrieveRepositories();

This specifies the request method and the URL for the request call.

Add the following imports to MainActivity.java:

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

Then add the following method:

private void makeRetrofitCalls() {
  Retrofit retrofit = new Retrofit.Builder()
   .baseUrl("https://api.github.com") // 1
   .addConverterFactory(GsonConverterFactory.create()) // 2
   .build();
 
  RetrofitAPI retrofitAPI = retrofit.create(RetrofitAPI.class); // 3
 
  Call<ArrayList<Repository>> call = retrofitAPI.retrieveRepositories(); // 4
 
  call.enqueue(new Callback<ArrayList<Repository>>() {  // 5
    @Override
    public void onResponse(Call<ArrayList<Repository>> call,
                                 Response<ArrayList<Repository>> response) {
      downloadComplete(response.body());  // 6
    }
 
    @Override
    public void onFailure(Call<ArrayList<Repository>> call, Throwable t) {
      Toast.makeText(MainActivity.this, t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
    }
  });
}

makeRetrofitCalls() does the following:

  1. Specifies the base URL
  2. Specifies GsonConverterFactory as the converter which uses Gson for its deserialization.
  3. Generates an implementation of the RetrofitAPI.
  4. Generates the request call.
  5. Queues the request call.
  6. Passes the results to downloadComplete().

Replace the code in startDownload() with the following:

makeRetrofitCalls();

Build and run your project, and the list of repositories shows up as before:

intro_to_net_6

Where to Go From Here?

You’ve explored (and survived!) a crash-course on network operations in Android. :] You can download the final project from this tutorial here.

For more details on the open source projects used in this Android networking tutorial, check out the OkHttp and Retrofit pages on Square’s GitHub pages and the Volley page on the Android developer site.

You can also check out the Networking Operations page on the Android developer site.

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

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

iOS 10 Screencast: Interactive Custom Notifications

Magical Error Handling in Swift

$
0
0

Swift Error Handling is Magical

Error handling in Swift has come a long way since the patterns in Swift 1 that were inspired by Objective-C. Major improvements in Swift 2 make the experience of handling unexpected states and conditions in your application more straightforward.

Just like other common programming languages, preferred error handling techniques in Swift can vary, depending upon the type of error encountered, and the overall architecture of your app.

This tutorial will take you through a magical example involving wizards, witches, bats and toads to illustrate how best to deal with common failure scenarios. You’ll also look at how to upgrade error handling from projects written in earlier versions of Swift and, finally, gaze into your crystal ball at the possible future of error handling in Swift!

Note: This tutorial assumes you’re familiar with Swift 2 syntax – particularly enumerations and optionals. If you need a refresher on these concepts, start with the What’s New in Swift 2 post by Greg Heo.

Time to dive straight in (from the the cauldron into the fire!) and discover the various charms of error handling in Swift 2!

Getting Started

There are two starter playgrounds for this tutorial, one for each section. Download Avoiding Errors with nil – Starter.playground and Avoiding Errors with Custom Handling – Starter.playground playgrounds.

Open up the Errors with nil starter playground in Xcode.

Read through the code and you’ll see several classes, structs and enums that hold the magic for this tutorial.

Take note the following parts of the code:

protocol MagicalTutorialObject {
  var avatar: String { get }
}

This protocol is applied to all classes and structs used throughout the tutorial to provide a visual representation of each object that can be printed to the console.

enum MagicWords: String {
  case Abracadbra = "abracadabra"
  case Alakazam = "alakazam"
  case HocusPocus = "hocus pocus"
  case PrestoChango = "presto chango"
}

This enumeration denotes magic words that can be used to create a Spell.

struct Spell: MagicalTutorialObject {
  var magicWords: MagicWords = .Abracadbra
  var avatar = "*"
}

This is the basic building block for a Spell. By default, you initialize its magic words to Abracadabra.

Now that you’re acquainted with the basics of this supernatural world, you’re ready to start casting some spells.

Why Should I Care About Error Handling?

“Error handling is the art of failing gracefully.”

Swift Apprentice, Chapter 21 (Error Handing)

Good error handling enhances the experience for end users as well as software maintainers by making it easier to identify issues, their causes and their associated severity. The more specific the error handling is throughout the code, the easier the issues are to diagnose. Error handling also lets systems fail in an appropriate way so as not to frustrate or upset users.

But errors don’t always need to be handled. When they don’t, language features let you avoid certain classes of errors altogether. As a general rule, if you can avoid the possibility of an error, take that design path. If you can’t avoid a potential error condition, then explicit handling is your next best option.

Avoiding Swift Errors Using nil

Since Swift has elegant optional-handling capabilities, you can completely avoid the error condition where you expect a value, but no value is provided. As a clever programmer, you can manipulate this feature to intentionally return nil in an error condition. This approach works best where you should take no action if you reach an error state; i.e., you choose inaction over emergency action.

Two typical examples of avoiding Swift errors using nil are failable initializers and guard statements.

Failable Initializers

Failable initializers prevent the creation of an object unless sufficient information has been provided. Prior to Swift 2 (and in other languages), this functionality was typically achieved via the Factory Method Pattern.

An example of this pattern in Swift can be seen in createWithMagicWords:

static func createWithMagicWords(words: String) -> Spell? {
  if let incantation = MagicWords(rawValue: words) {
    var spell = Spell()
    spell.magicWords = incantation
    return spell
  }
  else {
    return nil
  }
}

The above initializer tries to create a spell using magic words provided, but if the words are not magical you return nil instead.

Inspect the creation of the spells at the very bottom of this tutorial to see this behavior in action:

Screen Shot 2016-04-15 at 6.45.31 PM

While first successfully creates a spell using the magic words "abracadabra", "ascendio" doesn’t have the same effect, and the return value of second is nil. (Hey, witches can’t win all the time).

Oh Really?

Factory methods are an old-school programming style. There are better ways to achieve the same thing in Swift. You’ll update the Spell extension to use a failable initializer instead of a factory method.

Delete createWithMagicWords(_:) and replace it with the following:

init?(words: String) {
  if let incantation = MagicWords(rawValue: words) {
    self.magicWords = incantation
  }
  else {
    return nil
  }
}

Here you’ve simplified the code by not explicitly creating and return the Spell object.

The lines that assign first and second now throw compiler errors:

let first = Spell.createWithMagicWords("abracadabra")
let second = Spell.createWithMagicWords("ascendio")

You’ll need to change these to use the new initializer. Replace the lines above with the following:

let first = Spell(words: "abracadabra")
let second = Spell(words: "ascendio")

After this, all errors should be fixed and the playground should compile without error. With this change your code is definitely tidier – but you can do even better! :]

Guard Statements

guard is a quick way to assert that something is true: for example, if a value is > 0, or if a conditional can be unwrapped. You can then execute a block of code if the check fails.

guard was introduced in Swift 2 and is typically used to (bubble, toil and trouble) bubble-up error handling through the call stack, where the error will eventually be handled. Guard statements allow early exit from a function or method; this makes it more clear which conditions need to be present for the rest of the processing logic to run.

To clean up Spell‘s failable initializer further, edit it as shown below to use guard:

init?(words: String) {
  guard let incantation = MagicWords(rawValue: words) else {
    return nil
  }
  self.magicWords = incantation
}

With this change, there’s no need need for an else clause on a separate line and and the failure case is more evident as it’s now at the top of the intializer. Also, the “golden path” is least indented. The “golden path” is the path of execution that happens when everything goes as expected, i.e. no error. Being least indented means that it is easy to read.

Note that the values of first and second Spell constants haven’t changed, but the code is more more streamlined.

Avoiding Errors with Custom Handling

Having cleaned up the Spell initializer and avoided some errors through the clever use of nil, you’re ready to tackle some more intriciate error handling.

For the next section of this tutorial, open up Avoiding Errors with Custom Handling – Starter.playground.

Take note of the following features of the code:

struct Spell: MagicalTutorialObject {
 
  var magicWords: MagicWords = .Abracadbra
  var avatar = "*"
 
  init?(words: String) {
    guard let incantation = MagicWords(rawValue: words) else {
      return nil
    }
    self.magicWords = incantation
  }
 
  init?(magicWords: MagicWords) {
    self.magicWords = magicWords
  }
}

This is the Spell initializer, updated to match the work you completed in the first section of this tutorial. Also note the presence of the MagicalTutorialObject protocol, and a second failable initializer, which has been added for convenience.

protocol Familiar: MagicalTutorialObject {
  var noise: String { get }
  var name: String? { get set }
  init()
  init(name: String?)
}

The Familiar protocol will be applied to various animals (such as bats and toads) further down in the playground.

Note:For those unfamiliar with the term familiar, this is a witch’s or wizard’s magical animal sidekick, which usually has human-like qualities. Think Hedwig from Harry Potter, or the flying monkeys in the Wizard of Oz.

Owl

This clearly isn’t Hedwig, but still cute nonetheless, no?

struct Witch: MagicalBeing {
  var avatar = "*"
  var name: String?
  var familiar: Familiar?
  var spells: [Spell] = []
  var hat: Hat?
 
  init(name: String?, familiar: Familiar?) {
    self.name = name
    self.familiar = familiar
 
    if let s = Spell(magicWords: .PrestoChango) {
      self.spells = [s]
    }
  }
 
  init(name: String?, familiar: Familiar?, hat: Hat?) {
    self.init(name: name, familiar: familiar)
    self.hat = hat
  }
 
  func turnFamiliarIntoToad() -> Toad {
    if let hat = hat {
      if hat.isMagical { // When have you ever seen a Witch perform a spell without her magical hat on ? :]
        if let familiar = familiar {   // Check if witch has a familiar
          if let toad = familiar as? Toad {  // Check if familiar is already a toad - no magic required
            return toad
          } else {
            if hasSpellOfType(.PrestoChango) {
              if let name = familiar.name {
                return Toad(name: name)
              }
            }
          }
        }
      }
    }
    return Toad(name: "New Toad")  // This is an entirely new Toad.
  }
 
  func hasSpellOfType(type: MagicWords) -> Bool { // Check if witch currently has appropriate spell in their spellbook
    return spells.contains { $0.magicWords == type }
  }
}

Finally, the witch. Here you see the following:

  • A Witch is initialized with a name and a familiar, or with a name, a familiar and a hat.
  • A Witch knows a finite number of spells, stored in spells, which is an array of Spell objects.
  • A Witch seems to have a penchant for turning her familiar into a toad via the use of the .PrestoChango spell, within turnFamiliarIntoToad().

Notice the length and amount of indentation in turnFamiliarIntoToad(). Also, if anything goes wrong in the method, an entirely new toad will be returned. That seems like a confusing (and incorrect!) outcome for this particular spell. You’ll clean up this code significantly with custom error handling in the next section.

Refactoring to Use Swift Errors

“Swift provides first-class support for throwing, catching, propagating, and manipulating
recoverable errors at runtime.”

The Swift Programming Language (Swift 2.2)

Not to be confused with the Temple of Doom, the Pyramid of Doom is an anti-pattern found in Swift and other languages that can require many levels of nested statements for control flow. It can be seen in turnFamiliarIntoToad() above – note the six closing parentheses required to close out all the statements, trailing down on a diagonal. Reading code nested in this way requires significant cognitive effort.

Pyramid of Doom

Guard statements, as you’ve seen earlier, and multiple optional binding can assist with the cleanup of pyramid-like code. The use of a do-catch mechanism, however, eliminates the problem altogether by decoupling control flow from error state handling.

do-catch mechanisms are often found near the following, related, keywords:

  • throws
  • do
  • catch
  • try
  • defer
  • ErrorType

To see these mechanisms in action, you are going to throw multiple custom errors. First, you’ll define the states you wish to handle by listing out everything that could possibly go wrong as an enumeration.

Add the following code to your playground above the definition of Witch:

enum ChangoSpellError: ErrorType {
  case HatMissingOrNotMagical
  case NoFamiliar
  case FamiliarAlreadyAToad
  case SpellFailed(reason: String)
  case SpellNotKnownToWitch
}

Note two things about ChangoSpellError:

  • It conforms to the ErrorType protocol, a requirement for defining errors in Swift.
  • In the SpellFailed case, you can handily specify a custom reason for the spell failure with an associated value.
Note: The ChangoSpellError is named after the magical utterance of “Presto Chango!” – frequently used by a Witch when attempting to change a familiar into a Toad).

OK, ready to make some magic, my pretties? Excellent. Add throws to the method signature, to indicate that errors may occur as a result of calling this method:

func turnFamiliarIntoToad() throws -> Toad {

Update it as well on the MagicalBeing protocol:

protocol MagicalBeing: MagicalTutorialObject {
  var name: String? { get set }
  var spells: [Spell] { get set }
  func turnFamiliarIntoToad() throws -> Toad
}

Now that you have the error states listed, you will rework the turnFamiliarIntoToad() method, one clause at a time.

Handling Hat Errors

First, modify the following statement to ensure the witch is wearing her all-important hat:

if let hat = hat {

…to the following:

guard let hat = hat else {
  throw ChangoSpellError.HatMissingOrNotMagical
}
Note: Don’t forget to remove the associated } at the bottom of the method, or else the playground will compile with errors!

The next line contains a boolean check, also associated with the hat:

if hat.isMagical {

You could choose to add a separate guard statement to perform this check, but it would be more clear to group the checks together on a single line for clarity. As such, change the first guard statement to match the following:

guard let hat = hat where hat.isMagical else {
  throw ChangoSpellError.HatMissingOrNotMagical
}

Now remove the if hat.isMagical { check altogether.

In the next section, you’ll continue to unravel the conditional pyramid.

Handling Familiar Errors

Next up, alter the statement that checks if the witch has a familiar:

if let familiar = familiar {

…to instead throw a .NoFamiliar error from another guard statement:

guard let familiar = familiar else {
  throw ChangoSpellError.NoFamiliar
}

Ignore any errors that occur for the moment, as they will disappear with your next code change.

Handling Toad Errors

On the next line, you return the existing toad if the Witch tries to cast the turnFamiliarIntoToad() spell on her unsuspecting amphibian, but an explicit error would better inform her of the mistake. Change the following:

if let toad = familiar as? Toad {
  return toad
}

…to the following:

if familiar is Toad {
  throw ChangoSpellError.FamiliarAlreadyAToad
}

Note the change from as? to is lets you more succinctly check for conformance to the protocol without necessarily needing to use the result. The is keyword can also be used for type comparison in a more general fashion. If you’re interested in learning more about is and as, check out the type casting section of The Swift Programming Language.

Move everything inside the else clause outside of the else clause, and delete the else. It’s no longer necessary!

Handling Spell Errors

Finally, the hasSpellOfType(type:) call ensures that the Witch has the appropriate spell in her spellbook. Change the code below:

if hasSpellOfType(.PrestoChango) {
  if let toad = f as? Toad {
    return toad
  }
}

…to the following:

guard hasSpellOfType(.PrestoChango) else {
  throw ChangoSpellError.SpellNotKnownToWitch
}
 
guard let name = familiar.name else {
  let reason = "Familiar doesn’t have a name."
  throw ChangoSpellError.SpellFailed(reason: reason)
}
 
return Toad(name: name)

And now you can remove the final line of code which was a fail-safe. Remove this line:

return Toad(name: "New Toad")

You now have the following clean and tidy method, ready for use. I’ve provided a few additional comments to the code below, to further explain what the method is doing:

func turnFamiliarIntoToad() throws -> Toad {
 
  // When have you ever seen a Witch perform a spell without her magical hat on ? :]
  guard let hat = hat where hat.isMagical else {
    throw ChangoSpellError.HatMissingOrNotMagical
  }
 
  // Check if witch has a familiar
  guard let familiar = familiar else {
    throw ChangoSpellError.NoFamiliar
  }
 
  // Check if familiar is already a toad - if so, why are you casting the spell?
  if familiar is Toad {
    throw ChangoSpellError.FamiliarAlreadyAToad
  }
  guard hasSpellOfType(.PrestoChango) else {
    throw ChangoSpellError.SpellNotKnownToWitch
  }
 
  // Check if the familiar has a name
  guard let name = familiar.name else {
    let reason = "Familiar doesn’t have a name."
    throw ChangoSpellError.SpellFailed(reason: reason)
  }
 
  // It all checks out! Return a toad with the same name as the witch's familiar
  return Toad(name: name)
}

You simply could have returned an optional from turnFamiliarIntoToad() to indicate that “something went wrong while this spell was being performed”, but using custom errors more clearly expresses the error states and lets you react to them accordingly.

What Else Are Custom Errors Good For?

Now that you have a method to throw custom Swift errors, you need to handle them. The standard mechanism for doing this is called the do-catch statement, which is similar to try-catch mechanisms found in other languages such as Java.

Add the following code to the bottom of your playground:

func exampleOne() {
  print("") // Add an empty line in the debug area
 
  // 1
  let salem = Cat(name: "Salem Saberhagen")
  salem.speak()
 
  // 2
  let witchOne = Witch(name: "Sabrina", familiar: salem)
  do {
    // 3
    try witchOne.turnFamiliarIntoToad()
  }
  // 4
  catch let error as ChangoSpellError {
    handleSpellError(error)
  }
  // 5
  catch {
    print("Something went wrong, are you feeling OK?")
  }
}

Here’s what that function does:

  1. Create the familiar for this witch. It’s a cat called Salem.
  2. Create the witch, called Sabrina.
  3. Attempt to turn the feline into a toad.
  4. Catch a ChangoSpellError error and handle the error appropriately.
  5. Finally, catch all other errors and print out a nice message.

After you add the above, you’ll see a compiler error – time to fix that.

handleSpellError() has not yet been defined, so add the following code above the exampleOne() function definition:

func handleSpellError(error: ChangoSpellError) {
  let prefix = "Spell Failed."
  switch error {
    case .HatMissingOrNotMagical:
      print("\(prefix) Did you forget your hat, or does it need its batteries charged?")
 
    case .FamiliarAlreadyAToad:
      print("\(prefix) Why are you trying to change a Toad into a Toad?")
 
    default:
      print(prefix)
  }
}

Finally, run the code by adding the following to the bottom of your playground:

exampleOne()

Reveal the Debug console by clicking the up arrow icon in the bottom left hand corner of the Xcode workspace so you can see the output from your playground:

Expand Debug Area

Catching Errors

Below is a brief discussion of each of language feature used in the above code snippet.

catch

You can use pattern matching in Swift to handle specific errors or group themes of error types together.

The code above demonstrates several uses of catch: one where you catch a specific ChangoSpell error, and one that handles the remaining error cases.

try

You use try in conjunction with do-catch statements to clearly indicate which line or section of code may throw errors.

You can use try in several different ways, one of which is used above:

  • try: standard usage within a clear and immediate do-catch statement. This is used above.
  • try?: handle an error by essentially ignoring it; if an error is thrown, the result of the statement will be nil.
  • try!: similar to the syntax used for force-unwrapping, this prefix creates the expectation that, in theory, a statement could throw an error, in practice the error condition will never occur. try! can be used for actions such as loading files, where you are certain the required media exists. Like force-unwrap, this construct should be used carefully.

Time to check out a try? statement in action. Cut and paste the following code into the bottom of your playground:

func exampleTwo() {
  print("") // Add an empty line in the debug area
 
  let toad = Toad(name: "Mr. Toad")
  toad.speak()
 
  let hat = Hat()
  let witchTwo = Witch(name: "Elphaba", familiar: toad, hat: hat)
 
  let newToad = try? witchTwo.turnFamiliarIntoToad()
  if newToad != nil { // Same logic as: if let _ = newToad
    print("Successfully changed familiar into toad.")
  }
  else {
    print("Spell failed.")
  }
}

Notice the difference with exampleOne. Here you don’t care about the output of the particular error, but still capture the fact that one occurred. The Toad was not created, so the value of newToad is nil.

Propagating Errors

throws

The throws keyword is required in Swift if a function or method throws an error. Thrown errors are automatically propagated up the call stack, but letting errors bubble too far from their source is considered bad practice. Significant propagation of errors throughout a codebase increases the likelihood errors will escape appropriate handling, so throws is a mandate to ensure propagation is documented in code – and remains evident to the coder.

rethrows

All examples you’ve seen so far have used throws, but what about its counterpart rethrows ?

rethrows tells the compiler that this function will only throw an error when its function parameter throws an error. A quick and magical example can be found below (no need to add this to the playground):

func doSomethingMagical(magicalOperation: () throws -> MagicalResult) rethrows -> MagicalResult {
  return try magicalOperation()
}

Here doSomethingMagical(_:) will only throw an error if the magicalOperation provided to the function throws one. If it succeeds, it returns a MagicalResult instead.

Manipulating Error Handling Behavior

defer

Although auto-propagation will serve you well in most cases, there are situations where you might want to manipulate the behavior of your application as an error travels up the call stack.

The defer statement is a mechanism that permits a ‘cleanup’ action to be performed whenever the current scope is exited, such as when a method or function returns. It’s useful for managing resources that need to be tidied up whether or not the action was successful, and so becomes especially useful in an error handling context.

To see this in action, add the following method to the Witch structure:

func speak() {
  defer {
    print("*cackles*")
  }
  print("Hello my pretties.")
}

Add the following code to the bottom of the playground:

func exampleThree() {
  print("") // Add an empty line in the debug area
 
  let witchThree = Witch(name: "Hermione", familiar: nil, hat: nil)
  witchThree.speak()
}
 
exampleThree()

In the debug console, you should see the witch cackle after everything she says.

Interestingly, defer statements are executed in the opposite order in which they are written.

Add a second defer statement to speak() so that a Witch screeches, then cackles after everything she says:

func speak() {
  defer {
    print("*cackles*")
  }
 
  defer {
    print("*screeches*")
  }
 
  print("Hello my pretties.")
}

Did the statements print in the order you expected? Ah, the magic of defer!

More Fun with Errors

The inclusion of the above statements in Swift brings the language into line with many other popular languages and separates Swift from the NSError-based approach found in Objective-C. Objective-C errors are, for the most part, directly translated, and the static analyzer in the compiler is excellent for helping you with which errors you need to catch, and when.

Although the do-catch and related features have significant overhead in other languages, in Swift they are treated like any other statement. This ensures they remain efficient – and effective.

But just because you can create custom errors and throw them around, doesn’t necessarily mean that you should. You really should develop guidelines regarding when to throw and catch errors for each project you undertake. I’d recommend the following:

  • Ensure error types are clearly named across your codebase.
  • Use optionals where a single error state exists.
  • Use custom errors where more than one error state exists.
  • Don’t allow an error to propagate too far from its source.

The Future of Error Handling in Swift

A couple of ideas for advanced error handling are floating around various Swift forums. One of the most-discussed concepts is untyped propagation.

“…we believe that we can extend our current model to support untyped propagation for universal errors. Doing this well, and in particular doing it without completely sacrificing code size and performance, will take a significant amount of planning and insight. For this reason, it is considered well out of scope for Swift 2.0.”

– from Swift 2.x Error Handling

Whether you enjoy the idea of a major error handling change in Swift 3, or are happy with where things are today, it’s nice to know that clean error handling is being actively discussed and improved as the language develops.

Excellent

Where To Go From Here?

You can download the finished set of playgrounds here for this tutorial.

For further reading, I recommend the following articles, some of which have already been referenced throughout this tutorial:

If you’re keen to see what may lie ahead in Swift 3, I recommend reading through the currently open proposals; see Swift Language Proposals for further details. If you’re feeling adventurous, why not submit your own? :]

Hopefully by now that you’ve been truly enchanted by error handling in Swift. If you have any questions or comments on this tutorial, please join the forum discussion below!

The post Magical Error Handling in Swift appeared first on Ray Wenderlich.


iOS 10 Screencast: Property Animators: Pause, Scrub & Reverse

iOS 10 Screencast: Collaborative Messaging in iOS 10

Advanced Collection Views in OS X Tutorial

$
0
0

Advanced Collection Views in OS X Tutorial

If you want to learn about the advanced capabilities of NSCollectionView, you’ve come to the right place. This is the second part of a tutorial that covered the basics, and in this Advanced Collection Views in OS X Tutorial, you step deeper into the encompassing world of collection views.

In this OS X tutorial, you’ll learn how:

  • To add, remove, move and reorder items
  • To implement drag and drop with collection views
  • To fine-tune selection and highlighting
  • To use animation in collection views
  • To implement sticky section headers

Prerequisites

You need basic knowledge of NSCollectionView, and you’ll need to know your way around the project from the Collection Views tutorial.

Getting Started

SlidesPro is the app you’re going to build, and it picks up where the previous tutorial left off.

Download the SlidesPro starter project here.

Build and run.

SlidesProStarterScreen

Add New Images to the Collection View

In this section, you’ll walk through the steps needed to add new items to the collection.

The Add Button

You’re not going to be able to add anything to that collection view until you make a way to do it. Good thing you’re a developer! What’s needed here is a button that displays a standard open panel from which you can choose images.

Open Main.storyboard and drag a Push Button into the bottom of the collection view. In the Attributes Inspector, set its Title to Add, and uncheck Enabled.

Add Slide Button

Select the Editor \ Resolve Auto Layout Issues \ Add Missing Constraints menu item to set the button’s Auto Layout constraints.

Build and run and check if you’ve got a button.

Add Button Added

Specify Where to Insert New Items

SlidesPro should be set up so that when you select an item, the new item is inserted starting at the index path of whatever image you’ve selected. Then this item and the rest of the section are pushed below the new items.

Accordingly, the add button can only be enabled when an item is selected.

In ViewController, add an IBOutlet for the button:

  @IBOutlet weak var addSlideButton: NSButton!

Next, open Main.storyboard and connect the outlet to the button.

You’ll need to track selection changes that will ultimately enable and disable the button inside of highlightItems(_: atIndexPaths:), a ViewController method. When items are selected or deselected it’s called by the two NSCollectionViewDelegate methods.

To do this, you just need to append one line to highlightItems(_: atIndexPaths:):

  func highlightItems(selected: Bool, atIndexPaths: Set<NSIndexPath>) {
    .......
    .......
    addSlideButton.enabled = collectionView.selectionIndexPaths.count == 1
  }

With this line you enable the button only when one item is selected.
Build and run. Verify that the button is enabled only when an item is selected.
First_Run

Insert the New Items

Adding new items to a collection view is a two-stage process. First, you add the items to the model then notify the collection view about the changes.

Note: When editing operations, such as add, remove and move, you must always update the model before you notify the collection view to tell it to update its layout.

To update your model, you’ll need to append the following to the ImageDirectoryLoader class:

  func insertImage(image: ImageFile, atIndexPath: NSIndexPath) {
    let imageIndexInImageFiles = sectionsAttributesArray[atIndexPath.section].sectionOffset + atIndexPath.item
    imageFiles.insert(image, atIndex: imageIndexInImageFiles)
    let sectionToUpdate = atIndexPath.section
    sectionsAttributesArray[sectionToUpdate].sectionLength += 1
    sectionLengthArray[sectionToUpdate] += 1
    if sectionToUpdate < numberOfSections-1 {
      for i in sectionToUpdate+1...numberOfSections-1 {
        sectionsAttributesArray[i].sectionOffset += 1
      }
    }
  }

This method inserts the new image to your data model and updates everything so that your model stays in a consistent state.

Add the following methods to ViewController. The first method is called from the IBAction method that’s triggered by clicking the add button, and then it’s followed by the action method:

   private func insertAtIndexPathFromURLs(urls: [NSURL], atIndexPath: NSIndexPath) {
    var indexPaths: Set<NSIndexPath> = []
    let section = atIndexPath.section
    var currentItem = atIndexPath.item
 
    // 1
    for url in urls {
      // 2
      let imageFile = ImageFile(url: url)
      let currentIndexPath = NSIndexPath(forItem: currentItem, inSection: section)
      imageDirectoryLoader.insertImage(imageFile, atIndexPath: currentIndexPath)
      indexPaths.insert(currentIndexPath)
      currentItem += 1
    }
 
    // 3
    collectionView.insertItemsAtIndexPaths(indexPaths)
  }
 
  @IBAction func addSlide(sender: NSButton) {
    // 4
    let insertAtIndexPath = collectionView.selectionIndexPaths.first!
    //5
    let openPanel = NSOpenPanel()
    openPanel.canChooseDirectories = false
    openPanel.canChooseFiles = true
    openPanel.allowsMultipleSelection = true;
    openPanel.allowedFileTypes = ["public.image"]
    openPanel.beginSheetModalForWindow(self.view.window!) { (response) -> Void in
      guard response == NSFileHandlingPanelOKButton else {return}
      self.insertAtIndexPathFromURLs(openPanel.URLs, atIndexPath: insertAtIndexPath)
    }
  }
  1. This iterates over the URLs chosen in the Open panel.
  2. For each URL, an ImageFile instance is created and added to the model.
  3. This notifies the collection view.
  4. The NSIndexPath of the selected item defines where the insertion starts.
  5. This creates an NSOpenPanel and configures it to only allow the selection of image files and shows it.

Open Main.storyboard and connect the addSlide(_:) IBAction to the button.

Build and run.

Select the last image in Section 1 — on my system it’s the Desert.jpg slide.

Click the Add button. In the Open panel navigate to the My Private Zoo folder inside the project’s root directory and select all files.

MyPrivateZoo

Click Open. The app will insert the new images in Section 1, starting at item 2, where Desert.jpg was before the insertion.

ZooBeforeAfter

Remove Items from the Collection View

To remove items in SlidesPro you’ll need a remove button, and it should sit next to the add button. The most logical implementation is that it should remove all selected items, hence, this button should be enabled only when one or more items are selected.

And then there’s this detail: multi-selection must be enabled to allow you to work with more than one image at a time.

This section will walk you through adding the button and enabling multi-select.

Enable Multi-Selection

Open Main.storyboard and select the Collection View. In the Attributes Inspector, check Allows Multiple Selection.

MultipleSelection

Build and run and verify that multi-selection works.

To expand or reduce a collection’s selection, press and hold the shift or command key while you click on various items. Multi-selections can reach across sections.

The Remove Button

Open Main.storyboard, and then drag a Push Button from the Object Library and place it to the left of the Add button.

In the Attributes Inspector, set its Title to Remove, and uncheck Enabled.
RemoveButton
Set the button’s Auto Layout constraints by selecting the Editor \ Resolve Auto Layout Issues \ Add Missing Constraints menu item.

Build and run.

RemoveBtnAdded

Add an IBOutlet in ViewController:

  @IBOutlet weak var removeSlideButton: NSButton!

Next, open Main.storyboard and connect the outlet to the button.

In ViewController, at the end of highlightItems(_: atIndexPaths:), add the line to enable/disable the remove button.

  func highlightItems(selected: Bool, atIndexPaths: Set<NSIndexPath>) {
    .......
    .......
    removeSlideButton.enabled = !collectionView.selectionIndexPaths.isEmpty
  }

Build and run, then select an item. Both the add and remove buttons should become enabled. Add more items to the selection; the add button should become disabled while the remove button stays enabled.

Enable Removal of Items

Now you’ll add the code that removes items from the collection. As it is with adding, removing is a two-stage process where you must remove images from the model before notifying the collection view about the changes.

To update the model, add the following method at the end of the ImageDirectoryLoader class:

  func removeImageAtIndexPath(indexPath: NSIndexPath) -> ImageFile {
    let imageIndexInImageFiles = sectionsAttributesArray[indexPath.section].sectionOffset + indexPath.item
    let imageFileRemoved = imageFiles.removeAtIndex(imageIndexInImageFiles)
    let sectionToUpdate = indexPath.section
    sectionsAttributesArray[sectionToUpdate].sectionLength -= 1
    if sectionToUpdate < numberOfSections-1 {
      for i in sectionToUpdate+1...numberOfSections-1 {
        sectionsAttributesArray[i].sectionOffset -= 1
      }
    }
    return imageFileRemoved
  }

In ViewController, add the IBAction method that’s triggered when you click the Remove button:

  @IBAction func removeSlide(sender: NSButton) {
 
    let selectionIndexPaths = collectionView.selectionIndexPaths
    if selectionIndexPaths.isEmpty {
      return
    }
 
    // 1
    var selectionArray = Array(selectionIndexPaths)
    selectionArray.sortInPlace({path1, path2 in return path1.compare(path2) == .OrderedDescending})
    for itemIndexPath in selectionArray {
      // 2
      imageDirectoryLoader.removeImageAtIndexPath(itemIndexPath)
    }
 
    // 3
    collectionView.deleteItemsAtIndexPaths(selectionIndexPaths)
  }

Here’s what happens in there:

  1. Creates an array to iterate over the selection in descending order regarding index paths, so you don’t need to adjust index path values during the iteration
  2. Removes selected items from the model
  3. Notifies the collection view that items have been removed

Now open Main.storyboard and connect the removeSlide(_:) IBAction to the button.

This is how the Connections Inspector of View Controller should look after adding the outlets and actions:

SlidesProActionsOutlets

Build and run.

Select one or more images and click the Remove button to verify that it successfully removes the items.

Drag and Drop in Collection Views

One of the best things about OS X is that you can drag and drop items to move or copy them to different apps. Users expect this behavior, so you’d be wise to add it to anything you decide to put out there.

With SlidesPro, you’ll use drag-and-drop to implement the following capabilities:

  • Move items inside the collection view
  • Drag image files from other apps into the collection view
  • Drag items from the collection view into other apps

To support drag-and-drop, you’ll need to implement the relevant NSCollectionViewDelegate methods, but you have to register the kind of drag-and-drop operations SlidesPro supports.

Add the following method to ViewController:

  func registerForDragAndDrop() {
    // 1
    collectionView.registerForDraggedTypes([NSURLPboardType])
    // 2
    collectionView.setDraggingSourceOperationMask(NSDragOperation.Every, forLocal: true)
    // 3
    collectionView.setDraggingSourceOperationMask(NSDragOperation.Every, forLocal: false)
  }

In here, you’ve:

  1. Registered for the dropped object types SlidesPro accepts
  2. Enabled dragging items within and into the collection view
  3. Enabled dragging items from the collection view to other applications

At the end of viewDidLoad(), add:

    registerForDragAndDrop()

Build and run.

Try to drag an item — the item will not move. Drag an image file from Finder and try to drop it on the collection view…nada.

What about left-clicking for 5 seconds...while kissing my elbow?

I asked you to perform this test so you can see that items aren’t responding to dragging, and nothing related to drag-and-drop works. Why is that? You’ll soon discover.

The first issue is that there needs to be some additional logic to handle the action, so append the following methods to the NSCollectionViewDelegate extension of ViewController:

  // 1
  func collectionView(collectionView: NSCollectionView, canDragItemsAtIndexes indexes: NSIndexSet, withEvent event: NSEvent) -> Bool {
    return true
  }
 
  // 2
  func collectionView(collectionView: NSCollectionView, pasteboardWriterForItemAtIndexPath indexPath: NSIndexPath) -> NSPasteboardWriting? {
    let imageFile = imageDirectoryLoader.imageFileForIndexPath(indexPath)
    return imageFile.url.absoluteURL
  }

Here’s what’s happening in here:

  1. When the collection view is about to start a drag operation, it sends this message to its delegate. The return value indicates whether the collection view is allowed to initiate the drag for the specified index paths. You need to be able to drag any item, so you return unconditionally true.
  2. Implementing this method is essential so the collection view can be a Drag Source. If the method in section one allows the drag to begin, the collection view invokes this method one time per item to be dragged. It requests a pasteboard writer for the item’s underlying model object. The method returns a custom object that implements NSPasteboardWriting; in your case it’s NSURL. Returning nil prevents the drag.

Build and run.

Try to drag an item, the item moves… Hallelujah!

Perhaps I spoke too soon? When you try to drop the item in a different location in the collection view, it just bounces back. Why? Because you did not define the collection view as a Drop Target.

Now try to drag an item and drop it in Finder; a new image file is created matching the source URL. You have made progress because it works to drag-and-drop from SlidesPro to another app!

Define Your Drop Target

Add the following property to ViewController:

  var indexPathsOfItemsBeingDragged: Set<NSIndexPath>!

Add the following methods to the NSCollectionViewDelegate extension of ViewController:

  // 1
  func collectionView(collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAtPoint screenPoint: NSPoint, forItemsAtIndexPaths indexPaths: Set<NSIndexPath>) {
    indexPathsOfItemsBeingDragged = indexPaths
  }
 
  // 2
  func collectionView(collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath
    proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath?>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionViewDropOperation>) -> NSDragOperation {
    // 3
    if proposedDropOperation.memory == NSCollectionViewDropOperation.On {
      proposedDropOperation.memory = NSCollectionViewDropOperation.Before
    }
    // 4
    if indexPathsOfItemsBeingDragged == nil {
      return NSDragOperation.Copy
    } else {
        return NSDragOperation.Move
    }
  }

Here’s a section-by-section breakdown of this code:

  1. An optional method is invoked when the dragging session is imminent. You’ll use this method to save the index paths of the items that are dragged. When this property is not nil, it’s an indication that the Drag Source is the collection view.
  2. Implement the delegation methods related to drop. This method returns the type of operation to perform.
  3. In SlidesPro, the items aren’t able to act as containers; this allows dropping between items but not dropping on them.
  4. When moving items inside the collection view, the operation is Move. When the Dragging Source is another app, the operation is Copy.

Build and run.

Drag an item. After you move it, you’ll see some weird gray rectangle with white text. As you keep moving the item over the other items, the same rectangle appears in the gap between the items.

HeaderAsInterGapIndicator

What is happening?

Inside of ViewController, look at the DataSource method that’s invoked when the collection view asks for a supplementary view:

  func collectionView(collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> NSView {
    let view = collectionView.makeSupplementaryViewOfKind(NSCollectionElementKindSectionHeader, withIdentifier: "HeaderView", forIndexPath: indexPath) as! HeaderView
    view.sectionTitle.stringValue = "Section \(indexPath.section)"
    let numberOfItemsInSection = imageDirectoryLoader.numberOfItemsInSection(indexPath.section)
    view.imageCount.stringValue = "\(numberOfItemsInSection) image files"
    return view
  }

When you start dragging an item, the collection view’s layout asks for the interim gap indicator’s supplementary view. The above DataSource method unconditionally assumes that this is a request for a header view. Accordingly, a header view is returned and displayed for the inter-item gap indicator.

None of this is going to work for you so replace the content of this method with:

  func collectionView(collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> NSView {
    // 1
    let identifier: String = kind == NSCollectionElementKindSectionHeader ? "HeaderView" : ""
    let view = collectionView.makeSupplementaryViewOfKind(kind, withIdentifier: identifier, forIndexPath: indexPath)
    // 2
    if kind == NSCollectionElementKindSectionHeader {
      let headerView = view as! HeaderView
      headerView.sectionTitle.stringValue = "Section \(indexPath.section)"
      let numberOfItemsInSection = imageDirectoryLoader.numberOfItemsInSection(indexPath.section)
      headerView.imageCount.stringValue = "\(numberOfItemsInSection) image files"
    }
    return view
  }

Here’s what you did in here:

  1. You set the identifier according to the kind parameter received. When it isn’t a header view, you set the identifier to the empty String. When you pass to makeSupplementaryViewOfKind an identifier that doesn’t have a matching class or nib, it will return nil. When a nil is returned, the collection view uses its default inter-item gap indicator. When you need to use a custom indicator, you define a nib (as you did for the header) and pass its identifier instead of the empty string.
  2. When it is a header view, you set up its labels as before.
Note: There is a bug in the Swift API regarding the method above, and the makeItemWithIdentifier and makeSupplementaryViewOfKind methods. The return value specified is NSView, but these methods may return nil so the return value should be NSView? — the question mark is part of the value.

Build and run.

Now you see an unmistakable aqua vertical line when you drag an item, indicating the drop target between the items. It’s a sign that the collection view is ready to accept the drop.

InterItemAnimation

Well…it’s sort of ready. When you try to drop the item, it still bounces back because the delegate methods to handle the drop are not in place yet.

Append the following method to ImageDirectoryLoader:

  // 1
  func moveImageFromIndexPath(indexPath: NSIndexPath, toIndexPath: NSIndexPath) {
 
    // 2
    let itemBeingDragged = removeImageAtIndexPath(indexPath)
 
    let destinationIsLower = indexPath.compare(toIndexPath) == .OrderedDescending
    var indexPathOfDestination: NSIndexPath
    if destinationIsLower {
      indexPathOfDestination = toIndexPath
    } else {
      indexPathOfDestination = NSIndexPath(forItem: toIndexPath.item-1, inSection: toIndexPath.section)
    }
    // 3
    insertImage(itemBeingDragged, atIndexPath: indexPathOfDestination)
  }

Here’s what’s going on in there:

  1. Call this method to update the model when items are moved
  2. Remove the dragged item from the model
  3. Reinsert at its new position in the model

Finish things off here by adding the following methods to the NSCollectionViewDelegate extension in ViewController:

  // 1
  func collectionView(collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: NSIndexPath, dropOperation: NSCollectionViewDropOperation) -> Bool {
    if indexPathsOfItemsBeingDragged != nil {
      // 2
      let indexPathOfFirstItemBeingDragged = indexPathsOfItemsBeingDragged.first!
      var toIndexPath: NSIndexPath
      if indexPathOfFirstItemBeingDragged.compare(indexPath) == .OrderedAscending {
        toIndexPath = NSIndexPath(forItem: indexPath.item-1, inSection: indexPath.section)
      } else {
        toIndexPath = NSIndexPath(forItem: indexPath.item, inSection: indexPath.section)
      }
      // 3
      imageDirectoryLoader.moveImageFromIndexPath(indexPathOfFirstItemBeingDragged, toIndexPath: toIndexPath)
      // 4
      collectionView.moveItemAtIndexPath(indexPathOfFirstItemBeingDragged, toIndexPath: toIndexPath)
    } else {
      // 5
      var droppedObjects = Array<NSURL>()
      draggingInfo.enumerateDraggingItemsWithOptions(NSDraggingItemEnumerationOptions.Concurrent, forView: collectionView, classes: [NSURL.self], searchOptions: [NSPasteboardURLReadingFileURLsOnlyKey : NSNumber(bool: true)]) { (draggingItem, idx, stop) in
        if let url = draggingItem.item as? NSURL {
          droppedObjects.append(url)
        }
      }
      // 6
      insertAtIndexPathFromURLs(droppedObjects, atIndexPath: indexPath)
    }
    return true
  }
 
  // 7
  func collectionView(collectionView: NSCollectionView, draggingSession session: NSDraggingSession, endedAtPoint screenPoint: NSPoint, dragOperation operation: NSDragOperation) {
    indexPathsOfItemsBeingDragged = nil
  }

Here’s what happens with these methods:

  1. This is invoked when the user releases the mouse to commit the drop operation.
  2. Then it falls here when it’s a move operation.
  3. It updates the model.
  4. Then it notifies the collection view about the changes.
  5. It falls here to accept a drop from another app.
  6. Calls the same method in ViewController as Add with URLs obtained from the NSDraggingInfo.
  7. Invoked to conclude the drag session. Clears the value of indexPathsOfItemsBeingDragged.

Build and run.

Now it’s possible to move a single item to a different location in the same section. Dragging one or more items from another app should work too.

Fix the UI

The current implementation of drag-and-drop in SlidesPro doesn’t support drop across sections. Also, multi-selection is supported only for a drop outside SlidesPro. To disable in UI, these unsupported capabilities change the else part of the second if statement to:

  func collectionView(collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath
    proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath?>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionViewDropOperation>) -> NSDragOperation {
    if proposedDropOperation.memory == NSCollectionViewDropOperation.On {
      proposedDropOperation.memory = NSCollectionViewDropOperation.Before
    }
    if indexPathsOfItemsBeingDragged == nil {
      return NSDragOperation.Copy
    } else {
      let sectionOfItemBeingDragged = indexPathsOfItemsBeingDragged.first!.section
      // 1
      if let proposedDropsection = proposedDropIndexPath.memory?.section where sectionOfItemBeingDragged == proposedDropsection && indexPathsOfItemsBeingDragged.count == 1 {
        return NSDragOperation.Move
      } else {
        // 2
        return NSDragOperation.None
      }
    }
  }
  1. The drop is enabled only when the source and target sections match and exactly one item is selected.
  2. Otherwise, it prevents the drop by returning .None

Build and run. Try dragging an item from one section to another. The drop indicator does not present itself, meaning a drop is impossible.

Now drag a multi-selection. While inside the bounds of the collection view there is no drop indicator; however, drag it to Finder, and you’ll see that a drop is allowed.

Note: If you tried to drag the selection outside the collection view, you might have noticed a highlight issue. You’ll come back to this in the upcoming section, “More Fun With Selection and Highlighting”.

More Fun With Selection and Highlighting

In the previous section, you noticed an issue with highlighting.

For the sake of sanity in this discussion, the item being moved will be Item-1. After Item-1 lands at a new position it stays highlighted, and the Add and Remove buttons are enabled, but the selection is empty.

To confirm this is true, select any item — Item-2. It highlights as expected, but Item-1 stays highlighted. It should have been deselected and the highlight removed when you selected Item-2.

Click anywhere between the items to deselect everything. Item-2’s highlight goes away, the Add and Remove buttons are disabled, as they should be for no selection, but Item-1 is still highlighted.

Note: The collection view tracks its selection in its selectionIndexPaths property. To debug, you can insert print statements to show the value of this property.

So what’s going wrong here?

Apparently, the collection view successfully deselects Item-1, but the collectionView(_:didDeselectItemsAtIndexPaths: ) delegate method is not called to remove the highlight and disable the buttons.

In NSCollectionView.h, the comments for the above method and its companion for the select action say, “Sent at the end of interactive selection…”. Hence, these notifications are sent only when you select/deselect via UI.

Here’s your answer, Sherlock: The deselection behavior that should occur when you’re moving an item is performed programmatically via the deselectItemsAtIndexPaths(_:) method of NSCollectionView.

You’ll need to override this method.

Go to File \ New \ File… and create a new Cocoa Class by the name CollectionView make it a subclass of NSCollectionView and put it in the Views group.

The template may add a drawRect(_:) — make sure to delete it.

EmptyCollectionView

Add the following method to CollectionView:

  override func deselectItemsAtIndexPaths(indexPaths: Set<NSIndexPath>) {
    super.deselectItemsAtIndexPaths(indexPaths)
    let viewController = delegate as! ViewController
    viewController.highlightItems(false, atIndexPaths: indexPaths)
  }

The method calls its super implementation followed by a call to highlightItems(_:atIndexPaths:) of its delegate, allowing ViewController to highlight/unhighlight items and enable/disable buttons respectively.

Open Main.storyboard and select the Collection View. In the Identity Inspector, change Class to CollectionView.

CollectionViewIB

Build and run.

Move an item inside the collection to a different location. Nothing shows as highlighted and buttons disable as expected. Case closed.

Animation in Collection Views

NSCollectionView, as a subclass of NSView, can perform animations via the animator proxy. It’s as easy as adding a single word in your code before an operation such as removal of items.

At the end of the removeSlide(_:) method in ViewController, replace this:

    collectionView.deleteItemsAtIndexPaths(selectionIndexPaths)

With this:

    collectionView.animator().deleteItemsAtIndexPaths(selectionIndexPaths)

Build and run.

Select several items and click the Remove button. Watch as the items glide to take up their new positions on the screen.

The default duration is a quarter of a second. To experience a really cool and beautiful effect, add a setting for the duration of the animation at a higher value. Place it above the line you just added:

    NSAnimationContext.currentContext().duration = 1.0
    collectionView.animator().deleteItemsAtIndexPaths(selectionIndexPaths)

Build and run, and then remove some items. Cool effect, isn’t it?

You can do the same for insertItemsAtIndexPaths when you’re adding items, as well as for moveItemAtIndexPath when moving an item.

Sticky Headers

When you scroll a collection view with section headers, the first element of a given section that vanishes at the top of the screen is its header.

In this section, you’ll implement Sticky Headers, so the top-most section header will pin itself to the top of the collection view. It will hold its position until the next section header bumps it out of the way.

StickyHeadersScreen

To make this effect reality, you’ll subclass NSCollectionViewFlowLayout.

Go to File \ New \ File… and create a new Cocoa Class named StickyHeadersLayout as a subclass of NSCollectionViewFlowLayout, and put it in the Layout group.

StickyHeaderSkeleton

In ViewController, change the first line of configureCollectionView() to:

    let flowLayout = StickyHeadersLayout()

Now implement sticky headers by adding the following method to the empty body of the StickyHeadersLayout class:

  override func layoutAttributesForElementsInRect(rect: NSRect) -> [NSCollectionViewLayoutAttributes] {
 
    // 1
    var layoutAttributes = super.layoutAttributesForElementsInRect(rect)
 
    // 2
    let sectionsToMoveHeaders = NSMutableIndexSet()
    for attributes in layoutAttributes {
      if attributes.representedElementCategory == .Item {
        sectionsToMoveHeaders.addIndex(attributes.indexPath!.section)
      }
    }
 
    // 3
    for attributes in layoutAttributes {
      if let elementKind = attributes.representedElementKind where elementKind == NSCollectionElementKindSectionHeader {
        sectionsToMoveHeaders.removeIndex(attributes.indexPath!.section)
      }
    }
 
    // 4
    sectionsToMoveHeaders.enumerateIndexesUsingBlock { (index, stop) -> Void in
      let indexPath = NSIndexPath(forItem: 0, inSection: index)
      let attributes = self.layoutAttributesForSupplementaryViewOfKind(NSCollectionElementKindSectionHeader, atIndexPath: indexPath)
      if attributes != nil {
        layoutAttributes.append(attributes!)
      }
    }
 
    for attributes in layoutAttributes {
      // 5
      if let elementKind = attributes.representedElementKind where elementKind == NSCollectionElementKindSectionHeader {
        let section = attributes.indexPath!.section
        let attributesForFirstItemInSection = layoutAttributesForItemAtIndexPath(NSIndexPath(forItem: 0, inSection: section))
        let attributesForLastItemInSection = layoutAttributesForItemAtIndexPath(NSIndexPath(forItem: collectionView!.numberOfItemsInSection(section) - 1, inSection: section))
        var frame = attributes.frame
 
        // 6
        let offset = collectionView!.enclosingScrollView?.documentVisibleRect.origin.y
 
        // 7
        let minY = CGRectGetMinY(attributesForFirstItemInSection!.frame) - frame.height
 
        // 8
        let maxY = CGRectGetMaxY(attributesForLastItemInSection!.frame) - frame.height
 
        // 9
        let y = min(max(offset!, minY), maxY)
 
        // 10
        frame.origin.y = y
        attributes.frame = frame
 
        // 11
        attributes.zIndex = 99
      }
    }
 
    // 12
    return layoutAttributes
  }

Okay, there’s a lot happening in there, but it makes sense when you take it section by section:

  1. The super method returns an array of attributes for the visible elements.
  2. The NSMutableIndexSet first aggregates all the sections that have at least one visible item.
  3. Remove all sections from the set where the header is already in layoutAttributes, leaving only the sections with “Missing Headers” in the set.
  4. Request the attributes for the missing headers and add them to layoutAttributes.
  5. Iterate over layoutAttributes and process only the headers.
  6. Set the coordinate for the top of the visible area, aka scroll offset.
  7. Make it so the header never goes further up than one-header-height above the upper bounds of the first item in the section.
  8. Make it so the header never goes further down than one-header-height above the lower bounds of the last item in the section.
  9. Let’s break this into 2 statements:
    1. maybeY = max(offset!, minY): When the top of the section is above the visible area this pins (or pushes down) the header to the top of the visible area.
    2. y = min(maybeY, maxY): When the space between the bottom of the section to the top of the visible area is less than header height, it shows only the part of the header’s bottom that fits this space.
  10. Update the vertical position of the header.
  11. Make the items “go” under the header.
  12. Return the updated attributes.

Add the following method to StickyHeadersLayout:

  override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
    return true
  }

You always return true because you want the to invalidate the layout as the user scrolls.

Build and run.

Scroll the collection to see your sticky headers in action.

Where To Go From Here

Download the final version of SlidesPro here.

In this Advanced Collection Views in OS X Tutorial you covered a lot of ground! You took the collection view from a rudimentary app to one that features the kinds of bells and whistles any Mac user would expect.

After all your hard work, you’re able to add and remove items, reorder them, and troubleshoot and correct highlighting/selection issues. You took it to the next level by adding in animations and implemented sticky headers to give SlidesPro a very polished look.

Most impressively, you now know how to build a functional, elegant collection view in OS X. Considering that the documentation for these is fairly limited, it’s a great skill to have.

Some of the topics that were not covered neither here nor in the basic tutorial are:

  • Creating custom layouts by subclassing directly NSCollectionViewLayout
  • “Data Source-less” collection views using Cocoa Bindings

One of the top resources recommended at the end of the basic tutorial is this excellent video tutorial series Custom Collection View Layout from Mic Pringle. Although it’s an iOS series, you can find lots of useful information that’s relevant to collection views in OS X as well.

I hope you found this tutorial most helpful! Let’s talk about it in the forums. I look forward to your questions, comments and discoveries!

The post Advanced Collection Views in OS X Tutorial appeared first on Ray Wenderlich.

iOS 10 Screencast: Manipulating Push Notifications with Service Extensions

Upgrading to Swift 3, and Swift Playgrounds – Podcast S06 E04

$
0
0
swift 3

Upgrading to Swift 3 got you confused? We’re here to help!

Join Mic, Jake, and Gemma as they discuss the trials and tribulations of upgrading the Harry’s app to Swift 3, before moving on to chat about Apple’s new Swift Playgrounds app, and the potential for it to become a great learning platform.

[Subscribe in iTunes] [RSS Feed]

Our Sponsors

This episode was brought to you by those kind folks over at Pyze.

The App stores are crowded and very few apps are making money!

Pyze has developed a free mobile intelligence platform that automates app growth so all apps can develop and retain loyal users thru personalized engagement.

Pyze provides behavior-based real-time insights and automates touch points to build meaningful relationships with each user – all without the time, effort and cost required by today’s big data analytics solutions.

Pyze delivers intelligence-driven marketing and recommendations to cultivate loyalty, increase engagement and grow monetization.

If you’ve got an app, you have to try Pyze, its free and can help grow your app.

Use Pyze and start automating your app’s growth for free.

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 Upgrading to Swift 3, and Swift Playgrounds – Podcast S06 E04 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>