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

In-App Purchase Tutorial: Getting Started

$
0
0
Update note: Pietro Rea updated this tutorial for Xcode 10, Swift 4.2 and iOS 11/12. Ray Wenderlich wrote the original.
in-app purchase tutorial

Get started with in-app purchases to grow app revenue!

One of the great things about building iOS apps is that you have lots of choices when it comes to monetizing your app: plain vanilla paid apps, free apps supported by ads, or even apps that support in-app purchases.

An in-app purchase (or IAP) allows developers to charge users for specific functionality or content while using an app. Implementing IAPs is particularly compelling for several reasons:

  • It’s an extra way to earn money, in addition to simply selling the app for a fee upfront. Some users are willing to spend a lot more on extra content or features.
  • An app can be offered for free, which makes it a no-brainer download for most people. Free apps will typically get many more downloads than paid apps. If users enjoy the app, then they can purchase more content or functionality later.
  • You can display advertisements to the user in a free app with an option to remove them by purchasing an IAP.
  • Following the initial release of an app, new paid content can be added to the same app instead of having to develop a brand new app to earn more money.

In this in-app purchase tutorial, you’ll leverage IAPs to unlock extra content embedded in an app. You’ll need to be familiar with basic Swift and iOS programming concepts. If these are unfamiliar topics, then check out our range of Swift tutorials before getting started. You’ll also need a paid developer account, with access to both the iOS Developer Center and App Store Connect.

Getting Started

In this in-app purchase tutorial, you’ll build a small app called “RazeFaces”, which allows users to buy a “RazeFace”, which is a neat illustration commonly used on this site for books and videos.

A typical RazeFace

A typical “RazeFace”

Download the materials using the link at the top and open the starter project in Xcode. Build and run to see what it does so far. The answer is: Not a lot! You’ll see an empty table view, with a single Restore button in the navigation bar, which will be hooked up later to restore purchases.

Upon finishing this tutorial, there will be a list of RazeFaces listed in the table view which you’ll be able to buy. If you delete and reinstall the app, the Restore button will reinstate any previously purchased RazeFaces.

Head over to Xcode to take a quick look at the code. The main view controller is in MasterViewController.swift. This class displays the table view which will contain a list of available IAPs. Purchases are stored as an array of SKProduct objects.

Notice that MasterViewController is using an object called RazeFaceProducts.store of type IAPHelper to do the heavy lifting. Take a look at their respective code files, RazeFaceProducts.swift and IAPHelper.swift.

RazeFaceProducts is a simple struct that contains some information about the products in the app, and IAPHelper does all the important work of talking to StoreKit. The methods are all stubbed out at the moment, but you’ll fill them out in this tutorial to add IAP functionality to the app.

Before writing any code to incorporate IAP, you’ll first need to do some setup in the iOS Developer Center and App Store Connect.

Creating an App ID

First, you need to create an App ID. This will link together your app to your in-app purchaseable products. Login to the Apple Developer Center, then select Certificates, IDs & Profiles.

Next, select Identifiers > App IDs, and click + in the upper right corner to create a new App ID.

Fill out the information for the new App ID. Enter RazeFace IAP Tutorial App for the Name. Choose Explicit App ID and enter a unique Bundle ID. A common practice is to use your domain name in reverse (for example, com.razeware.razefaces). Make note of the Bundle ID as it will be needed in the steps that follow.

Scroll down to the App Services section. Notice that In-App Purchase and GameCenter are enabled by default. Click Continue and then Register and Done.

Congratulations! You have a new App ID! Next, you’ll create a matching app in App Store Connect.

Checking Your Agreements

Before you can add IAPs to an app in iTunes Connect, you must do two things:

  • Make sure you have accepted the latest Apple Development Program License Agreement on developer.apple.com.
  • Make sure you have accepted the latest Paid Applications agreement in the Agreements, Tax, and Billing section in App Store Connect.

If you have not done this, usually iTunes Connect will give you a warning like the following:

Before-01-ITC

If you see something like the above, follow the steps to accept the appropriate agreements.

It’s also good to double check the Agreements, Tax, and Banking section in iTunes Connect:

in-app purchase tutorial

If you see a section entitled Request Contracts containing a row for Paid Applications, then click the Request button. Fill out all the necessary information and submit it. It may take some time for your request to be approved. Sit tight!

Otherwise, if you see Paid Applications listed under Contracts In Effect, then it looks like you’ve already done this step! Nice job!

Note: Apple can take days to approve these IAP-related agreements after you submit them. During this time, you won’t be able to display IAP products in your apps even if you implement everything correctly in code. This is a common source of frustration for folks implementing In-App Purchases for the first time. Hang in there!

Creating an App in iTunes Connect

Now to create the app record itself, click App Store Connect in the upper left corner of the page, then click My Apps.

in-app purchase tutorial

Next, click + in the upper left corner of the page and select New App to add a new app record. Fill out the information as shown here:

in-app purchase tutorial

You won’t be able to use the exact same app Name that you see here, because app names need to be unique across the App Store. Perhaps add your own initials after the example title shown in the screenshot above.

Note: If you are quick in getting to this step, the Bundle ID might not be showing up in the dropdown list. This sometimes takes a while to propagate through Apple’s systems.

Click Create and you’re done!

Creating In-App Purchase Products

When offering IAPs you must first add an entry for each individual purchase within App Store Connect. If you’ve ever listed an app for sale in the store, it’s a similar process and includes things like choosing a pricing tier for the purchase. When the user makes a purchase, the App Store handles the complex process of charging the user and reply with data about such operation.

There are a whole bunch of different types of IAP you can add:

  • Consumable: These can be bought more than once and can be used up. These are a good fit for extra lives, in-game currency, temporary power-ups, and the like.
  • Non-Consumable: Something that you buy once, and expect to have permanently such as extra levels and unlockable content. The RazeFace illustrations from this tutorial fall into this category.
  • Non-Renewing Subscription: Content that’s available for a fixed period of time.
  • Auto-Renewing Subscription: A repeating subscription such as a monthly raywenderlich.com subscription.

You can only offer In-App Purchases for digital items, and not for physical goods or services. For more information about all of this, check out Apple’s full documentation on Creating In-App Purchase Products.

Now, while viewing your app’s entry in App Store Connect, click on the Features tab and then select In-App Purchases. To add a new IAP product, click the + to the right of In-App Purchases.

You’ll see the following dialog appear:

IAP-Type

When a user purchases a RazeFace in your app, you’ll want them to always have access to it, so select Non-Consumable, and click Create.

Next, fill out the details for the IAP as follows:

  • Reference Name: A nickname identifying the IAP within iTunes Connect. This name does not appear anywhere in the app. The title of the RazeFace you’ll be unlocking with this purchase is Swift Shopping, so enter that here.
  • Product ID: This is a unique string identifying the IAP. Usually it’s best to start with the Bundle ID and then append a unique name specific to this purchasable item. For this tutorial, make sure you append swiftshopping, as this will be used later within the app to look up the RazeFace to unlock. For example, you can use: com.theNameYouPickedEarlier.razefaces.swiftshopping.
  • Cleared for Sale: Enables or disables the sale of the IAP. You want to enable it!
  • Price Tier: The cost of the IAP. Choose Tier 1.

Now scroll down to the Localizations section and note that there is a default entry for English (U.S.). Enter “Swift Shopping” for both the Display Name and the Description. Click Save. Great! You’ve created your first IAP product.

Localizations

Note: App Store Connect may complain that you’re missing metadata for your IAP. Before you submit your app for review, you’re required to add a screenshot of the IAP at the bottom of this page. The screenshot is used only for Apple’s review and does not appear in your App Store listing.

There’s one more step required before you can delve into some code. When testing in-app purchases in a development build of an app, Apple provides a test environment which allows you to “purchase” your IAP products without creating financial transactions.

These special test purchases can only be made by a special “Sandbox Tester” user account in App Store Connect. You’re almost at the code, I promise!

Creating a Sandbox User

In App Store Connect, click App Store Connect in the top left corner of the window to get back to the main menu. Select Users and Roles, then click the Sandbox Testers tab. Click + next to the “Tester” title.

Fill out the information and click Save when you’re done. You can make up a first and last name for your test user, but you must use a real email address as Apple will send a verification email to the address. Once you receive that email, be sure to click the link in it to verify your address.

The email address you enter should also NOT already be associated with an Apple ID account. Hint: if you have a gmail account, you can simply use an address alias instead of having to create a brand new account.

Note: Unfortunately, testing a new purchase of a non-consumable IAP requires a new sandbox tester (and email address) each time. Repeated purchases using the same sandbox tester will be treated as restoring an already purchased item, so any code specific to new purchases will not be exercised.

If multiple test runs through new purchase code are necessary and your email provider does not support qualifiers, then consider setting up a consumable IAP just for testing purposes. Delete the app on your device after each test and the purchase of a consumable IAP will be considered a new purchase.

One strategy you could adopt is testing the failure cases as many times as possible before testing the successful case. That way you’ll need to create fewer sandbox testers. In general, remember the rule that once a user (even a sandbox one) has bought a non-consumable IAP, he can’t buy it again, only restore it.

Great — you now have a test user. You can finally implement IAPs in your app!

Project Configuration

For everything to work correctly, it’s really important that the bundle identifier and product identifiers in the app match the ones you just created in the Developer Center and in App Store Connect.

Head over to the starter project in Xcode. Select the RazeFaces project in the Project navigator, then select it again under Targets. Select the General tab, switch your Team to your correct team, and enter the bundle ID you used earlier.

Enter App Id

Next select the Capabilities tab. Scroll down to In-App Purchase and toggle the switch to ON.

Note: If IAP does not show up in the list, make sure that, in the Accounts section of Xcode preferences, you are logged in with the Apple ID you used to create the app ID.

Open RazeFaceProducts.swift. Notice that there is a placeholder reference to the IAP product you created: SwiftShopping. Replace this with the full Product ID that you configured in App Store Connect — for example:

public static let SwiftShopping = "com.theNameYouPickedEarlier.razefaces.swiftshopping"
Note: The list of product identifiers can be pulled from a web server so new IAPs can be added dynamically rather than requiring an app update. This tutorial keeps things simple and uses hard-coded product identifiers.

Listing In-App Purchases

The store property of RazeFaceProducts is an instance of IAPHelper. As mentioned earlier, this object interacts with the StoreKit API to list and perform purchases. Your first task is to update IAPHelper to retrieve a list of IAPs — there’s only one so far — from Apple’s servers.

Open IAPHelper.swift. At the top of the class, add the following private property:

private let productIdentifiers: Set<ProductIdentifier>

Next, add the following to init(productIds:) before the call to super.init():

productIdentifiers = productIds

An IAPHelper instance is created by passing in a set of product identifiers. This is how RazeFaceProducts creates its store instance.

Next, add these other private properties just under the one you added a moment ago:

private var purchasedProductIdentifiers: Set<ProductIdentifier> = []
private var productsRequest: SKProductsRequest?
private var productsRequestCompletionHandler: ProductsRequestCompletionHandler?

purchasedProductIdentifiers tracks which items have been purchased. The other two properties are used by the SKProductsRequest delegate to perform requests to Apple servers.

Next, still in IAPHelper.swift replace the implementation of requestProducts(_:) with the following:

public func requestProducts(completionHandler: @escaping ProductsRequestCompletionHandler) {
  productsRequest?.cancel()
  productsRequestCompletionHandler = completionHandler

  productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
  productsRequest!.delegate = self
  productsRequest!.start()
}

This code saves the user’s completion handler for future execution. It then creates and initiates a request to Apple via an SKProductsRequest object. There’s one problem: the code declares IAPHelper as the request’s delegate, but it doesn’t yet conform to the SKProductsRequestDelegate protocol.

To fix this, add the following extension to the very end of IAPHelper.swift, after the last curly brace:

// MARK: - SKProductsRequestDelegate

extension IAPHelper: SKProductsRequestDelegate {

  public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    print("Loaded list of products...")
    let products = response.products
    productsRequestCompletionHandler?(true, products)
    clearRequestAndHandler()

    for p in products {
      print("Found product: \(p.productIdentifier) \(p.localizedTitle) \(p.price.floatValue)")
    }
  }
  
  public func request(_ request: SKRequest, didFailWithError error: Error) {
    print("Failed to load list of products.")
    print("Error: \(error.localizedDescription)")
    productsRequestCompletionHandler?(false, nil)
    clearRequestAndHandler()
  }

  private func clearRequestAndHandler() {
    productsRequest = nil
    productsRequestCompletionHandler = nil
  }
}

This extension is used to get a list of products, their titles, descriptions and prices from Apple’s servers by implementing the two methods required by the SKProductsRequestDelegate protocol.

productsRequest(_:didReceive:) is called when the list is succesfully retrieved. It receives an array of SKProduct objects and passes them to the previously saved completion handler. The handler reloads the table with new data. If a problem occurs, request(_:didFailWithError:) is called. In either case, when the request finishes, both the request and completion handler are cleared with clearRequestAndHandler().

Build and run. Hooray! A list of products (only one so far) is displayed in the table view! It took some work, but you got there in the end.

Note: You can display IAP products on both the iOS simulator as well as physical iOS devices, but if you want to test buying or restoring purchases, you can only do this on physical devices. More on this in the purchasing section below.

 

RazeFaces - Run 2

Note: If the run was unsuccessful and you didn’t see any products, then there are a number of things to check. This list is courtesy of itsme.manish and abgtan from the forums on earlier versions of this post, plus more tips added over time.
  • Does the project’s Bundle ID match the App ID from the iOS Development Center?
  • Is the full product ID being used when making an SKProductRequest? (Check the productIdentifiers property of RazeFaceProducts.)
  • Is the Paid Applications Contract in effect on iTunes Connect? It can take hours to days for them to go from pending to accepted from them moment you submit them.
  • Have you waited several hours since adding your product to App Store Connect? Product additions may be active immediately or may take some time.
  • Check Apple Developer System Status. Alternatively, try this link. If it doesn’t respond with a status value, then the iTunes sandbox may be down. The status codes are explained in Apple’s Validating Receipts With the App Store documentation.
  • Have IAPs been enabled for the App ID? (Did you select Cleared for Sale earlier?)
  • Have you tried deleting the app from your device and reinstalling it?

Still stuck? As you can see, there’s a lot of setting up to do for IAP. Try this tutorial’s comments for a discussion with other readers.

Purchased Items

You want to be able to determine which items are already purchased. To do this, you’ll use the  purchasedProductIdentifiers property added earlier. If a product identifier is contained in this set, the user has purchased the item. The method for checking this is straightforward.

In IAPHelper.swift, replace the return statement in isProductPurchased(_:) with the following:

return purchasedProductIdentifiers.contains(productIdentifier)

Saving purchase status locally alleviates the need to request such data to Apple’s servers every time the app starts. purchasedProductIdentifiers are saved using UserDefaults.

Still in IAPHelper.swift, replace init(productIds:) with the following:

public init(productIds: Set<ProductIdentifier>) {
  productIdentifiers = productIds
  for productIdentifier in productIds {
    let purchased = UserDefaults.standard.bool(forKey: productIdentifier)
    if purchased {
      purchasedProductIdentifiers.insert(productIdentifier)
      print("Previously purchased: \(productIdentifier)")
    } else {
      print("Not purchased: \(productIdentifier)")
    }
  }
  super.init()
}

For each product identifier, you check whether the value is stored in UserDefaults. If it is, then the identifier is inserted into the purchasedProductIdentifiers set. Later, you’ll add an identifier to the set following a purchase.

Note: User defaults may not be the best place to store information about purchased products in a real application. An owner of a jailbroken device could easily access your app’s UserDefaults plist, and modify it to ‘unlock’ purchases. If this sort of thing concerns you, then it’s worth checking out Apple’s documentation on Validating App Store Receipts — this allows you to verify that a user has made a particular purchase.

Making Purchases (Show Me The Money!)

Knowing what a user has purchased is great, but you still need to be able to make the purchases in the first place! Implementing purchase capability is the logical next step.

Still in IAPHelper.swift, replace buyProduct(_:) with the following:

public func buyProduct(_ product: SKProduct) {
  print("Buying \(product.productIdentifier)...")
  let payment = SKPayment(product: product)
  SKPaymentQueue.default().add(payment)
}

This creates a payment object using an SKProduct (retrieved from the Apple server) to add to a payment queue. The code utilizes a singleton SKPaymentQueue object called default(). Boom! Money in the bank. Or is it? How do you know if the payment went through?

Payment verification is achieved by having the IAPHelper observe transactions happening on the SKPaymentQueue. Before setting up IAPHelper as an SKPaymentQueue transactions observer, the class must conform to the SKPaymentTransactionObserver protocol.

Go to the very bottom (after the last curly brace) of IAPHelper.swift and add the following extension:

// MARK: - SKPaymentTransactionObserver
 
extension IAPHelper: SKPaymentTransactionObserver {
 
  public func paymentQueue(_ queue: SKPaymentQueue, 
                           updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {
      switch transaction.transactionState {
      case .purchased:
        complete(transaction: transaction)
        break
      case .failed:
        fail(transaction: transaction)
        break
      case .restored:
        restore(transaction: transaction)
        break
      case .deferred:
        break
      case .purchasing:
        break
      }
    }
  }
 
  private func complete(transaction: SKPaymentTransaction) {
    print("complete...")
    deliverPurchaseNotificationFor(identifier: transaction.payment.productIdentifier)
    SKPaymentQueue.default().finishTransaction(transaction)
  }
 
  private func restore(transaction: SKPaymentTransaction) {
    guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }
 
    print("restore... \(productIdentifier)")
    deliverPurchaseNotificationFor(identifier: productIdentifier)
    SKPaymentQueue.default().finishTransaction(transaction)
  }
 
  private func fail(transaction: SKPaymentTransaction) {
    print("fail...")
    if let transactionError = transaction.error as NSError?,
      let localizedDescription = transaction.error?.localizedDescription,
        transactionError.code != SKError.paymentCancelled.rawValue {
        print("Transaction Error: \(localizedDescription)")
      }

    SKPaymentQueue.default().finishTransaction(transaction)
  }
 
  private func deliverPurchaseNotificationFor(identifier: String?) {
    guard let identifier = identifier else { return }
 
    purchasedProductIdentifiers.insert(identifier)
    UserDefaults.standard.set(true, forKey: identifier)
    NotificationCenter.default.post(name: .IAPHelperPurchaseNotification, object: identifier)
  }
}

That’s a lot of code! A detailed review is in order. Fortunately, each method is quite short.

paymentQueue(_:updatedTransactions:) is the only method actually required by the protocol. It gets called when one or more transaction states change. This method evaluates the state of each transaction in an array of updated transactions and calls the relevant helper method: complete(transaction:), restore(transaction:) or fail(transaction:).

If the transaction was completed or restored, it adds to the set of purchases and saves the identifier in UserDefaults. It also posts a notification with that transaction so that any interested object in the app can listen for it to do things like update the user interface. Finally, in both the case of success or failure, it marks the transaction as finished.

All that’s left is to hook up IAPHelper as a payment transaction observer. Still in IAPHelper.swift, go back to init(productIds:) and add the following line right after super.init().

SKPaymentQueue.default().add(self)

Making a Sandbox Purchase

Build and run the app — but to test out purchases, you’ll have to run it on a device. The sandbox tester created earlier can be used to perform the purchase without getting charged. If only I could have a sandbox tester to do my grocery shopping :] Here’s how to use the tester account:

Go to your iPhone and make sure you’re logged out of your normal App Store account. To do this, go to the Settings app and tap iTunes & App Store.

Tap your iCloud account name and then tap Sign Out. At this point don’t actually sign in with the sandbox user. You will be prompted to do this once you attempt to buy the IAP back in the sample app.

Connect your device, build and run! You’ll see your product listed in the app. To begin the purchase process, tap the Buy button.

An alert will appear prompting you to log in. Tap Use Existing Apple ID, and enter the login details for the sandbox tester account that you created earlier.

Confirm the purchase by tapping Buy. The alert view shows that the purchase is being made in the sandbox as a reminder that you won’t be charged for it.

Finally, an alert view will appear confirming the purchase was successful. Once the purchase process has been completed, a checkmark appears next to the purchased item. Tap on the purchased item to enjoy your new RazeFace.

Finally you get to see this “Swift Shopping” RazeFace that you’ve been hearing so much about!

Swift Shopping RazeFace

Restoring Purchases

If the user deletes and re-installs the app or installs it on another device, then they need the ability to access previously purchased items. In fact, Apple may reject an app if it cannot restore non-consumable purchases.

As a purchase transaction observer, IAPHelper is already being notified when purchases have been restored. The next step is to react to this notification by restoring the purchases.

Open IAPHelper.swift and scroll to the bottom of the file. In the StoreKit API extension, replace restorePurchases() with the following:

public func restorePurchases() {
  SKPaymentQueue.default().restoreCompletedTransactions()
}

That was almost too easy! You’ve already set the transaction observer and implemented the method to handle restoring transactions in the previous step.

To test this out, after you’ve made a purchase in the previous step, delete the app from your device. Build and run again, then tap Restore on the top right. You should see a checkmark appear next to the previously purchased product.

Payment Permissions

Some devices and accounts may not permit an in-app purchase. This can happen, for example, if parental controls are set to disallow it. Apple requires this situation to be handled gracefully. Not doing so will likely result in an app rejection.

Open IAPHelper.swift again. In the StoreKit API extension, replace the return statement in canMakePayments() with this line:

return SKPaymentQueue.canMakePayments()

Product cells should behave differently depending on the value returned by canMakePayments(). For example, if canMakePayments() returns false, then the Buy button should not be shown and the price should be replaced by “Not Available”.

To accomplish this, open ProductCell.swift and replace the entire implementation of the product property’s didSet handler with the following:

didSet {
  guard let product = product else { return }
 
  textLabel?.text = product.localizedTitle
 
  if RazeFaceProducts.store.isProductPurchased(product.productIdentifier) {
    accessoryType = .checkmark
    accessoryView = nil
    detailTextLabel?.text = ""
  } else if IAPHelper.canMakePayments() {
    ProductCell.priceFormatter.locale = product.priceLocale
    detailTextLabel?.text = ProductCell.priceFormatter.string(from: product.price)
 
    accessoryType = .none
    accessoryView = self.newBuyButton()
  } else {
    detailTextLabel?.text = "Not available"
  }
}

This implementation will display more appropriate information when payments cannot be made with the device. And there you have it — an app with in-app purchase!

Where To Go From Here?

You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial. Feel free to re-use the IAP helper class in your own projects!

The In-App Purchase Video Tutorial Series by Sam Davies covers all of the topics introduced here, but goes to the next level in Part 3 where he talks about validating receipts.

One shortcoming of the sample app is that it doesn’t indicate to the user when it is communicating with Apple. A possible improvement would be to display a spinner or HUD control at appropriate times. This UI enhancement, however, is beyond the scope of this tutorial. For more information on HUD controls, check out Section 3 of The iOS Apprentice.

Apple has a great landing page for in-app purchase: In-App Purchase for Developers. It collects together links to all the relevant documentation and WWDC videos.

IAPs can be an important part of your business model. Use them wisely and be sure to follow the guidelines about restoring purchases and failing gracefully, and you’ll be well on your way to success!

If you have any questions or comments about this in-app purchase tutorial, then please join the forum discussion below!

The post In-App Purchase Tutorial: Getting Started appeared first on Ray Wenderlich.


Android KTX Tutorial: Getting Started

$
0
0

Android KTX Tutorial: Getting Started

Android KTX is one of the new kids in the Android Jetpack family It’s a collection of Kotlin extension functions that will beautify and give a Kotlin-ish touch to existing Android APIs. KTX takes advantage of many of the awesome features in the Kotlin language to make all the old and rusty Java Android APIs feel like brand new.

Android KTX is an effort by the Google Android team to make the Android APIs more pleasant, idiomatic and easy to use for Kotlin developers, without adding any new features to the existing Android APIs. In this journey, you’re going to explore the APIs that Android KTX has to offer you

Why Is Android KTX Useful?

Android KTX simplifies your interaction with Android APIs by using features of the Kotlin language like extension functions, lambdas, and named and default parameters.

As an example, did you know that the class TextUtils has the methods isDigitsOnly and getTrimmedLength? Android KTX helps you discover many features on the Android APIs like these that you did not previously know existed because they were buried under tons of utility classes or static methods. Just by writing a dot after any object, you can now have a concise list of all the operations that you can apply to it, without having to memorize the methods in utility classes or make constant searches on Stack Overflow. This way, you can stand on the shoulders of giants and avoid code duplication.

Android KTX Packages

The KTX toolset is broken up into a number of different packages, so that you can import only the ones you need into your app project:

  • Core KTX: for use with framework APIs such as Toasts, Spans, and Menus
  • Fragment KTX: simplifies Fragment transactions
  • Palette KTX: for working with color palette APIs
  • SQLite KTX: simplifies SQLite database transactions
  • Collections KTX: for use with collection APIs
  • Navigation KTX: for use with Jetpack Navigation

In this tutorial, you’ll focus on the APIs that are wrapped by Core KTX.

Core KTX covers the Android framework APIs that are not tied to a specific component like fragments or SQLite. The following is a list of the packages included in core KTX:

Note: This tutorial assumes you have previous experience with Kotlin and Android. If you are unfamiliar with the language, have a look at this tutorial. If you’re just beginning with Android, check out some of our Getting Started and other Android tutorials.

Getting Started

Collage Droid is the app that you will use to use to see Core KTX in action — it’s an app that generates delightful photo collages. Along the way, you’re going to update it to take advantage of Core KTX goodies.

Download the Collage Droid project using the Download Materials button at the top or bottom of the tutorial. Open the starter project by using Android Studio 3.1.3 or later and selecting Open an existing Android Studio project:

Open an existing Android Studio project

Before going on with the tutorial, take a look at the existing starter code.

Collage Droid Structure

The Kotlin source code packages in the starter project appear as follows:

The Kotlin source files are:

  • MainActivity.kt
    This is the entry point of the app.
  • CollageFragment.kt
    This fragment is in charge of loading each photo template depending on which tab you selected.
  • TemplateType.kt
    An enum class that holds information related to every specific template; like the aspect ratio of the photos and which droid image should be presented by every template.

Now you have x-ray vision into how Collage Droid is structured!

Understanding Key Kotlin Features

Before you get started adding Core KTX code into the starter project, it’s worthwhile to look at how KTX works under the hood.

To enhance the Android APIs, Android KTX takes advantage of many awesome Kotlin features, like extension functions, operator overloading and destructuring declarations.

Note: If some of these topics are unfamiliar to you, take a look at the next few optional sections, which are denoted as “Optional” in the headers. If you are familiar with concepts, then feel free the skip these sections.

Extension Functions Fundamentals (Optional)

Extension functions allow you to add new functionality to an existing class, even without having access to the original class definition or source code.

For example, you already know that the class TextUtils has the method isDigitsOnly. To use it, you have to do something like this:

import android.text.TextUtils

fun test() {
  if (TextUtils.isDigitsOnly("123")) {
    //Do something
  }
}

But it makes more sense if the class String has this method itself, and you can then use it with any String instance, instead of having to call a static TextUtils method, like this:

fun test() {
  if ("123".isDigitsOnly()) { // Much nicer :]
    //Do something
  }
}

How can you achieve that? This is exactly what an extension function does. In another part of your code you define an extension function for the String class:

import android.text.TextUtils

//(1)  (2)   (3)
fun String.isDigitsOnly(): Boolean {
  return TextUtils.isDigitsOnly(this)
}

The following is going on in the extension function definition:

  1. The fun keyword.
  2. Name of the class to add the extension to followed by the dot(.) notation.
  3. The name of your new extension function.
Note: Inside the extension function, this is a reference to the caller object:
"1".isDigitsOnly() // this represents the String instance "1"

You can use an extension function by importing the file in which the extension function is defined.

Operator Overloading (Optional)

Did you know that in Kotlin you can change the behavior of common operators like +, * , [], and more?

Take a look at some niceties that Core KTX offers for you in the Menu class:

import android.view.Menu
import androidx.core.view.get
import androidx.core.view.minusAssign

fun myFunction(menu : Menu) {
  val firstMenuItem = menu[0] //Gets the first menu item.
  menu -= firstMenuItem // Removes the menu item from the menu object.
}

You’re able to use index notation on menu items and use the subtraction assignment operator right on the menu items themselves!

Behind the scenes, this operator overloading is occurring thanks to operator extension functions on the menu class:

import android.view.MenuItem

operator fun Menu.minusAssign(item: MenuItem) {
  return removeItem(item.itemId)
}

operator fun  Menu.get(index: Int): MenuItem {
  return getItem(index)
}

The only difference here is that you have to add the operator keyword before the fun keyword.

Destructuring Declarations (Optional)

Destructuring is a shortcut feature that allows you to break down an object into multiple variables and declare them in just one step.

For example, the Location class is primarily made up of two main variables latitude and longitude. Using destructuring declarations from Core KTX you can access the latitude and longitude as follows:

import androidx.core.location.component1
import androidx.core.location.component2

fun myFunction(location: Location) {
  val (lat, lon) = location
}

You assign (lat, lon) to the Location instance, and Kotlin extracts the parts of the location for you.

Behind the scenes, this is once again working thanks to extension functions:

package androidx.core.location

import android.location.Location

inline operator fun Location.component1() = this.latitude

inline operator fun Location.component2() = this.longitude

The Location class has to operator extension functions component1() and component2() that let you break the Location instance into parts.

The inline keyword is an optimization feature. If you want learn more take a look at here.

Enough with the theory! It’s time for you to start exploring Core KTX! :]

Android KTX

Setting Up Android Core KTX

You’ll improve Collage Droid by adding Core KTX!

To set it up in the project, you only need to include the Gradle dependency. Open the app module build.gradle file and add this line to your dependencies:

implementation "androidx.core:core-ktx:$androidx_version"

Core KTX is part of Android Jetpack, which is specified using an androidx dependency.

Note: As of the writing of this tutorial, the latest version of AndroidX is 1.0.0-beta01. You can find the last version of Android Core KTX here. Each new release comes with release notes for what has changed since the last version.

Also, be aware that to use the 1.0.0-beta01 version, you must target Android API Level 28 and migrate all your support library dependencies to AndroidX. If you just want to try Core KTX without migrating, you can use version 0.3 instead.

Click “Sync Now” to sync your Gradle files.

Awesome! Now you can use Core KTX in your project.

Enhancing Collage Droid

In Collage Droid, every time you tap a tab, the photo template changes; you do this by passing a Bundle object with the type of template that you want to present.

bundleOf

bundleOf is a Core KTX helper function that lets you create a Bundle object from anywhere.

In the CollageFragment.kt file, find the newInstance() function and replace it with this equivalent using bundleOf:

fun newInstance(templateType: TemplateType): CollageFragment {
  val fragment = CollageFragment()

  val bundle = bundleOf(ARG_TEMPLATE_TYPE to templateType.name)
  
  fragment.arguments = bundle
  return fragment
}

You may need to press Option+Return on Mac or Alt+Enter on PC to pull in the function import.

This how the code looked before:

fun newInstance(templateType: TemplateType): CollageFragment {
  val fragment = CollageFragment()
  
  val bundle = Bundle()
  bundle.putString(ARG_TEMPLATE_TYPE, templateType.name)
  
  fragment.arguments = bundle
  return fragment
}

Using bundleOf(), you avoid needing to first create a Bundle object and then making multiple put calls to add data into the Bundle. Instead, with Core KTX you pass one or many key/value arguments as Kotlin Pair objects using the to infix function into bundleOf() all at once.

So Core KTX has made your code more concise and readable with just this first simple change!

drawToBitmap

drawToBitmap() is an extension function on the View class. It converts a view to a Bitmap object; this will be pretty handy for creating the collages.

In the app, when you have completed all the image buckets, a check mark menu appears to let you generate a collage; you can improve the function that is in charge of that.

In CollageFragment, find the function viewToBitmap() and replace it with the KTX version:

private fun viewToBitmap(view: View): Bitmap {
  return view.drawToBitmap()
}

Here is the code prior to switching to KTX:

private fun viewToBitmap(view: View): Bitmap {
  // 1 Create a new bitmap with the view info
  val bitmap = Bitmap.createBitmap(view.width, view.height,
      Bitmap.Config.ARGB_8888)
  // 2 Draw the view pixes into the bitmap
  val canvas = Canvas(bitmap)
  view.draw(canvas)
  return bitmap
}

Using Core KTX, you killed the boilerplate Bitmap creation code and focused on the main goal.

contentValuesOf

Content providers are superb for sharing data between apps. After the app creates the collage, it stores it in the picture directory. To do that it uses a ContentValues object to share the metadata of the photo collage with other apps.

You can use the Core KTX contentValuesOf() function to create ContentValues objects in an easy and compact way.

alerts

In CollageFragment, find the function addImageToGallery and update it with the Core KTX version:

private fun addImageToGallery(cr: ContentResolver, imgType: String, filepath: File): Uri? {
  val currentTime = System.currentTimeMillis()
  val fileString = filepath.toString()

  val values = contentValuesOf(
      MIME_TYPE to "image/$imgType",
      DATE_ADDED to currentTime,
      DATE_TAKEN to currentTime,
      DATA to fileString
  )
  return cr.insert(EXTERNAL_CONTENT_URI, values)
}

Without Core KTX, the code looked as follows:

private fun addImageToGallery(cr: ContentResolver, imgType: String, filepath: File): Uri? {
  val values = ContentValues()
  val currentTime = System.currentTimeMillis()
  val fileString = filepath.toString()

  values.put(MIME_TYPE, "image/$imgType")
  values.put(DATE_ADDED, currentTime)
  values.put(DATE_TAKEN, currentTime)
  values.put(DATA, fileString)
  return cr.insert(EXTERNAL_CONTENT_URI, values)
}

The contentValuesOf() function has a similar syntax to bundleOf(). You pass the metadata values as pair objects rather than having to make a bunch of calls to put() functions.

Getting [] Operator on Menu

As you saw in one of the optional sections above, Kotlin lets you add new behavior to operators. In Core KTX, the Menu class has extended the get operator to give you an array-like syntax to access the menu elements.

In Collage Droid, once you’ve filled in all the image buckets on a tab, the check mark menu option is made visible.

In CollageFragment, search for the function hideMenuItemDone() and update it to a Core KTX version:

private fun hideMenuItemDone(menu: Menu) {
  menuDone = menu[0]
  menuDone.isVisible = false
}

Here is the code without Core KTX:

private fun hideMenuItemDone(menu: Menu) {
  menuDone = menu.getItem(0)
  menuDone.isVisible = false
}

As you can see, the Core KTX version takes full advantage of the Kotlin operator-overloading feature. It’s not a huge change to switch from using the getItem() method, but it makes your code a little more concise and readable.

Collage Droid is now completely using Core KTX!

More Gems of Android Core KTX

You’ve just scratched the surface in this tutorial on the power of Core KTX! This section discusses a few more features of the library.

Collection Extensions on ViewGroup

Core KTX added many goodies to the ViewGroup class to make it more like a collection. This makes a lot of sense because all ViewGroup children like FrameLayout, LinearLayout, RelativeLayout, and others are containers that have their own children views.

Some examples of the collection extensions for ViewGroup in Core KTX are seen in the following function:

fun example(context: Context) {
  val viewGroup = LinearLayout(context)

  val view1 = View(context)
  val view2 = View(context)

  //1 Adding children
  viewGroup += view1
  viewGroup += view2

  //2 
  viewGroup[0] // Will return view1
  viewGroup[1] // Will return view2

  //3 
  view1 in viewGroup // Will return true
  view2 !in viewGroup // Will return false

  //4 
  viewGroup.size // Will return 2

  //5 
  viewGroup.isEmpty() //Will return false
  viewGroup.isNotEmpty() //Will return true

  //6 
  viewGroup.forEach {
    // the variable it will has the actual view index

  }
  viewGroup.forEachIndexed { index, view ->
    // the variable index contains the actual index and view has the actual view object
  }

  for (view in viewGroup) {
    // view is the actual view object
  }

  //7 
  viewGroup -= view1
  viewGroup -= view2

}

The above examples include the following collection-like actions on a ViewGroup:

  1. Adding children
  2. Accessing children using the get operator []
  3. Checking if an element is in the ViewGroup
  4. Checking number of child elements using size
  5. isEmpty() and isNotEmpty()
  6. Looping over children
  7. Removing elements

View

The View class also has useful Core KTX features, as shown in the following example:

import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible

fun example(context: Context) {
  val view = View(context)

  //1
  if (view.isVisible) // Boolean instead of Int
    view.isVisible = false // Instead of View.GONE
  else
    view.isVisible = true // Instead of View.VISIBLE

  if (view.isInvisible) // Boolean instead of Int
    view.isInvisible = false // Instead of View.INVISIBLE
  else
    view.isInvisible = true // Instead of View.VISIBLE

  if (view.isGone) // Boolean instead of Int
    view.isGone = false // Instead of View.GONE
  else
    view.isGone = true // Instead of View.VISIBLE


  //2
  view.updatePadding(
      left = 0, top = 0,
      right = 0, bottom = 0
  )

  view.updatePadding(left = 0) //Just update the left and the others are default values

}

The possibilities for View using Core KTX include:

  1. Checking visibility
  2. Updating padding

Uri

The Uri class has some nice additions in Core KTX:

import androidx.core.net.toFile
import androidx.core.net.toUri

fun example() {

  val string = "https://test.example.com/foo?bar#baz"

  // 1
  val uri = string.toUri() // Uri object from String

  // 2
  val fileFromUri = uri.toFile() // File object from Uri object

  // 3
  val uriFromFile =  fileFromUri.toUri() // Uri object from File object

}

You have helpers to:

  1. Convert a String to a Uri
  2. Get a File object from a Uri

SharedPreferences

Getting an Editor for SharedPreferences can be a pain, and often you might forget to call apply() to persist the changes:

Core KTX has you covered:

import androidx.core.content.edit

fun example(context: Context) {

  val preferences = context.getSharedPreferences("prefs", 0)

  //1
  preferences.edit(commit = true) {
    putString("key1", "value")
    putInt("key2", 1)
  } //2

  //1
  preferences.edit {
    putString("key1", "value")
    putInt("key2", 1)
  } //2
  
}

For SharedPreferences, Core KTX offers:

  1. Calling edit() to get an Editor
  2. Not needing to call apply(), since Core KTX does it for you

Anko vs. KTX

Anko is a Kotlin library from JetBrains. It shares the goals with Android KTX of making your life as an Android developer easier and making your code more readable and pleasant to use. Android KTX and are Anko are similar but not quite the same.

Differences Between Anko and Android KTX

They have common goals but with different scopes in mind. The mission of KTX is to bring idiomatic APIs to Kotlin users. As is mentioned in the repository, KTX does not intend to add new functionality to the existing Android APIs.

Anko has a wider scope, and this gives it the flexibility to innovate and create new features (Anko Layouts and Anko Coroutines are examples).

Deciding When to Use Anko or Android KTX

Generally speaking, Anko and Android KTX cover different parts of the Android API, and there’s no reason you can’t use both in your project. Investigate each one individually and use either as much as you want in your projects!

Where to Go From Here?

You can download the final project with all the Android KTX changes using the Download Materials button at the top and bottom of the tutorial.

Now that you’ve gotten started with Core KTX, you should check out other parts of Android KTX, including its use for Fragments, Palette, SQLite, and Navigation.

I also suggest you to check out some other resources:

Remember that Core KTX is an open-source project and you can contribute to it in many ways: proposing new features, reporting bugs or submitting a pull request.

I hope you enjoyed this introduction to Android KTX. If you have any comments or questions, please join in on the forum discussion below.

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

Screencast: Password Autofill in iOS 12: Strong Passwords and Passcode Autofill

HomeKit Tutorial: Getting Started

$
0
0
Note: This tutorial is up to date for Swift 4.2, Xcode 10 and iOS 12.

HomeKit Tutorial: Getting Started

HomeKit is a library that allows users to use their Apple devices to configure, monitor and control smart-home devices.

The HomeKit framework allows your apps to set up and control your HomeKit accessories. It provides a central repository for configuring these accessories in your home. Configuration is synchronized across all of your apps and devices, and it’s integrated into iOS, watchOS and tvOS. HomeKit even provides you with a way to control accessories or automation using Siri voice commands.

Before you learn how to control accessories from your app, first take a look at how Apple identifies pieces inside HomeKit.

Getting Started

HomeKit has a well-defined structure to enable developer communications with compatible devices.

At the root, HomeKit defines Homes, with one home being designated as the “primary home.” Every home needs to have a unique name, so when you ask Siri to interact with your home, it can easily identify if you mean your primary residence in the city, or your secondary residence such as a beach house or mountain cabin. Unique names are a fixed requirement across each piece of HomeKit.

There are many aspects to a home that HomeKit considers.

Each home can have one or more rooms, and each room can have one or more accessories. An accessory could be anything from a ceiling fan to a toaster.

Accessories can be equipped with one or more services. Think of services as the capabilities of an accessory. For example, a ceiling fan with lights has two services: One for the fan and one for the light.

Finally, each service can have one or more of its own characteristics. A characteristic is a specific trait or value type associated with that service. Your ceiling fan might have a speed characteristic, for example. The light in the ceiling fan might have two characteristics: One for intensity and another for color.

HomeKit-compatible devices can expose these characteristics, and developers can have granular control over how users interact with them.

In this tutorial, you’ll go over each of these pieces in an app that’ll control the On/Off state of a lightbulb.

Use the Download Materials button at the top or bottom of this tutorial to download the starter project. Open the starter project in Xcode.

Note: You may or may not have a HomeKit device available to you. In this tutorial, however, you’ll make use of a simulator provided by Apple to create HomeKit accessories.

Getting the HomeKit Simulator

The easiest way to get the simulator is to follow these steps:

  1. Select the LightSwitch project in the Project navigator and click the Capabilities tab.
  2. In the list that appears, you should see an option for HomeKit with a toggle button set to Off. Enable it to reveal a button labeled Download HomeKit Simulator.
  3. Click the button to open a Browser window, which takes you to your Apple Developer download page.
  4. Search for Hardware IO Tools for Xcode 7.3. Once you’ve found it, download it and open it.
  5. Copy the HomeKit Accessory Simulator app to your Applications folder.

Now, launch HomeKit Accessory Simulator. In the left pane, you should see an empty list of accessories. Click the + at the bottom of the list and choose New Accessory….

HomeKit Simulator

Give the accessory the name Lightbulb; you can leave all the other fields blank. Then, click Finish. The first thing you’ll note is each device is given a unique Setup Code. This code is used to pair and register the device when it’s on your network.

At the moment, your accessory has no service. A sad existence for any accessory, no?

Click the Add Service button at the bottom of the window. In the dialog, click the drop-down selector next to Service. You can see there are a number of predefined categories of services that HomeKit lets you choose from, including a Custom category.

Take a minute to go through them all and, when ready, select Lightbulb. Feel free to give this service a name and click Finish.

HomeKit Simulator configuring a lightbulb

You can see the Lightbulb service already comes with some predefined characteristics. You should see On, Brightness, Hue and Saturation as characteristics of the service. For the purposes of this demo, you can remove the last three characteristics and just focus on the On state of the characteristic.

HomeKit Simulator displaying a configured lightbulb

Congratulations, you’ve just created your first HomeKit accessory!

Creating Homes, Sweet Homes

Head back to the starter project.

Because you’re working with a user’s private data, you’ll ask the user permission to read and/or write that data. Open Info.plist, and add a key named Privacy – HomeKit Usage Description and set its value to This app lets you control your lightbulb.

In order to start interacting with HomeKit, first open HomeViewController.swift. Next, add a reference to the HMHomeManager under the comment as shown below. This object will give you access to all things HomeKit.

// 1. Add the homeManager
let homeManager = HMHomeManager()

Once it’s been added, you can access all the homes known to HomeKit by accessing the homes property on the homeManager. Next, add the following inside viewDidLoad() below the comment.

// 2. Add homes from homeManager
addHomes(homeManager.homes)

Since changes to homes can happen on any device synchronized with your account, you’ll add a delegate to homeManager to get notifications of changes to any homes. First, add the following inside init(coder:) below the comment to assign yourself as the delegate object:

// 3. Add HomeViewController as delegate to homeManager
homeManager.delegate = self

Don’t panic about the error this line of code just generated. It’s because HomeViewController doesn’t conform to HMHomeManagerDelegate yet. You’ll fix that by implementing the optional delegate methods. Add the following extension at the bottom of HomeViewController.swift:

// 4. Implement HMHomeManagerDelegate as extension on HomeViewController
extension HomeViewController: HMHomeManagerDelegate {
  func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
    addHomes(manager.homes)
  }
}

Here’s what this does. First, you conform HomeViewController to HMHomeManagerDelegate. Then, you implement homeManagerDidUpdateHomes(_:) to add all of the homes from HMHomeManager into your homes array by using the helper addHomes(_:).

Not everyone will already have a Home defined in HomeKit, so while you’re here you’ll add the ability to create your own home and give it a Room. Locate newHome(sender:) and add the following code inside below the comment:

// 5. Add new Home + Room

// 1
self.homeManager.addHome(withName: homeName) { [weak self] home, error in
  guard let self = self else {
    return
  }

  // 2
  if let error = error {
    print("Failed to add home: \(error.localizedDescription)")
  }

  // 3
  if let discoveredHome = home {
    discoveredHome.addRoom(withName: roomName) { _, error  in

      // 4
      if let error = error {
        print("Failed to add room: \(error.localizedDescription)")
      } else {
        self.homes.append(discoveredHome)
        self.collectionView?.reloadData()
      }
    }
  }
}

Here’s what this code does:

  1. Call addHome(withName:completionHandler:) passing in the name of the home.
  2. Verify there was no error while adding the home. If there was an error, simply print it out.
  3. If you successfully created an HMHome, add a room to it using the name entered in the dialog.
  4. If you successfully added a room, add the newly created home to the homes array and refresh the collection view.

Build and run the app. After granting the necessary permissions, create a new Home and Room.

Adding Accessories

Adding accessories is a bit different from the way you added homes. Accessories need to be discovered before they can be added to a given Home and Room. So that’s what you’re going to do next: Handling accessory discovery.

First, open AccessoryViewController.swift and add the following properties to the class declaration at the top:

// 1. For discovering new accessories
let browser = HMAccessoryBrowser()
var discoveredAccessories: [HMAccessory] = []

Here, you added an instance of HMAccessoryBrowser as a property to the class to help discover accessories, along with an array of HMAccessory objects, which you’ll use to keep track of all of the discovered accessories.

The AccessoryViewController has a button on the top-right you can tap to start the discovery of new accessories. This button calls back to discoverAccessories(sender:) in your code. Add the following inside discoverAccessories(sender:):

// 2. Start discovery
discoveredAccessories.removeAll()
browser.delegate = self
browser.startSearchingForNewAccessories()
perform(#selector(stopDiscoveringAccessories), with: nil, afterDelay: 10)

Here, you reset the list of discovered accessories and assign AccessoryViewController as a delegate to the HMAccessoryBrowser. Next, you have the browser start the discovery and set a timer to stop searching after 10 seconds.

Now, remember, as a delegate of HMAccessoryBrowser, you’re required to conform to HMAccessoryBrowserDelegate so HomeKit can notify your app when it discovers a new accessory during the search. Fix this issue by adding the following extension to the bottom of AccessoryViewController.swift:

// 3. Have AccessoryViewController implement HMAccessoryBrowserDelegate
extension AccessoryViewController: HMAccessoryBrowserDelegate {
  func accessoryBrowser(_ browser: HMAccessoryBrowser, didFindNewAccessory accessory: HMAccessory) {
    discoveredAccessories.append(accessory)
  }
}

In this app, you’ll just record when a new accessory has been found, and you’ll analyze the results after the timer completes.

After 10 seconds, the timer will call stopDiscoveringAccessories(). This is your chance to see if any accessories were discovered. Add the following code inside stopDiscoveringAccessories() right before the closing bracket:

// 4. Stop discovering
if discoveredAccessories.isEmpty {
  let alert = UIAlertController(
    title: "No Accessories Found",
    message: "No Accessories were found. Make sure your accessory is nearby and on the same network.",
    preferredStyle: .alert)
  alert.addAction(UIAlertAction(title: "Dismiss", style: .default))
  present(alert, animated: true)
} else {
  let homeName = home?.name
  let message = """
                Found a total of \(discoveredAccessories.count) accessories. \
                Add them to your home \(homeName ?? "")?
                """

  let alert = UIAlertController(
    title: "Accessories Found",
    message: message,
    preferredStyle: .alert)
  alert.addAction(UIAlertAction(title: "Cancel", style: .default))
  alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
      self.addAccessories(self.discoveredAccessories)
  })
  present(alert, animated: true)
}

In this code, you first check if the list of discovered accessories is empty. If it is, then you let the user know that no accessories were found. Otherwise, you notify the user how many accessories you want to add to their home and prompt for confirmation. Notice how you call addAccessories(_:) with the discovered accessories as part of the OK button action call.

Note: You’ll just add the accessory to the home. Normally you’d want to add it to a certain HMRoom for better management, but in the interest of time, you’ll just add it to the home.

So now that you’ve added accessories to the Home, you must add the ability to control the On/Off state of the lightbulb.

Start by displaying all known accessories. In loadAccessories(), add the following code before the call to reload the data:

// 5. Load accessories
guard let homeAccessories = home?.accessories else {
  return
}
    
for accessory in homeAccessories {
  if let characteristic = accessory.find(serviceType: HMServiceTypeLightbulb,
                                         characteristicType: HMCharacteristicMetadataFormatBool) {
    accessories.append(accessory)
    accessory.delegate = self
    characteristic.enableNotification(true) { error in
      if error != nil {
        print("Something went wrong when enabling notification for a characteristic.")
      }
    }
  }
}

Here’s what you’re doing in this code:

  • First you sort through all of a home’s accessories to try to find an accessory with a service that matches the HMServiceTypeLightbulb type and has a Boolean (on/off) characteristic format.
  • In the case you found one, you append it to the list of accessories, set AccessoryViewController as the delegate for the accessory and enable notification on that characteristic. That way, you’ll receive a callback when the value of the characteristic changes.

Next, add the following extension at the bottom of AccessoryViewController to implement the HMAccessoryDelegate protocol:

// 6. Have AccessoryViewController implement HMAccessoryDelegate to detect changes in accessory
extension AccessoryViewController: HMAccessoryDelegate {
  func accessory(_ accessory: HMAccessory, service: HMService,
                 didUpdateValueFor characteristic: HMCharacteristic) {
    collectionView?.reloadData()
  }
}

Here, you reload all of the data on the Collection view to reflect the new changes.

Finally, in collectionView(_:didSelectItemAt:), you want to toggle the value of the On/Off state in the lightbulb. The easiest way to do this is to use the asynchronous method writeValue(_:completionHandler:) on the characteristic whose value needs to be changed. Locate collectionView(_:didSelectItemAt:) and add the following code right before the closing bracket:

// 7. Handle touches which toggle the state of the lightbulb
let accessory = accessories[indexPath.row]
guard 
  let characteristic = accessory.find(serviceType: HMServiceTypeLightbulb,
                                      characteristicType: HMCharacteristicMetadataFormatBool) else {
    return
}

let toggleState = (characteristic.value as! Bool) ? false : true
characteristic.writeValue(NSNumber(value: toggleState)) { error in
  if error != nil {
    print("Something went wrong when attempting to update the service characteristic.")
  }
  collectionView.reloadData()
}

In this code, you make sure the item is a lightbulb and has a Bool data format. Then you toggle its state and reload all the data on the Collection view.

Build and run the app. Tap on the Home you added, then select the magnifying glass button. This will start searching for any accessories on the network.

It will find the accessory you created earlier; go ahead and complete the setup process for this.

Toggling the LightBulb accessory On/Off will also toggle the status inside the HomeKit simulator. Give it a try!

And that’s it!

Where to Go From Here?

You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial.

HomeKit is a great library to help you interact with all your HomeKit enabled devices. Although you didn’t cover it in this tutorial, HomeKit gives you the ability to set up scenes and automation, which are groups of actions that affect the accessories in your smart home.

HomeKit also allows you to create action triggers based on calendar values or geolocation. You can view the 2017 WWDC video, “What’s New in Homekit”, showing the very best of HomeKit.

I hope you enjoyed this HomeKit tutorial and feel empowered to start building HomeKit into your own apps! Please join the discussion below about how you can utilize the HomeKit framework.

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

Design Patterns – Podcast S08 E03

$
0
0

Joshua Greene and Jay Strawn, the authors of “Design Patterns by Tutorials”, join us to talk about different Design Patterns and SOLID.

[Subscribe in iTunes] [RSS Feed]

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

Episode Links

Design Patterns

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 Design Patterns – Podcast S08 E03 appeared first on Ray Wenderlich.

Firebase Tutorial: Real-time Chat

$
0
0
Update note: Ryan Ackermann updated this tutorial to iOS 12, Swift 4.2, Xcode 10, MessageKit, and Cloud Firestore. Tom Elliott wrote the original tutorial. You can safely ignore the warnings about updating to Swift 4.2 since MessageKit is not yet updated.

Firebase_Chat2-Wordpress-feature

It seems like every major app out there has a chat feature — and yours should be no different! This Firebase tutorial will show you how.

However, creating a chat tool can seem like a daunting task. There’s no native UIKit controls designed specifically for chat, and you’ll need a server to coordinate the messages and conversations between users.

Fortunately, there are some great frameworks out there to help you: Firebase lets you synchronize real time data without writing a line of server code, while MessageKit gives you a messaging UI that’s on par with the native Messages app.

In this Firebase tutorial, you’ll build RWRC (Ray Wenderlich Relay Chat) — an anonymous chat application. If you’ve used IRC or Slack, this sort of application should already be familiar to you.

Along the way, you’ll learn how to do the following:

  • Set up the Firebase SDK and MessageKit with CocoaPods.
  • Synchronize data in real time with the Cloud Firestore.
  • Authenticate anonymously with Firebase.
  • Leverage MessageKit for a complete chat UI.
  • Create multiple message threads.
  • Use Firebase Storage to send pictures.

Getting Started

Use the Download Materials button at the top or bottom of this tutorial to download the starter project. To get you started the project contains a simple dummy login screen, where the credentials are saved to User Defaults.

The starter project has a few helper classes that handle sending data to Firebase and saving data to User Defaults. Feel free to browse the starter project a bit to get familiar with the code.

In the starter project you’ll find ChannelsViewController.swift which listens to changes in a Firebase Firestore database and updates a table view whenever the user adds a new channel. You’ll build a similar implementation for displaying chat messages instead of channels.

You’ll use CocoaPods to install both the Firebase SDK and MessageKit. If you’re new to CocoaPods, check out our Cocoapods with Swift tutorial to get up and running.

Open Terminal at the project’s folder location and run the following command to install your dependencies:

pod install

This may take a few minutes, but once the packages have installed, open RWRC.xcworkspace in Xcode. Before you can run the app, you’ll need to configure Firebase.

If you’re new to Firebase you’ll need to create an account. Don’t worry — this is easy and totally free.

Note: For a detailed walkthrough on setting up Firebase, see the Getting Started with Firebase tutorial.

Create a Firebase Account

Head to the Firebase signup site, create an account, and then create a new Firebase project.

In Xcode, click on the target and change the Bundle Identifier to any value you like, and select a Team in the Signing section.

Follow Steps 1 and 2 of the instructions to add Firebase to an iOS app, starting here:

Add Firebase to iOS App

Next, enter in the app’s bundle ID into the form, after which you will download and add the GoogleService-Info.plist config file to your project under the Supporting Files group as shown in the Firebase instructions. This .plist file contains the configuration information needed for Firebase integration with your app.

Warning: Do only Steps 1 and 2 of the instructions. The rest is already done in the starter project and your app will crash if you duplicate the steps.

Now build and run the app. You should see the following:

Login Screen

Enabling Anonymous Authentication

Firebase lets users log in through email or social accounts, but it can also authenticate users anonymously, giving you a unique identifier for a user without knowing any information about them.

To set up anonymous authentication, open the Firebase App Dashboard, select the Authentication option on the left, click Set Up Sign-In Method, then select the Anonymous option, switch Enable so that it’s on, then click Save.

Firebase Auth Console

Just like that, you’ve enabled super secret stealth mode! Okay, so it’s really just anonymous authentication, but hey — it’s still cool. :]

Stealth Swift

Super secret stealth mode achieved!

Logging In

Open LoginViewController.swift and add the following underneath import UIKit:

import FirebaseAuth

To log in to chat, the app will need to authenticate using the Firebase authentication service. Add the following code to the bottom of signIn:

Auth.auth().signInAnonymously(completion: nil)

That line of code from the FirebaseAuth framework will post the Notification.Name.AuthStateDidChange notification that AppController is listening for. Once the notification is fired AppController will update the root view controller for you.

Build and run your app, enter a display name and tap Get Started.

Empty Channel List

Once the user signs in, they navigate to the ChannelsViewController, whose job it is to show the user a list of current channels and allow creating new channels. The table has a single section to display all available channels. There is a toolbar at the bottom with a sign out button, a label displaying your name, and an add button.

Firebase Data Structure

Before you dive into sending messages in realtime, take a moment and think about the data structure first.

Cloud Firestore is a NoSQL JSON data store. Essentially, everything in the Cloud Firestore is a JSON object, and each key of this JSON object has its own URL.

Here’s a sample of how your data could look as a JSON object:

{
  "channels": [{
    "MOuL1sdbrnh0x1zGuXn7": { // channel id
      "name": "Puppies",
      "thread": [{
        "3a6Fo5rrUcBqhUJcLsP0": { // message id
          "content": "Wow, that's so cute!",
          "created": "May 12, 2018 at 10:44:11 PM UTC-5",
          "senderID": "YCrPJF3shzWSHagmr0Zl2WZFBgT2",
          "senderName": "naturaln0va",
        },
        "4LXlVnWnoqyZEuKiiubh": { // message id
          "content": "Yes he is.",
          "created": "May 12, 2018 at 10:40:05 PM UTC-5",
          "senderID": "f84PFeGl2yaqUDaSiTVeqe9gHfD3",
          "senderName": "lumberjack16",
        },
      }]
    },
  }]
}

Cloud Firestore favors a denormalized data structure, so it’s okay to include senderId and senderName for each message item. A denormalized data structure means you’ll duplicate a lot of data, but the upside is faster data retrieval. Tradeoffs — we haz them! :]

Chat Interface Setup

MessageKit is a souped-up UICollectionViewController that’s customized for chat, so you don’t have to create your own! :]

In this section of the tutorial, you’ll focus on four things:

  1. Handling input from the message bar.
  2. Creating message data.
  3. Styling message bubbles.
  4. Removing avatar support.

Almost everything you’ll need to do requires that you override methods. MessageKit provides the MessagesDisplayDelegate, MessagesLayoutDelegate, and MessagesDataSource protocols, so you only need to override the default implementations.

Note: For more information on customizing and working with MessagesViewController, check out the full the documentation here.

Open ChatViewController.swift and, at the top of ChatViewController, define the following properties:

private var messages: [Message] = []
private var messageListener: ListenerRegistration?

These properties are similar to those added to the channels view controller. The messages array is the data model and the listener handles clean up.

Now you can start configuring the data source. Above the MessageInputBarDelegate section, add the following:

// MARK: - MessagesDataSource

extension ChatViewController: MessagesDataSource {

  // 1
  func currentSender() -> Sender {
    return Sender(id: user.uid, displayName: AppSettings.displayName)
  }

  // 2
  func numberOfMessages(in messagesCollectionView: MessagesCollectionView) -> Int {
    return messages.count
  }

  // 3
  func messageForItem(at indexPath: IndexPath, 
    in messagesCollectionView: MessagesCollectionView) -> MessageType {

    return messages[indexPath.section]
  }

  // 4
  func cellTopLabelAttributedText(for message: MessageType, 
    at indexPath: IndexPath) -> NSAttributedString? {

    let name = message.sender.displayName
    return NSAttributedString(
      string: name,
      attributes: [
        .font: UIFont.preferredFont(forTextStyle: .caption1),
        .foregroundColor: UIColor(white: 0.3, alpha: 1)
      ]
    )
  }
}

There’s a bit going on here:

  1. A sender is a simple struct that has an id and name property. You create an instance of a sender from the anonymous Firebase user id and the chosen display name.
  2. The number of messages in the collection view will be equal to the local array of messages.
  3. Your Message model object conforms to MessageType so you can just return the message for the given index path.
  4. The last method returns the attributed text for the name above each message bubble. You can modify the text you’re returning here to your liking, but these are some good defaults.

Build and run the app, add a channel named Cooking and then navigate to it. It should now look like:

Empty Message Thread

So far, so good. Next, you’ll need to implement a few more delegates before you start sending messages.

Setting Up the Display and Layout Delegates

Now that you’ve seen your new awesome chat UI, you probably want to start displaying messages. But before you do that, you have to take care of a few more things.

First, you’ll fine tune some layout parameters from the MessagesLayoutDelegate. Add the following section below the MessagesDisplayDelegate section:

// MARK: - MessagesLayoutDelegate

extension ChatViewController: MessagesLayoutDelegate {

  func avatarSize(for message: MessageType, at indexPath: IndexPath, 
    in messagesCollectionView: MessagesCollectionView) -> CGSize {

    // 1
    return .zero
  }

  func footerViewSize(for message: MessageType, at indexPath: IndexPath, 
    in messagesCollectionView: MessagesCollectionView) -> CGSize {

    // 2
    return CGSize(width: 0, height: 8)
  }

  func heightForLocation(message: MessageType, at indexPath: IndexPath, 
    with maxWidth: CGFloat, in messagesCollectionView: MessagesCollectionView) -> CGFloat {

    // 3
    return 0
  }
}

Here’s the break down:

  1. Returning zero for the avatar size will hide it from the view.
  2. Adding a little padding on the bottom of each message will help the readability of the chat.
  3. At the time of writing, MessageKit doesn’t have a default implementation for the height of a location message. Since you won’t be sending a location message in this tutorial, return zero as the default.

The messages displayed in the collection view are simply images with text overlaid. There are two types of messages: outgoing and incoming. Outgoing messages are displayed to the right and incoming messages on the left.

In ChatViewController, replace the MessagesDisplayDelegate extension with the following:

extension ChatViewController: MessagesDisplayDelegate {
  
  func backgroundColor(for message: MessageType, at indexPath: IndexPath, 
    in messagesCollectionView: MessagesCollectionView) -> UIColor {
    
    // 1
    return isFromCurrentSender(message: message) ? .primary : .incomingMessage
  }

  func shouldDisplayHeader(for message: MessageType, at indexPath: IndexPath, 
    in messagesCollectionView: MessagesCollectionView) -> Bool {

    // 2
    return false
  }

  func messageStyle(for message: MessageType, at indexPath: IndexPath, 
    in messagesCollectionView: MessagesCollectionView) -> MessageStyle {

    let corner: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft

    // 3
    return .bubbleTail(corner, .curved)
  }
}

Taking the above code step-by-step:

  1. For the given message, you check and see if it’s from the current sender. If it is, you return the app’s primary green color; if not, you return a muted gray color. MessageKit uses this color when creating the background image for the message.
  2. You return false to remove the header from each message. You can use this to display thread specific information, such as a timestamp.
  3. Finally, based on who sent the message, you choose a corner for the tail of the message bubble.

To tie this all together, add the following to the bottom of viewDidLoad():

messageInputBar.delegate = self
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self

Check that your app builds and you can navigate to one of your channels

Another Empty Thread

Believe it or not, that’s all it takes to configure a MessagesViewController subclass to display messages! Well, it would be more exciting to see some messages, wouldn’t it?

Time to get this conversation started!

Creating Messages

Create the following method below viewDidLoad() in ChatViewController:

// MARK: - Helpers

private func insertNewMessage(_ message: Message) {
  guard !messages.contains(message) else {
    return
  }
  
  messages.append(message)
  messages.sort()
  
  let isLatestMessage = messages.index(of: message) == (messages.count - 1)
  let shouldScrollToBottom = messagesCollectionView.isAtBottom && isLatestMessage
  
  messagesCollectionView.reloadData()
  
  if shouldScrollToBottom {
    DispatchQueue.main.async {
      self.messagesCollectionView.scrollToBottom(animated: true)
    }
  }
}

This helper method is similar to the one that’s in ChannelsViewController. It makes sure the messages array doesn’t already contain the message, then adds it to the collection view. Then, if the new message is the latest and the collection view is at the bottom, scroll to reveal the new message.

Add a test message by overriding viewDidAppear(_:):

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
  
  let testMessage = Message(user: user, content: "I love pizza, what is your favorite kind?")
  insertNewMessage(testMessage)
}

Build and run the app; you’ll see your message appear in the conversation view:

Test Message

Boom — that’s one nice looking chat app! Time to make it work (for real) with Firebase.

Sending Messages

First, delete viewDidAppear(_:) to remove the test message in ChatViewController and add the following properties at the top of the file:

private let db = Firestore.firestore()
private var reference: CollectionReference?

At the top of viewDidLoad add the following:

guard let id = channel.id else {
  navigationController?.popViewController(animated: true)
  return
}

reference = db.collection(["channels", id, "thread"].joined(separator: "/"))

The reference property is the point in the database where the messages are stored. The id property on the channel is optional because you might not yet have synced the channel. If the channel doesn’t exist in Firestore yet messages cannot be sent, so returning to the channel list makes the most sense.

Next add the following method to the top of the Helpers section:

private func save(_ message: Message) {
  reference?.addDocument(data: message.representation) { error in
    if let e = error {
      print("Error sending message: \(e.localizedDescription)")
      return
    }
    
    self.messagesCollectionView.scrollToBottom()
  }
}

This method uses the reference that was just setup. The addDocument method on the reference takes a dictionary with the keys and values that represent that data. The message data struct implements DatabaseRepresentation, which defines a dictionary property to fill out.

Open Message.swift and examine the implementation of DatabaseRepresentation. As you can see, it maps its properties to readable keys and only sets the content of the message if there is no download URL.

Back in ChatViewController.swift, add the following delegate method inside the MessageInputBarDelegate extension:

func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {

  // 1
  let message = Message(user: user, content: text)

  // 2
  save(message)

  // 3
  inputBar.inputTextView.text = ""
}

Here’s what’s going on:

  1. Create a message from the contents of the message bar and the current user.
  2. Save the message to Cloud Firestore using save(_:).
  3. Clear the message bar’s input field after you send the message.

Build and run; open up your Firebase App Dashboard and click on the Database tab. Select a channel, then send a message in the app and you should see the messages appear in the dashboard in real time:

Note: The first time you view the database on the console, it will prompt you to select a database type. For this tutorial, you’re using Cloud Firestore. After clicking the Create Database button, select the test mode option. For a real world setup, you’ll want to configure security rules for Firestore. You can read more about security rules here.

Firebase Messaging

High five! You’re saving messages to Cloud Firestore like a pro. The messages don’t appear on the screen, but you’ll take care of that next.

Synchronizing the Data Source

Add the following to below insertNewMessage(_:) in ChatViewController:

private func handleDocumentChange(_ change: DocumentChange) {
  guard let message = Message(document: change.document) else {
    return
  }

  switch change.type {
  case .added:
    insertNewMessage(message)

  default:
    break
  }
}

This is very similar to how ChannelsViewController observes new database changes. For brevity, the only change type you handle in the switch statement is add.

Next, add the following code below the reference initialization in viewDidLoad():

messageListener = reference?.addSnapshotListener { querySnapshot, error in
  guard let snapshot = querySnapshot else {
    print("Error listening for channel updates: \(error?.localizedDescription ?? "No error")")
    return
  }
  
  snapshot.documentChanges.forEach { change in
    self.handleDocumentChange(change)
  }
}

Firestore calls this snapshot listener whenever there is a change to the database.

To clean things up add a deinit towards the top of the file:

deinit {
  messageListener?.remove()
}

Build and run your app; you should see any messages sent earlier along with any new ones you enter:

Previous Messages

Congrats! You have a real time chat app! Now it’s time to add one final finishing touch.

Sending Images

To send images, you’re going to follow mostly the same principle as sending text with one key difference. Rather than storing the image data directly with the message, you’ll use Firebase Storage, which is better suited to storing large files like audio, video or images.

To start, you need to add the Photos import to ChatViewController.swift:

import Photos

Add the following above the Helpers section:

// MARK: - Actions

@objc private func cameraButtonPressed() {
  let picker = UIImagePickerController()
  picker.delegate = self

  if UIImagePickerController.isSourceTypeAvailable(.camera) {
    picker.sourceType = .camera
  } else {
    picker.sourceType = .photoLibrary
  }

  present(picker, animated: true, completion: nil)
}

This method will present an image picker controller to allow the user to select an image.

Next, add the following to viewDidLoad():

// 1
let cameraItem = InputBarButtonItem(type: .system)
cameraItem.tintColor = .primary
cameraItem.image = #imageLiteral(resourceName: "camera")

// 2
cameraItem.addTarget(
  self,
  action: #selector(cameraButtonPressed),
  for: .primaryActionTriggered
)
cameraItem.setSize(CGSize(width: 60, height: 30), animated: false)

messageInputBar.leftStackView.alignment = .center
messageInputBar.setLeftStackViewWidthConstant(to: 50, animated: false)

// 3
messageInputBar.setStackViewItems([cameraItem], forStack: .left, animated: false)

Going through this:

  1. Create a new InputBarButtonItem with a tint color and an image.
  2. Connect the new button to cameraButtonPressed().
  3. Lastly, add the item to the left side of the message bar.

Sending a photo message is a little different then sending a plain text message. Saving a photo to Firebase Storage returns a URL, but this may take a couple of seconds — perhaps longer, if the network connection is poor. Rather than blocking the user interface during this time, which will make your app feel slow, you’ll start sending the message and disable the camera message bar item.

Add the following properties at the top of ChatViewController:

private var isSendingPhoto = false {
  didSet {
    DispatchQueue.main.async {
      self.messageInputBar.leftStackViewItems.forEach { item in
        item.isEnabled = !self.isSendingPhoto
      }
    }
  }
}

private let storage = Storage.storage().reference()

and add this method to the bottom of the Helpers section:

private func uploadImage(_ image: UIImage, to channel: Channel, completion: @escaping (URL?) -> Void) {
  guard let channelID = channel.id else {
    completion(nil)
    return
  }
  
  guard let scaledImage = image.scaledToSafeUploadSize,
    let data = scaledImage.jpegData(compressionQuality: 0.4) else {
    completion(nil)
    return
  }
  
  let metadata = StorageMetadata()
  metadata.contentType = "image/jpeg"
  
  let imageName = [UUID().uuidString, String(Date().timeIntervalSince1970)].joined()
  storage.child(channelID).child(imageName).putData(data, metadata: metadata) { meta, error in
    completion(meta?.downloadURL())
  }
}

The isSendingPhoto property takes care of updating the camera item when it changes and the storage property is a reference to the root of Firebase Storage. uploadImage(_:to:completion:) uploads an image to the specified channel in the Firebase Storage.

Below uploadImage(_:to:completion:), add:

private func sendPhoto(_ image: UIImage) {
  isSendingPhoto = true
  
  uploadImage(image, to: channel) { [weak self] url in
    guard let `self` = self else {
      return
    }
    self.isSendingPhoto = false
    
    guard let url = url else {
      return
    }
    
    var message = Message(user: self.user, image: image)
    message.downloadURL = url
    
    self.save(message)
    self.messagesCollectionView.scrollToBottom()
  }
}

This method takes care of updating the isSendingPhoto property to update the UI. Once the photo upload completes and the URL to that photo is returned, save a new message with that photo URL to the database.

Next, to use sendPhoto(_:), add the following image picker delegate methods to the UIImagePickerControllerDelegate extension:

func imagePickerController(_ picker: UIImagePickerController, 
                           didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
  picker.dismiss(animated: true, completion: nil)
  
  // 1
  if let asset = info[.phAsset] as? PHAsset {
    let size = CGSize(width: 500, height: 500)
    PHImageManager.default().requestImage(
      for: asset,
      targetSize: size,
      contentMode: .aspectFit,
      options: nil) { result, info in
        
      guard let image = result else {
        return
      }
      
      self.sendPhoto(image)
    }

  // 2
  } else if let image = info[.originalImage] as? UIImage {
    sendPhoto(image)
  }
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
  picker.dismiss(animated: true, completion: nil)
}

These two methods handle the cases when the user either selects an image or cancels the selection process. When selecting an image, the user can either get one from the photo library or take an image directly with the camera.

Here’s what this does:

  1. If the user selected an asset, the selected image needs to be downloaded from iCloud. Request it at a fixed size. Once it’s successfully retrieved, send it.
  2. If there is an original image in the info dictionary, send that. You don’t need to worry about the original image being too large here because the storage helper handles resizing the image for you. Have a look at UIImage+Additions.swift to see how the resizing is done.

Nearly there! You’ve now set up your app to save the image data to Firebase Storage and save the URL to the message data, but you’ve not yet updated the app to display those photos. Time to fix that.

Get started by adding the following to the bottom of the Helpers section:

private func downloadImage(at url: URL, completion: @escaping (UIImage?) -> Void) {
  let ref = Storage.storage().reference(forURL: url.absoluteString)
  let megaByte = Int64(1 * 1024 * 1024)
  
  ref.getData(maxSize: megaByte) { data, error in
    guard let imageData = data else {
      completion(nil)
      return
    }
    
    completion(UIImage(data: imageData))
  }
}

This method asynchronously downloads an image at the specified path from Firebase Storage.

Next, change the guard statement from a constant to a variable in the handleDocumentChange(_:) method:

guard var message = Message(document: change.document) else {
  return
}

Then, in handleDocumentChange(_:), replace the content of the .added case with the following:

if let url = message.downloadURL {
  downloadImage(at: url) { [weak self] image in
    guard let self = self else {
      return
    }
    guard let image = image else {
      return
    }
    
    message.image = image
    self.insertNewMessage(message)
  }
} else {
  insertNewMessage(message)
}
Note: You’ll need to open the Firebase Console and enable Storage. To do this, first select storage on the left, click Get Started, then choose default security rules.

Build and run the app; tap on the little camera icon and send a photo message in your chat. Notice how the camera icon is disabled when your app is saving the photo data to Firebase Storage.

Photo Message

Kaboom! You just made a big, bad, real time, photo and text sending chat app. Go grab yourself your favorite beverage, you earned it!

Where to Go From Here?

Use the Download Materials button at the top or bottom of this tutorial to download the completed project.

You now know the basics of Cloud Firestore and MessageKit, but there’s plenty more you can do, including one-to-one messaging, social authentication, and avatar display.

To take this app even further, you could take a look at the Firebase iOS documentation. You can also take a look at our 22 part video course on Beginning Firebase!

I hope you’ve enjoyed this Firebase tutorial; if you have any questions feel free to leave them in the non-anonymous yet avatar-enabled discussion below! :]

The post Firebase Tutorial: Real-time Chat appeared first on Ray Wenderlich.

Open Call for Applications on the Unity, Unreal Engine, and Android Teams!

$
0
0

The Tutorial Team: A Happy Cycle!

The Tutorial Team: A Happy Cycle!


Do you like making games with Unity or Unreal Engine? Are you an experienced Android developer?

If you answered yes to either of these questions, then you may be exactly who need on our Unity, Unreal Engine and Android tutorial teams!

We’re looking for authors and technical editors for our Unity and Unreal Engine teams, and we’re looking for tech editors for our Android team.

Keep reading to find out what’s involved with each position, and how to apply!

Note: This is not a full time job; these are part-time, informal paid contracting positions that you can do in the evenings and weekends.

Why Join Our Team?

Here are the top 5 reasons to join the Android, Unity or Unreal Engine Team:

  1. Learning: You’ll always be learning something new — and you’ll have fun doing it! You’ll become a better developer and writer. But the best part is that you’ll make a lot of new friends who are also passionate about Android, Unity or Unreal Engine along the way.
  2. Money: Get paid to learn! We offer the highest rates in the industry.
  3. Special Opportunities: Members of the Tutorial Team get access to special opportunities such as contributing to our books and products, speaking at our conference, being a guest on our podcast, working on team projects and much more. Did we mention books? That’s right, we’re thinking of making a book on Unreal Engine, and you could be a part of it!
  4. Make a Difference: We get emails every day about how our tutorials help our readers make their first app, get their dream job or accomplish a lifelong dream of making their own game. This means a lot to us, and it makes all the hard work worth it!
  5. Free Stuff: As a final bonus, by joining the Tutorial Team you’ll get a free copy of all of the products we sell on the site — over $1,000 in value!

Aww Yeah!

Requirements and How to Apply

Here are the requirements:

  • You must be an experienced Android, Unity or Unreal Engine developer.
  • You should be a good writer with fluent English writing skills.
  • You should be comfortable learning about brand new topics that you’ve never done before; some of which may be poorly documented or not documented at all.
  • You should have a strong work ethic. This will be a significant time commitment and you must be able to deliver on deadline.

To apply, send me an email, and be sure to include the following information:

  • Do you want to join the Android, Unity or Unreal Engine team? Why do you want to be an Android tech editor, or Unreal Engine or Unity author or tech editor? Please specify which team and role you prefer.
  • Please tell me a little bit about yourself and your experience.
  • What is the best game you’ve made or worked on using Android, Unity or Unreal Engine, and why are you proud of the work you did on this game? [Please include link]
  • Please link to any examples of technical writing you’ve done in the past or books you’ve written.
  • Please include links to your StackOverFlow account, Twitter account, your GitHub account, your Unreal Engine AnswerHub profile, your Unreal Engine Community Forum account, or your Unity Answers/Forums Account (if you have one).

If you’re applying to be an author, you’ll be required to write a sample tutorial to gauge your writing skills. If you’re applying to be a tech editor, you’ll be given a tech editor tryout so we can gauge your editing skills. If you pass the tryout, you’re in!

Now’s The Time!

Don’t sit on the sidelines wishing you had applied — try out for the team. Come share your knowledge and be part of our growing Android, Unity and Unreal Engine community!

What are you waiting for? Send me that email and let’s get a conversation going! :]

The post Open Call for Applications on the Unity, Unreal Engine, and Android Teams! appeared first on Ray Wenderlich.

Kotlin Cheat Sheet and Quick Reference

$
0
0

Kotlin Cheat Sheet and Quick Reference

Kotlin is a modern and powerful language, but getting started with it can be a challenge. When you’re getting going it can be difficult remembering the syntax for common operations, so we’ve put together a downloadable PDF Kotlin cheat sheet and quick reference.

This includes some examples that will enable you to quickly get up to speed with writing Kotlin if it’s the first time you’ve written it, or you’re just a little rusty.

Without further ado, here is the download link:

The cheat sheet summarizes the most important Kotlin syntax on two pages to get you up to speed quickly, including:

  • Declaring Variables
  • Nullable Types
  • Control Flow
  • Collections
  • Functions and Lambdas
  • Classes and Objects
  • Data, Enum, and Sealed Classes

Hopefully, this free PDF will be useful as you start writing your first Android apps. If you’d like to learn more about Android and Kotlin development, please check out our Android tutorials, screencasts, and video courses!

The post Kotlin Cheat Sheet and Quick Reference appeared first on Ray Wenderlich.


Your First iOS App · Introduction to Auto Layout

$
0
0
Learn how to use Auto Layout, a core UIKit technology that makes it easy to support many different screen sizes.

Your First iOS App · Challenge: Making a Programming To-Do List

$
0
0
Make a programming to-do list of all the things you'll need to do to build the game. This helps build a good programming practice of gathering requirements first!

Your First iOS App · Challenge: Basic Controls

$
0
0
Practice adding basic controls like sliders, buttons, and labels into your app by creating the basic layout for the Bulls-eye game.

Your First iOS App · Challenge: Connecting Actions

$
0
0
Practice connecting an action performs on a button - such as a tap - to some Swift code that you write.

Updated Course: Your First iOS App

$
0
0
If you’re a complete beginner to iOS development and wondering how to get started, this is the course for you!

In-App Purchase Tutorial: Getting Started

$
0
0
Learn how to grow app revenue in this in-app purchase tutorial by allowing users to purchase or unlock content or features.

Android KTX Tutorial: Getting Started

$
0
0
In this tutorial, you’ll learn how to use Core KTX to make your code more concise and readable by refactoring an app that generates delightful collages.

Password Autofill in iOS 12: Strong Passwords and Passcode Autofill

$
0
0
iOS 12 improvements to Password Autofill include strong password generation right on your account creation page, and SMS authentication code autofill.

Screencast: Password Autofill in iOS 12: Strong Passwords and Passcode Autofill

$
0
0
iOS 12 improvements to Password Autofill include strong password generation right on your account creation page, and SMS authentication code autofill.

HomeKit Tutorial: Getting Started

$
0
0
Learn how to use the HomeKit framework to control smart home devices by making an app that controls a smart lightbulb.

Design Patterns – Podcast S08 E03

$
0
0
Joshua Greene and Jay Strawn, the authors of “Design Patterns by Tutorials”, join us to talk about different Design Patterns and SOLID.

Firebase Tutorial: Real-time Chat

$
0
0
It seems like every major app out there has a chat feature. This tutorial shows you how to build a chat app with Firebase and MessageKit!
Viewing all 4395 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>