Brian Coleman

  • Home
  • ABOUT
  • SERVICES
  • PORTFOLIO
  • BLOG
  • Contact

Tutorial 59

TestFlight vs Enterprise Distribution

If you build apps more than likely you’ll need to send out test builds of your app to a group of users so they can review it and give you feedback before releasing it to the public. Depending on the scale and complexity of the app you’ll need to do this many times before your final release. Below are a couple of options when sharing with your team.

TestFlight Distribution

TestFlight is a free over-the-air platform used to distribute beta and internal iOS applications to team members. Developers can manage testing and receive feedback from their team with TestFlight’s Dashboard. The App Store approval process is not required. The general public cannot see a listing for your app, purchase or install it, only the people you invite to use it.

How Does it Work?

iOS Developers sign up at TestFlight, create a team, invite team members, add the team members’ devices to their application build (this still has to be done via Apple’s provisioning on the Developer Portal), upload to TestFlight and then distribute. Developers still have to build the app, however.

For a Developer

  1. Sign up with TestFlight and create your team.
  2. Invite and gather the UDIDs from the team members and add them to your Apple Developer Portal and provisioning profile for the app.
  3. Build the iPhone Application (.IPA) and upload to TestFlight.
  4. Distribute the build to team members and let the magic happen.
  5. Receive feedback, monitor tester’s activity and perform a full fledge beta test from the comfort of your chair. Developers on teams can also upload multiple apps for testing/internal distribution.
  6. Integrating the TestFlight SDK will further the data collected from your testing phase.


For a Tester

  1. As a tester using TestFlight, you can either be invited, recruited or just sign up.
  2. Once you sign up with TestFlight and login to the dashboard for the first time, you will be asked to connect your device, allowing developers to access the device’s Unique Device Identifier (UDID).
  3. Once you have successfully connected the device to your account and have been accepted to a team, the developer will have to add the UDID to the application, so that you can install it on your device. (Only 100 UDIDs allowed per account)
  4. Once the developer uploads a build you will receive an email through TestFlight, from the developer, with a link to install the build.
  5. Follow the instructions in the email, click on the link, download the build to your device, and you will be ready to begin using the app.



Note: If you are a registered developer with an enterprise account and are making apps for in-house distribution, TestFlight works with those too. TestFlight fully supports enterprise apps and it works much like ad hoc apps for distribution. Just upload an application signed with an enterprise provisioning profile and distribute to your team and only approved members of your team will have access to the application for installation.

Apple Buys TestFlight

screenshot2
In February of this year Apple announced it had bought TestFlight and as of March 21 TestFlight no longer supported Android builds. Since this time Apple has integrated the TestFlight platform into iTunes Connect so Apple developers can easily send out test builds of their app before submitting to Apple for review.

For instructions on how to prepare your beta app and set up a list of testers watch the TestFlight video tutorial below.

Screen Shot 2014-09-23 at 10.22.20 AM

Enterprise Distribution

The iOS Enterprise Distribution program allows a company to distribute their own “in-house” apps directly. It is intended for employees of the licensee company only and that licensee must be a company or organization with a DUNS number. The cost is $299 per year for this license compared to $99 per year for the iOS Developer License. A given device can have apps installed from only one iOS Enterprise License at a time.

“Internal Use Applications developed under this Agreement may be deployed on Deployment Devices in two ways: (1) deployment for internal use by Employees, and (2) deployment for use by Customers either on Your physical premises or under the direct supervision and physical control of Your Employees in other locations, subject to Apple’s right to review and approve such deployment as set forth herein.” – Apple

Note: The Enterprise program does not enable you to deploy apps to the Public App Store. For that you need to be enrolled in the standard iOS Developer Program.

How Does it Work?

Note: You must have the Team Agent role in the iOS Provisioning Portal to create the artifacts needed for this build.

The process for building an Enterprise app is:

1. Create the Distribution Certificate.

2. Create an Enterprise Provisioning Profile. (Go to developer.apple.com and click Member Center.)

3. Build the app using the Enterprise Provisioning Profile.

Following sections explain these steps in further detail.

Create a Distribution Certificate

Distribution certificates are used to sign Enterprise apps.
The steps to receive a Distribution Certificate are:

1. Login to the iOS Provisioning Portal with the Agent role. Goto “iOS Dev Center” -> “Certificates, Identifiers & Profiles” -> “Certificates

2. In the Certificates section, go to the Production tab. Click on ‘+’ icon. Select the type as “In-House and Ad Hoc”.

3. Follow the steps to generate Certificate Signing Request (CSR). Use the Certificate Assistant (available in the Keychain Access application on Mac OS X).

4. Upload the CSR to the iOS Provisioning Portal and download the distribution certificate.

5. Save the certificate on the disk, and open this using Key Chain Access.

6. Xcode can now use this certificate to sign iOS apps.

7. To export this certificate to a different (build) machine, exported it in .p12 format:

a. In Key Chain Access, go to the My Certificates section

b. Right-click on the downloaded certificate and click “Export…”

c. Save the certificate in .p12 format, and provide a secure password while saving.

Create an Enterprise Provisioning Profile

The steps to create an Enterprise Provisioning Profile are:

1. Log in to the iOS Provisioning Portal with the Team Agent role. Goto “iOS Dev Center” -> “Certificates, Identifiers & Profiles” -> “Provisioning Profiles”

2. In the Provisioning section, go to the Distribution tab.

3. Click on New Profile. On the Create iOS Provisioning Profile page

a. Select the Distribution Method as In House

b. Select an appropriate distribution certificate

c. Select App ID

4. Download the provisioning profile.

5. This provisioning profile can be used to build the app for in-house deployment.

Building an App Using an Enterprise Provisioning Profile

The steps to build an app on a machine are:

1. Install the Distribution certificate (.p12 file)

2. Open Xcode. Go to the Organizer window

3. Import the Enterprise Provisioning Profile

4. Open the app’s project file in Xcode

5. Go to Build Settings, Code Signing section

6. Select the certification contained in the Enterprise Provisioning Profile

7. Build the project using Build For > Build for Archiving. This will create the app archive in .ipa file format

8. The .ipa file is ready for enterprise deployment

Distribute to Your Team

Once you have the *.ipa you can store that on an internal web server. Since iOS7, the web server needs to support SSL secure encryption.

Create a web page on your internal network that everyone can access within the company, i.e. http://apps.yourcompany.com. Then create an HTML with a link for each app that you would like to share.

The a href link for every app in your list should look similar to this and point to a manifest file:
itms-services://?action=download-manifest&url=https:///appName.plist

The App Name

Here is a sample manifest file (appName.plist):



items


assets


kind
software-package
url
http://10.44.41.21/appName.ipa


kind
display-image
needs-shine

url
http://10.44.41.21/appName.png


kind
full-size-image
needs-shine

url
http://10.44.41.21/appName.png


metadata

bundle-identifier
ent.company.appName
bundle-version

kind
software
title
appName





Note: As of iOS8, ensure that the icon URLs are valid. If they are not the user will not be able to install the app on their device.

Conclusion

Personally I like to use the Enterprise Distribution method internally. It’s easy because you don’t have to provision individual devices, this is ideal for large companies with lots of stakeholders and testers. It’s also nice to have one URL that everyone can visit to download any of your test apps within the company. Also best to host that URL on the companies internal network, in order to follow Apple’s rules about “deployment for internal use by Employees” mentioned above. It’s well worth the $299 a year for the Enterprise license, and you can create as many apps as you need.

If I were to send out builds for a personal project or for a small startup company, TestFlight is a much better option since your distribution list of people you need to share the app with is much smaller and within the standard Ad Hoc distribution limitation of 100 devices allowed to provision for a developer account.

September 23, 2014 News, Tools, Tutorialsapple, ios8, tutorial

Tutorial: How to use Auto Layout in Xcode 6

Apple just introduced the new iPhone 6 (4.7in) and iPhone 6 Plus (5.5in). These are two additional sizes to the existing iPhone 4 (3.5in), iPhone 5 (4in), and iPad (9.4in). As iOS developers we have long enjoyed not having to develop for too many different screen sizes, unlike Android, until now. With this many screens available, your apps need to be flexible enough to work on all of them or Apple may reject it or even worse, users will complain it doesn’t work for them on their device.

Below is a simple tutorial to help you with adding constraints to your views using Storyboards, without having to write any code. This is helpful for handling auto rotation as well.

Designing an Interface using Constraints

  1. Make a new project based on the Single View Application template, and name it AutoLayout.
  2. Select Storyboard to edit the interface. One nice thing about using constraints is that they accomplish quite a lot using no code. All of the auto layout implementation can be done right here in the Storyboard.
  3. Add four labeled buttons. Drag four round rect buttons from the library over to your view. Use the dashed blue guidelines to help you line up each one near its respective corner. Double-click each button, and assign a title to each one so you can tell them apart later.
    Screen Shot 2014-09-10 at 9.24.50 AM

  4. Run It. Let’s see what happens now that we’ve specified that we support autorotation but haven’t set any autosize attributes. Build and run the app. Select Hardware Rotate Left, which will simulate turning the iPhone to landscape mode. Notice that all of the buttons except for the top left one is off the screen. We need to fix this.
    Screen Shot 2014-09-10 at 9.26.04 AM

  5. The image below shows an example of the Auto Layout Menu. You’ll find this in the bottom right of the Storyboard. First select which view you want to modify, then choose one of the buttons to modify it’s layout properties.

    Align – Create alignment constraints, such as aligning the left edges of two views.
    Pin – Create spacing constraints, such as defining the width of a UI control.
    Issues – Resolve layout issues.
    Resizing – Specify how resizing affects constraints.

    © Copyright 2014 AppCoda

    © Copyright 2014 AppCoda

  6. Let’s add our constraints. Click the upper-left button to select it. Choose the Pin button to change constraints for the button. Add 20px to the top and 0px to the left. Then press the “Add 2 Contraints” button to make the change.
    Screen Shot 2014-09-10 at 10.45.58 AM

  7. You’ll see that the constraints are now set by the blue lines now displayed when the button is selected. You may not see blue lines for the top left button since all constraints for that location are by default.
  8. Now do the same for the other three buttons.
    Screen Shot 2014-09-10 at 11.02.59 AM

  9. Run it and look at the results. Each button stuck tight to its nearest corner. The buttons on the right shifted out to the right to match the view’s new width, and the buttons on the bottom were pulled up to match the new height.
    Screen Shot 2014-09-10 at 10.48.55 AM
    Screen Shot 2014-09-10 at 10.49.00 AM

    You can grab the full source code for this tutorial. Note: Built for XCode6-Beta7.

    Additional Tips for using Auto Layout

    Use Auto Layout to lay out your views relative to each other without fixed origins, widths, and heights, so that views reposition and resize appropriately when the language or locale changes. Auto Layout makes it possible to have one set of .storyboard and .xib files for all devices.

    Remove fixed width constraints. Allow views that display text to resize. If you use fixed width constraints, text may appear cropped in some views.

    Use intrinsic content sizes. The default behavior for text fields and labels is to resize automatically. If a view is not adjusting to the size of text, select the view and choose Editor > Size To Fit Content.

    Use leading and trailing attributes. When adding constraints, use the attributes leading and trailing for horizontal constraints. The attributes leading and trailing are equivalent to left and right. The leading and trailing attributes are the default values for horizontal constraints.

    Pin views to adjacent views. Pinning causes a domino effect. When one view resizes to fit text, other views do the same. Otherwise, views may overlap in some devices.

    Constantly test your layout changes. Test your app using different device sizes, like iPhone 4, iPhone 5, iPhone 6, iPhone 6 Plus, and iPad.

    Don’t set the minimum size or maximum size of a window. Let the window and its content view adjust to the size of the containing views.

    Auto Layout is enabled by default when you create a new project. To enable Auto Layout for an older project, select the first view of the View Controller, then select the File Inspector (first one on the left) and make sure the “Use Auto Layout” check mark is selected.

September 10, 2014 Swift, Tutorialsios8, swift, tutorial

Tutorial: Collection View using Swift

Flow Layout
One of the best features for developers that came in the iOS 6 SDK is UICollectionView. It’s been a popular tool for developers since, so let’s review how to create one in Swift (if you need it in objective-c read Tutorial: Collection View using Flow Layout).

UICollectionView is very similar to UITableView but you can customize it a lot more and it can scroll horizontal, goodbye scroll views! Most recently I used a collection view for a bottom navigation scroller and it worked really well.

A UICollectionView view has three main components:
1. Cells: Display your content in cells that are de-queued as they leave the screen.
2. Supplementary Views: Add labels, section headers and footers to define your content areas.
3. Decoration Views: Decorate the collection view to look like a bookshelf or a background image.

Start by adding the delegates to your class file and define your collection view.

class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    
    @IBOutlet var collectionView: UICollectionView?

The most common layout for a UICollectionView is UICollectionViewFlowLayout. Flow layout organizes items into a grid with optional header and footer views for each section. The items in the collection view flow from one row or column (depending on the scrolling direction) to the next, with each row comprising as many cells as will fit. Cells can be the same sizes or different sizes.

Define the UICollectionView & UICollectionViewFlowLayout properties in the viewDidLoad method.

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
    layout.itemSize = CGSize(width: 90, height: 90)
    collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
    collectionView!.dataSource = self
    collectionView!.delegate = self
    collectionView!.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
    collectionView!.backgroundColor = UIColor.whiteColor()
    self.view.addSubview(collectionView!)
}

Most of the properties are self explanatory but the key is to define your cell class if you are using custom cells (just like table views) and setting the size of the cells, above they are set to 90×90. Choose your section insets which is the spacing in between each cell, I have it set to 20 for the top and 10 for the rest. Lastly define the layout, we’re using let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout().

flow_section_insets_2x

Setting up a UICollectionView is very similar to a UITableView, so you’ll recognized a lot of the methods below.

func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return 1
}

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 20
}

Above we’re defining how many items and sections are in the collection view. If you have more than one section, then you’ll need to define the number of sections, if you only have one you don’t need that method.

Below is the real meat of the UICollectionView, defining the cells with your data. If you choose to use a custom cell you can add images or whatever you need to display.

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as CollectionViewCell
    cell.backgroundColor = UIColor.blackColor()
    cell.textLabel?.text = "\(indexPath.section):\(indexPath.row)"
    cell.imageView?.image = UIImage(named: "circle")
    return cell
}

Collection View Data
Finally you can drag the UICollectionView object into your view within your NIB or storyboard. Move and scale the collection view to where you want to display it and review all of the properties within the inspector. Be sure to hookup the collectionView reference outlet and dataSource and delegates so the collection view can provide all the functionality available. Do the same for the Collection View Flow Layout. (See image on the right).

You can grab the full source code for this tutorial. Note: Built for XCode6-Beta7.
If you need it built using Xcode 6.3 (Swift 1.2).

Screen Shot 2014-09-04 at 11.12.51 AM

September 4, 2014 Swift, Tutorialsios8, swift, tutorial

Tutorial: Force Upgrade in Swift

Sometimes you need all users to use the same version of your App. There are many reasons for this but the most popular is to ensure all users have a specific new feature that’s only available in the latest version. Another popular reason is you changed a data source or functionality that is no longer supported in older versions of your app. This will force the user to upgrade the app on their device and will not allow them to use their current version anymore.

It’s important that this feature is built into your very first release of your application (v1.0) so you can force all users to upgrade when you need to.

Below is a tutorial to help you add a forced upgrade alert in Swift.

Web Server API

  1. Create “appVersion.php” on your Server. This can be created using any kind of Web API (Perl, PHP, Ruby, etc..).
  2. Write this code into your file. This code will give out your latest App Version.
    
    

Get App Version using NSURLConnection

  1. You’ll need the following method to read in the latest App Version from the Web Server API.
    func getAppVersion() {
        let url = NSURL(string:"http://www.brianjcoleman.com/code/appVersion.php")
        let cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
        var request = NSMutableURLRequest(URL: url!, cachePolicy: cachePolicy, timeoutInterval: 2.0)
        request.HTTPMethod = "POST"
        
        // set Content-Type in HTTP header
        let boundaryConstant = "----------V2ymHFg03esomerandomstuffhbqgZCaKO6jy";
        let contentType = "multipart/form-data; boundary=" + boundaryConstant
        NSURLProtocol.setProperty(contentType, forKey: "Content-Type", inRequest: request)
        
        // set data
        var dataString = ""
        let requestBodyData = (dataString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
        request.HTTPBody = requestBodyData
        //println("Data String: \(dataString)")
        // set content length
        //NSURLProtocol.setProperty(requestBodyData.length, forKey: "Content-Length", inRequest: request)
        
        var response: NSURLResponse? = nil
        var error: NSError? = nil
        let reply = NSURLConnection.sendSynchronousRequest(request, returningResponse:&response, error:&error)
        
        let results = NSString(data:reply!, encoding:NSUTF8StringEncoding)
        println("API Response: \(results)")
        self.serverAppVersion = results!
    }
    

Display UIAlertController in Swift

  1. Then you need to compare the minimum app version from the web server against the app version the user has on their device.
  2. If the minimum app version from the server is greater than the users app version it will display an alert view and force the user to upgrade.
  3. Add the method below into your first view controller in your app.
    func checkForAppUpdate() {
        
        getAppVersion()
        
        var minAppVersion = self.serverAppVersion
        var appVersion = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") as String
        
        println("Current Version: \(appVersion) -- Minimum Version: \(minAppVersion)")
        
        //Compare App Versions
        var minAppVersionComponents : NSArray = minAppVersion.componentsSeparatedByString(".")
        var appVersionComponents : NSArray = appVersion.componentsSeparatedByString(".")
        
        var needToUpdate = false
        
        for i in 0..
    
  4. Lastly you'll need to call the checkForAppUpdate() from viewDidLoad() so it will only get called the when the app is initially loaded.

You can grab the full source code for this tutorial. Note: Built using XCode6.1
If you need it built using Xcode 6.3 (Swift 1.2).

July 31, 2014 Swift, Tutorialsios8, swift, tutorial

Tutorial: Rate Me using UIAlertController in Swift

We all know that good reviews for our apps will influence new users to download the app. It doesn’t help that there is no standard way to ask users for a review. The only way to get your app reviewed is on your app page within the App Store. How many people after downloading your app will again go back to AppStore, search for your app, go to rate screen and give you a rating? Not many outside of your own family and friends!

If you prompt the customer at some point while the app is running you’re always going to disrupt their workflow to some degree. You can alleviate this by trying to pick a moment that’s the least disruptive. If possible choose a moment when something positive or rewarding has just happened.

Below is a tutorial to help you add a Rate Me alert view in Swift.

Setup Rate Me

The method below will control the logic of when the Rate Me alert view is displayed to the user. Enter in the minimum number of sessions the app should be opened before showing the request to rate the app the first time and how often it should try again if the user selects “Maybe Later”. The numLaunches and neverRate values are stored using NSUserDefaults.

var iMinSessions = 3
var iTryAgainSessions = 6

func rateMe() {
    var neverRate = NSUserDefaults.standardUserDefaults().boolForKey("neverRate")
    var numLaunches = NSUserDefaults.standardUserDefaults().integerForKey("numLaunches") + 1
    
    if (!neverRate && (numLaunches == iMinSessions || numLaunches >= (iMinSessions + iTryAgainSessions + 1)))
    {
        showRateMe()
        numLaunches = iMinSessions + 1
    }
    NSUserDefaults.standardUserDefaults().setInteger(numLaunches, forKey: "numLaunches")
}

Using UIAlertController in Swift

UIAlertView is deprecated. Use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert instead. Now UIAlertController is a single class for creating and interacting with what we knew as UIAlertViews and UIActionSheets on iOS 8.

If you would like to use UIAlertView with multiple actions, the method below is the best way to handle each button tapped. The advantage of using completion handlers is you can easily handle multiple alert views within a single view controller.

func showRateMe() {
    var alert = UIAlertController(title: "Rate Us", message: "Thanks for using ", preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "Rate ", style: UIAlertActionStyle.Default, handler: { alertAction in
        UIApplication.sharedApplication().openURL(NSURL(string : "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id="))
        alert.dismissViewControllerAnimated(true, completion: nil)
        }))
    alert.addAction(UIAlertAction(title: "No Thanks", style: UIAlertActionStyle.Default, handler: { alertAction in
        NSUserDefaults.standardUserDefaults().setBool(true, forKey: "neverRate")
        alert.dismissViewControllerAnimated(true, completion: nil)
        }))
    alert.addAction(UIAlertAction(title: "Maybe Later", style: UIAlertActionStyle.Default, handler: { alertAction in
        alert.dismissViewControllerAnimated(true, completion: nil)
        }))
    self.presentViewController(alert, animated: true, completion: nil)
}

If you just need an alert view with a single action “Ok”, the following code will do.

let alert = UIAlertView()
    alert.title = "Alert"
    alert.message = "Here's a message"
    alert.addButtonWithTitle("Understod")
    alert.show()
July 30, 2014 Swift, Tutorialsios, swift, tutorial
Page 5 of 12« First«...34567...10...»Last »
Recent Posts
  • Classix for iPhone, iPad & Apple TV
  • Tutorial: How to test your app for IPv6 compatibility
  • Tutorial: Testing SSL using Charles Proxy on an iOS Device
  • Tutorial: 3D Touch – Quick Actions in Swift
  • tvOS Tutorial: Top Shelf in Swift
Featured Apps
Classix
Sportsnet
TAGS
tutorialswiftios8iosobjective-cvideogamesstrategynewsframeworkappsmonitizefacebookwatchappleios7toolstvosios9apiprovisionsocialtutorialsbooksdesignbookiapIPv6iTunes Connect
Search
TAGS
tutorialswiftios8iosobjective-cvideogamesstrategynewsframeworkappsmonitizefacebookwatchappleios7toolstvosios9apiprovisionsocialtutorialsbooksdesignbookiapIPv6iTunes Connect
ABOUT
Brian is a Lead iOS/tvOS Developer from Toronto with over 18 years of multifaceted experience including development, design, business analysis and project management.

FOLLOW ME
    
Email Subscription
Sign up for my newsletter to receive the latest news and tutorials posted.

Enter your email address:

2023 © Brian Coleman