Recipes – Cocoanetics https://www.cocoanetics.com Our DNA is written in Swift Sun, 02 Apr 2017 19:07:48 +0000 en-US hourly 1 https://wordpress.org/?v=6.4.3 39982308 Group by, Count and Sum in CoreData https://www.cocoanetics.com/2017/04/group-by-count-and-sum-in-coredata/ https://www.cocoanetics.com/2017/04/group-by-count-and-sum-in-coredata/#comments Sun, 02 Apr 2017 19:06:14 +0000 https://www.cocoanetics.com/?p=10483 For a tvOS app I needed to efficiently group by a certain column and then count the resulting rows or sum the value. And since I do everything in Swift 3 these days, I couldn’t find any suitable example on line. But I eventually figured it out.

The biggest change in CoreData – as of Swift 3 – was that fetch requests are now generics which return an array of the generic type. The model editor auto-generates the NSManagedObject sub-classes for us. We don’t have to add any code for that to happen.

The auto-generated code includes a function for a typed fetch request, as in this example:

extension Record {

    @nonobjc public class func fetchRequest() -> NSFetchRequest {
        return NSFetchRequest(entityName: "Record")
    }
}

So querying all Record instances that are currently in the database becomes quite simple:

let fetch: NSFetchRequest = Record.fetchRequest()
do {
   let results = try fetch.execute()
   // results is now an array: [Record]
catch {
   NSLog("Error fetching entity: %@", error.localizedDescription)
}

Note that we have to specify the generic type to be used with the fetch request or else you get an error message that the fetchRequest function is ambiguous.

Group by and Count

I needed to group my records by a certain column and count the records for each of the column’s values. In SQL you would the query for that like this:

SELECT col, count(*) from Record GROUP BY col

It’s a big more involved to achieve the same effect with CoreData. Until now I didn’t even know that this was possible nowadays. The two key parts are that you have to formulate an expression for the count(*) and then specify the key(s) to group by.

First the expression…

let keypathExp = NSExpression(forKeyPath: "col") // can be any column
let expression = NSExpression(forFunction: "count:", arguments: [keypathExp])

let countDesc = NSExpressionDescription()
countDesc.expression = expression
countDesc.name = "count"
countDesc.expressionResultType = .integer64AttributeType

For the purpose of counting records in each group, it does not matter which key path you specify for the key path expression. But you need to have at least one parameter in the arguments array, or else you get an exception.

The second part generates an expression description to give the result a name and result type. This we will use then specifying the columns/expression to return from the grouping.

Now the fetch request that also groups…

let request = NSFetchRequest(entityName: "Record")
request.returnsObjectsAsFaults = false
request.propertiesToGroupBy = ["col"]
request.propertiesToFetch = ["col", countDesc]
request.resultType = .dictionaryResultType

So we are fetching from Record. We want the result to be returned as NSDictionary. We are grouping by “col” and the properties to output in the dictionary should be col and the count. The result type must to be a dictionary, or else you will get an exception:

Invalid fetch request: GROUP BY requires NSDictionaryResultType

The result is still an array, but with a dictionary for each row of the result.

[
   {
      "count": 111,
      "col": "One"
   },
   {
      "count": 222,
      "col": "Two"
   }
]

The exercise to convert that into a single dictionary is left to the reader. But please share your solution to me on Twitter!

Just a Sum

To get a sum of a column’s values instead of the count, you take exactly the same code, but substitute sum: for count:. This time though, the expression must be referencing the column you want to sum up.

let keypathExp1 = NSExpression(forKeyPath: "col")
let expression = NSExpression(forFunction: "sum:", arguments: [keypathExp1])
let sumDesc = NSExpressionDescription()
sumDesc.expression = expression
sumDesc.name = "sum"
sumDesc.expressionResultType = .integer64AttributeType
                
let request = NSFetchRequest(entityName: "Record")
request.returnsObjectsAsFaults = false
request.propertiesToFetch = [sumDesc]
request.resultType = .dictionaryResultType

Note that here I didn’t group, the sum is over the entire set of rows in the CoreData DB. But grouping could easily be added by adding the propertiesToGroupBy.

Conclusion

Besides the grouping functions count and sum there are a few more that might come in handy: min, max and avg. In the same project, I used max to retrieve the latest date from all rows of an entity. As with other fetch requests you can also add a sort descriptor and a predicate to filter.

The functionality to perform such aggregate operations must have been existing in iOS for a very very long time. The facility to group by properties exists since iOS 5, expressions even since iOS 3. This makes me wonder why few people know about it.

At the time of this writing I am still trying to figure out if there is a way to reduce the amount of code for each such aggregation fetch request. If you can think of any, please let me know. In any case: Happy Grouping!

]]>
https://www.cocoanetics.com/2017/04/group-by-count-and-sum-in-coredata/feed/ 4 10483
Firebase Tutorial https://www.cocoanetics.com/2017/02/firebase-tutorial/ https://www.cocoanetics.com/2017/02/firebase-tutorial/#comments Mon, 27 Feb 2017 12:05:01 +0000 https://www.cocoanetics.com/?p=10389 Since the official Parse shutdown, many people are looking for alternatives. Google established Firebase as one such option for your web-based backend. In this tutorial we will be exploring how to build an iOS app that uses Google Firebase as backend.

My name is Bernhard Götzendorfer and I’m currently interning at Cocoanetics. I’ve spend some time working with Google’s Firebase and since I wanted to start blogging, I will take this opportunity and walk you through the steps to create a simple iOS App using Firebase.

This is how our finished tutorial app will look like:

Setting up the Firebase App

First we will create a new app project with Xcode and then we will configure Firebase to support it.

Open Xcode to create an Single View Application and save it in a convenient location.
Now go to https://console.firebase.google.com and login with your Google Account.
  • Create new Project -> Name: Firebase, choose your country and Create Project.
  • Go to your Xcode Project and copy the app’s Bundle Identifier

  • Back in Firebase click Add Firebase to an iOS Project and paste your identifier into the field named iOS-Bunde-ID, optionally you may insert an app-nickname/appstore-ID.

Then click Add App. This will generate a GoogleService-Info.plist which downloads.

  • Add this file to your Xcode project right next to the Info.plist. Make sure to check the Copy if needed option.

Installing the Firebase Cocoapod

  • Open your Terminal, navigate to the project location. One quick way to do that is to enter cd and then drag the project folder onto the terminal window.
  • Create a new Podfile by typing pod init.

There are several text editors available in terminal, for this example we are using vi to edit the Podfile.

  • Open the Podfile with the command vi Podfile.
  • Move the cursor to the line below # Pods for FirebaseDemo.
  • Press i to activate insert mode and type pod ‘Firebase/Database’
  • press ESC to leave insert mode.
  • To Exit type :wq to Write and Quit.

  • Type in your Terminal “pod install” to install the pod and generate a workspace for Xcode.

Note: You need to close your Xcode project and instead open the newly generated xcworkspace.
Otherwise you won’t be able to compile because only the work space has the necessary pod references.

Firebase Code Setup

In AppDelegate.swift, we add an import for the Firebase module and call to the configure function within didFinishLaunchingWithOptions as shown below.

Firebase Authentication Rules

As the last step for configuring Firebase we need to modify the authentication rules in the Firebase console. If you still see the Add Firebase to my iOS-App popup, then close that by clicking on the X.

Click on the Database tab on the left side and click on Rules at the top. Modify the rules to look like shown in the screenshot below:

For this tutorial we will not need any more elaborate authentication, so we just make read access public. For more information about this refer to https://firebase.google.com/docs/database/security/quickstart.

Firebase setup is now complete!

Building the Messaging App

With Firebase setup now complete, we can proceed with creating our tutorial app to send messages between users.

  • Delete ViewController.swift – move to Trash.
  • Right-click on the FirebaseDemo folder in project navigator and select New File.
  • Create a Cocoa Touch Class – Subclass of UIViewController named AddViewController.
  • Repeat that step but with Subclass of UITableViewController named MessageTableViewController.
  • And our last one: Choose Swift File and name it Message.

Xcode will append .swift to all these new files. Your file structure should look like this:

Go to your Main.storyboard and delete your current view controller. We will use our own custom messages table view controller as the initial view controller of the storyboard.

Search for Table View Controller in the object browser (bottom right of the inspector panel) and drag it into your storyboard.
  • Select the new Table View Controller and in the Menu Bar at the top select Editor -> Embed in -> Navigation Controller. This creates a new navigation controller that has the table view controller as root.
  • In the attribute inspector for the new navigation controller, check “Is Initial View Controller” – this is our App’s starting point.
  • Search on for a Bar Button Item on the bottom right and insert it at the top right of the navigation bar on the table view controller. Change the System Item to Add.
  • Drag a new View Controller onto the canvas.
  • Control+drag the plus button to the view controller and select the Show option in the popup.


If you now click on the + in your app, it will navigate you to the view controller.
  • Select the table view controller – Go to the identity inspector (third tab from the left) and enter MessageTableViewController for its Custom Class.
  • Repeat the same action for the view controller but enter AddViewController for the Custom Class. This makes storyboard use our custom classes instead of the standard system ones.

  • Change the Style of the MessageTableViewController’s prototype cell to Subtitle and specify “Cell” as its identifier.
Let’s fill the AddViewController with the stuff we need.
  • Add a Vertical Stack view and bind it to the View with Constraints at top, left and right.
  • Increase the Spacing from 0 to 20.

Into the Stack View add in that order:

  • Label
  • TextField
  • Label
  • Picker View
  • Button

Make sure that you add all of them into the Stack View! The view controller should look like this:

Now we need outlets for the text field and picker and an action for the Send button. Show the assistant inspector via the button with the two circles and control+drag from the elements onto the code pane. For the text field choose an outlet. For the Picker as well. For the button change the Connection type to Action.  I named them messageField, recipientPicker and sendMessage.

For simplicity the data source for the recipient picker will be merely a static array of names.

We make the view controller the delegate and datasource for the picker view. Control-drag from the picker onto the icon representing the view controller at the top.


Okay, thats it for setting up the user interface.

Interacting with Firebase

You can find the code for my sample project on GitHub. I will not reproduce all of the code in the project in this blog post, only two parts as they show how to talk to Firebase from Swift.

Communicating with Firebase requires a bit unusual syntax, but it is very simple. This is how a new message is sent in AddViewController.swift:

func msg(to user: String, withText msg: String)
{
   //Build the Connection to the Firebase Database
   let firebaseRef = FIRDatabase.database().reference()
        
   //Tell Firebase the structure
   let msgDict = ["recipient": user, "msg": msg]
        
   //Tell Firebase where to save and save the Value
   firebaseRef.child("Messages").childByAutoId().setValue(msgDict)
}

This gets a reference to the database and then tells it to add a new child object with an automatic identifier. The necessary fields get inferred from the dictionary. This is quite similar to Parse.

To have the messages table view be automatically be updated by firebase can tell it to send us notifications whenever something changes. The following function launches an observation where every time a new object is added our handler closure will be called.

func observeMessages(withHandler handler: @escaping (_ Message: Message) -> ())
{
   FIRDatabase.database().reference().child("Messages").queryOrderedByKey().
      observe(.childAdded, with: { (snapshot) in
                
         let dict = snapshot.value as! NSDictionary
                
         guard let to = dict["recipient"] as? String,
               let msg = dict["msg"] as? String else
         {
            print("Error in msg")
            return
         }
                
         let msgObj = Message(to: to, msg: msg)
                
         handler(msgObj)
      })
}

The establishes an ongoing query for the Messages entity. In the table view’s viewDidLoad we give it a handler to be called for each new message.

override func viewDidLoad()
{
   super.viewDidLoad()

   // Needed to load the Data with observerMessages and put it into our Array.
   // messages and later into the TableViewController
   observeMessages { (message) in
                
      self.messages.insert(message, at: 0)
      self.tableView.reloadData()
   }
}

I encourage you to try to understand the contents of Message.swift, MessageTableViewController and AddViewController and how they interact with each other.

Conclusion

I had great fun creating this tutorial and trying it out with several of my friends. I found that when you are explaining a concept that is new to yourself, it helps to put yourself into the frame of mind of somebody who needs to explain it to somebody else.

Google Firebase is still a bit of a mystery to me, but it looks like I made the first successful steps in using its powers for my own apps. However, the main benefit for me personally was that I got much better at creating the basics of a new iOS app, like setting up Xcode, adding CocoaPods, modifying the storyboard in Interface Builder and much more.

I welcome your feedback. You can either send me an mail or create an issue on GitHub. Special thanks to Oliver for motivating and mentoring me, my teachers for giving me a good start into programming and all the people which follow the same path as I do and are a part of my life.

]]>
https://www.cocoanetics.com/2017/02/firebase-tutorial/feed/ 3 10389
iTunes Connect Encryption Info https://www.cocoanetics.com/2017/02/itunes-connect-encryption-info/ https://www.cocoanetics.com/2017/02/itunes-connect-encryption-info/#comments Sun, 26 Feb 2017 15:08:16 +0000 https://www.cocoanetics.com/?p=10446 Apps on Apple’s App Store are technically originating in the US. Therefore they are affected by US rules governing the export of encryption technology. For a long while, I remember, Apple asked developers during app submission if the app was using encryption.

Most people just chose no for this question, if their app uses encryption, even when they were simply accessing web services or pages via HTTPS. So did I and it was confirmed to me by an Apple employee at one WWDC who told me that iOS encryption is exempted from the export restriction rule. So we kept saying no.

The reason for this worry was that saying yes would mean you would have to get special permission from the US government and would have the hassle of having to submit reports about your encryption every year. All these complications we would want to avoid, especially when building apps for clients whom you could hardly hope to explain why they would be required to jump through such hoops.

Fortunately since then it has been clarified that if the app “Only makes call(s) over HTTPS” it gets an exemption. There are more elaborate explanations on why this exemption exists and I am not a lawyer… but I think the main reasons for this exemption is that you cannot encrypt user files with HTTPS on disk, you cannot change how HTTPS works because it is part of the OS and the source code for SSL is publicly available.

So, for this specific use case, accessing a RESTful API via HTTPS, the correct answers are:

Question 1: Is your app designed to use cryptography or does it contain or incorporate cryptography?

– Making calls over secure channels (i.e. HTTPS, SSL, and so on)
– Using standard encryption algorithms
– Using crypto functionality from other sources such as iOS or macOS
– Using proprietary or non-standard encryption algorithms

We answer YES, because we are using the SSL standard encryption algorithm from iOS or macOS to make calls over secure channels. 3 out of 4 when only one is enough for an affirmative answer.

OMG OMG, we are using encryption…

Question 2: Does your app meet any of the following:

– Qualifies for one or more exemptions provided under category 5 part 2
– Use of encryption is limited to encryption within the operating system (iOS or macOS)
– Only makes call(s) over HTTPS
– App is made available only in the U.S. and/or Canada

We answer YES, YES, YES, phew! Again, only one match with one of the four examples would have been sufficient. But we are exempted.

There are two more questions but you will only ever see these if you answered NO to the latter question.

Then Apple acquired TestFlight

Since beta testing is also a form of international distribution – especially if you are sharing your app with international users by e-mail – Apple wants to know about the encryption status of your app beforehand.

The initial process was to specify the Export Compliance Information also by answering the above questions. But having to do so for every build or new version gets old really fast. Which is why Apple added the functionality for us to add this information already in Xcode.

The developer knows best if the app contains an exempt encryption and the ITSAppUsesNonExemptEncryption key in Info.plist lets you codify the answer once and for all.

But what is the correct bool value. True or False? The double negative in here might confuse you… I know some people who were.

Read the key again: App uses non-exempt encryption… does it use an encryption that is not exempt from the export restriction?

The correct answers is FALSE. HTTPS is exempt. Since we don’t use anything else we DON’T use a NON-exempt encryption.

Which is why I am now adding this to all my app’s Info.plist:

<key>ITSAppUsesNonExemptEncryption</key>
<false/>

Xcode even has a nice way to display this:

With this setting in there the TestFlight builds become available for testing as soon as processing is done. No longer are my clients bothered by having to think of the right answers to these questions about encryption.

Conclusion

Many an app submission has caused uneasy feelings in the early days of the App Store because people didn’t know how to properly deal with the export restrictions. It was very welcome that Apple clarified this common use case of HTTPS as being exempt from having to register and report to the US government.

It is great that Apple added this functionality to specify the encryption technologies status information in Xcode. Now we developers can mark our app as being harmless and no longer need to bother our clients with such questions.

 

]]>
https://www.cocoanetics.com/2017/02/itunes-connect-encryption-info/feed/ 8 10446
Asynchronous NSTextAttachments (2/2) https://www.cocoanetics.com/2016/09/asynchronous-nstextattachments-22/ https://www.cocoanetics.com/2016/09/asynchronous-nstextattachments-22/#respond Fri, 09 Sep 2016 15:35:55 +0000 https://www.cocoanetics.com/?p=10276 In the first part of this two-part blog post, you saw how NSAttributedString works with remote images when parsing HTML, namely synchronously downloading them. I teased that I found a way to have NSTextAttachment also work asynchronously. In this second part I will walk you through how this is achieved.

When showing an inline image you usually don’t know the size of the remote image. This means we cannot instruct the text layout engine in NSLayoutManager how much space to reserve for the invisible object placeholder glyph. We need to download the image first and then we can determine how large we want it to be displayed.

Here’s a reminder how layout manager, text storage and text container are connected. This setup is done for us by UITextView and UITextLabel with only the former exposing these objects as properties.

Layout Classes Connections

We don’t want to dirty the layout for the entire attributed text, but just for the placeholder characters. Technically we can reuse the same attachment object instance multiple times, so I created this helper method to find the ranges in the attributed string where this attachment is … um, attached.

import UIKit

extension NSLayoutManager
{
   /// Determine the character ranges for an attachment
   private func rangesForAttachment(attachment: NSTextAttachment) -> [NSRange]?
   {
      guard let attributedString = self.textStorage else
      {
         return nil
      }
        
      // find character range for this attachment
      let range = NSRange(location: 0, length: attributedString.length)
        
      var refreshRanges = [NSRange]()
        
      attributedString.enumerateAttribute(NSAttachmentAttributeName, 
                                          inRange: range, options: []) { 
         (value, effectiveRange, nil) in
            
         guard let foundAttachment = value as? NSTextAttachment 
               where foundAttachment == attachment else
         {
            return
         }
            
         // add this range to the refresh ranges
         refreshRanges.append(effectiveRange)
      }
        
      if refreshRanges.count == 0
      {
         return nil
      }
        
      return refreshRanges
   }
}

We want to be able to re-display the text as well as re-layout it, depending on whether we already reserved the correct space or need to change the layout selectively. This method uses the previous private helper method to trigger re-display.

/// Trigger a re-display for an attachment
public func setNeedsDisplay(forAttachment attachment: NSTextAttachment)
{
      guard let ranges = rangesForAttachment(attachment) else
      {
         return
      }
        
      // invalidate the display for the corresponding ranges
      ranges.reverse().forEach { (range) in
            
      self.invalidateDisplayForCharacterRange(range)
   }
}

For re-layout we have almost the same. I noticed in my tests though that sometimes the display of images does not refresh, so we also trigger a re-display here as well.

/// Trigger a relayout for an attachment
public func setNeedsLayout(forAttachment attachment: NSTextAttachment)
{
   guard let ranges = rangesForAttachment(attachment) else
   {
      return
   }
        
   // invalidate the display for the corresponding ranges
   ranges.reverse().forEach { (range) in
            
   self.invalidateLayoutForCharacterRange(range, actualCharacterRange: nil)
            
   // also need to trigger re-display or already visible images might not get updated
   self.invalidateDisplayForCharacterRange(range)
}

The reverse() in both functions has a special purpose: without it I found that UIKit would sometimes crash in an endless loop if I had dozens of ranges. Maybe Apple doesn’t unit test the invalidation functions being called in a tight loop dozens of times.

Now for our AsyncTextAttachment class which I also teased in the first part of this post. This class will be able to both notify a delegate as well as the layout manager of the successful download of the image. The delegate protocol looks like this:

@objc public protocol AsyncTextAttachmentDelegate
{
   /// Called when the image has been loaded
   func textAttachmentDidLoadImage(textAttachment: AsyncTextAttachment, 
                                   displaySizeChanged: Bool)
}

The properties and the initializer as straightforward as well. To spice it up, there are some additional bells and whistles!

/// An image text attachment that gets loaded from a remote URL
public class AsyncTextAttachment: NSTextAttachment
{
   /// Remote URL for the image
   public var imageURL: NSURL?

   /// To specify an absolute display size.
   public var displaySize: CGSize?
    
   /// if determining the display size automatically this can be used to specify a 
   /// maximum width. If it is not set then the text container's width will be used
   public var maximumDisplayWidth: CGFloat?

   /// A delegate to be informed of the finished download
   public weak var delegate: AsyncTextAttachmentDelegate?
    
   /// Remember the text container from delegate message
   weak var textContainer: NSTextContainer?

   /// The download task to keep track of whether we are already downloading the image
   private var downloadTask: NSURLSessionDataTask!
    
   /// The size of the downloaded image. Used if we need to determine display size
   private var originalImageSize: CGSize?
    
   /// Designated initializer
   public init(imageURL: NSURL? = nil, delegate: AsyncTextAttachmentDelegate? = nil)
   {
      self.imageURL = imageURL
      self.delegate = delegate
        
      super.init(data: nil, ofType: nil)
   }
    
   required public init?(coder aDecoder: NSCoder) 
   {
      fatalError("init(coder:) has not been implemented")
   }
    
   override public var image: UIImage? 
   {
      didSet 
      {
         originalImageSize = image?.size
      }
   }

The imageURL will receive the URL we want to download the image from. Via displaySize you can set an absolute displaySize, but for the dynamic sizing you leave it nil. The maximumDisplayWidth allows us to automatically limit the size of the downloaded image. The delegate is optional and weak. The textContainer will be where we remember the text container in which the image is being displayed.

The NSTextAttachmentContainer protocol defines two functions by which the text attachment is queried by NSLayoutManager. The one for determining the size returns either the fixed displaySize or a suitable size based on the maximumDisplayWidth or the with of the current line fragment. The latter has the wonderful effect of always sizing the image to be as wide as the text view.

public override func attachmentBoundsForTextContainer(textContainer: NSTextContainer?,
                                                proposedLineFragment lineFrag: CGRect, 
                                                      glyphPosition position: CGPoint, 
                                           characterIndex charIndex: Int) -> CGRect
{
   if let displaySize = displaySize
   {
      return CGRect(origin: CGPointZero, size: displaySize)
   }
        
   if let imageSize = originalImageSize
   {
      let maxWidth = maximumDisplayWidth ?? lineFrag.size.width
      let factor = maxWidth / imageSize.width
            
      return CGRect(origin: CGPointZero, 
                    size:CGSize(width: Int(imageSize.width * factor), 
                                height: Int(imageSize.height * factor)))
   }
        
   return CGRectZero
}

The second delegate method, for getting the image, is where we store the “who’s asking?” in the layoutManager property. Also, if we don’t have the image yet – neither in the image property, now the contents property – we start the asynchronous download.

public override func imageForBounds(imageBounds: CGRect, 
                                         textContainer: NSTextContainer?, 
                                         characterIndex charIndex: Int) -> UIImage?
{
   if let image = image 
   { 
      return image 
   }
        
   guard let contents = contents, image = UIImage(data: contents) else
   {
      // remember reference so that we can update it later
      self.textContainer = textContainer
            
      startAsyncImageDownload()
            
      return nil
   }
        
   return image
}

Now the the core of the ingeniousness, the method for asynchronous download. It employs NSURLSessionDataTask for getting the image. Of course we only need to start the download task if there is an imageURL, we don’t have the image contents yet and there is not already another download running.

private func startAsyncImageDownload()
{
   guard let imageURL = imageURL where contents == nil && downloadTask == nil else
   {
      return
   }
        
   downloadTask = NSURLSession.sharedSession().dataTaskWithURL(imageURL) { 
      (data, response, error) in

      defer 
      {
         // done with the task
         self.downloadTask = nil
      }
            
      guard let data = data where error == nil else {
         print(error?.localizedDescription)
         return
      }
            
      var displaySizeChanged = false
      self.contents = data
            
      let ext = imageURL.pathExtension!
      if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, 
                                                         ext, nil)
      {
         self.fileType = uti.takeRetainedValue() as String
      }
            
      if let image = UIImage(data: data)
      {
         let imageSize = image.size
                
         if self.displaySize == nil
         {
            displaySizeChanged = true
         }
                
         self.originalImageSize = imageSize
      }
            
      dispatch_async(dispatch_get_main_queue()) {

         // tell layout manager so that it should refresh
         if displaySizeChanged
         {
            self.textContainer?.layoutManager?.setNeedsLayout(forAttachment: self)
         }
         else
         {
            self.textContainer?.layoutManager?.setNeedsDisplay(forAttachment: self)
         }
                
         // notify the optional delegate
         self.delegate?.textAttachmentDidLoadImage(self, 
                                                   displaySizeChanged: displaySizeChanged)
      }
   }
        
   downloadTask.resume()
}

As a free bonus, I am also setting the fileType UTI with a method from the MobileCoreServices framework. Note that we are always setting the content property and not the image property: the reason for this is that setting image property resets the content property.

In my tests I didn’t specifically need to refresh the UITextView. It looks to me like that is done by the text view’s layoutManager and/or textContainer. In a second scenario where I am using this code however I needed the delegate: using it on an UILabel inside a table view cell. After the download concluded, I needed to reload the affected table view cells so that the would be properly resized to show the image.

Conclusion

The trick is to inform the layoutManager asking about the size and image for the text attachment, when a new image and/or size are available. It only gets tricky if you use an attributed string inside a table view cell and want to cell height to dynamically update.

The sample project is available on my Swift Examples repo on GitHub.

]]>
https://www.cocoanetics.com/2016/09/asynchronous-nstextattachments-22/feed/ 0 10276
Asynchronous NSTextAttachments (1/2) https://www.cocoanetics.com/2016/09/asynchronous-nstextattachment-1/ https://www.cocoanetics.com/2016/09/asynchronous-nstextattachment-1/#comments Wed, 07 Sep 2016 16:45:58 +0000 https://www.cocoanetics.com/?p=10269 In iOS 7, Apple introduced the NSTextAttachment class for embedding images into attributed strings. OS X, pardon macOS, did have this feature much earlier, already as early as 2001 in version 10.0 “Cheetah”. I suspect that they needed 7 years to migrate because the first needed to revamp the inner workings of UITextView and UITextField to natively work with attributed text, as well as modernize CoreText into what is nowadays referred to as TextKit.

With iOS 10 being released, we now have 3 major iOS releases supporting text attachment in standard UIKit views. About time, that we start using text attachments for displaying inline images in rich text.

In DTCoreText – which supports iOS 4.3 and up – I needed to work around the non-existence of NSTextAttachment by creating my own attachment class, DTTextAttachment. It uses the CoreText run delegate callback mechanism to reserve sufficient space in the text view – also custom – to show the image inline.

Simple Inline Images

At its simplest you put your UIImage into the appropriate property and set the bounds to something manageable. If you don’t set the bounds then the image will likely be way too large.

let image = UIImage(named: "cocoanetics")

// create a text attachment for the image
let attachment = NSTextAttachment()
attachment.image = image
attachment.bounds = CGRect(x: 0, y: 0, width: 100, height: 134)

// use convenience initializer that takes attachment as parameter       
let attributedString = NSAttributedString(attachment: attachment)

// set it on UIKit view
textView.attributedText = attributedString

The attributedString here becomes a string of length 1, consisting of a standard unicode object character  and the attachment under the NSAttachmentAttributeName key.

How about HTML?

Most people probably would be lazy and avoid constructing attributed strings by hand. Especially if they learn that they could have it parse HTML via the appropriate init function. The following code shows a simple text I performed to see how Apple is dealing with HTML IMG tags referencing remote images by URL.

let body = "<img src=\"https://www.cocoanetics.com/files/Cocoanetics_Square.jpg\" />"
let data = body.dataUsingEncoding(NSUTF8StringEncoding)

let options : [String: AnyObject] = [
   NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
   NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
]

// parse HTML data      
let attribStr = try! NSAttributedString(data: data!, options: options, 
                                        documentAttributes: nil)

// get the attachment at index 0
let attachment = attribStr.attribute(NSAttachmentAttributeName, atIndex: 0, 
                                     effectiveRange: nil)

// show that the image data is already there
print(attachment?.fileWrapper??.regularFileContents)

When we inspect the resulting attachment, we find that Apple has already (synchronously) downloaded the referenced image data and placed it into the fileWrapper property of the attachment. NSFileWrapper does not have a facility for asynchronously downloading files over the web and so we find the image data already present in the file wrapper’s regularFileContents property.

Since the attachment is buried in the resulting attributed string, we find it a bit harder to set a custom value there. Also we don’t like that this operation occurred synchronously. The same test – with WiFi disabled – doesn’t download the image, and omits the text attachment altogether, resulting in an attributedString of length 0 for the above example. Ugh!

You can see how this can become a problem if you are dependent on the device having a good internet connection for generating the correct attributed string. If you wanted to be independent of internet connectivity, you could reference an image by relative URL and specify the base URL to be the app’s resource folder URL.

Making it Asynchronous

We want to retain control over the downloading of images. So we need an asynchronous version of NSTextAttachment. One that downloads the image as soon as it is needed, and updates the text view as soon as the download is complete.

We want to be able to do this to lazily load the referenced remote image:

// create an async text attachment
let imageURL = NSURL(string: "https://www.cocoanetics.com/files/Cocoanetics_Square.jpg")!
let attachment = AsyncTextAttachment(imageURL: imageURL)

// use convenience initializer that takes attachment as parameter       
let attributedString = NSAttributedString(attachment: attachment)

// set it on UIKit view
textView.attributedText = attributedString

Oh, and we would also like the resulting image to be scaled to fit the width of the text view, regardless of orientation. And the text view should update without us having to call any layout methods.

Asynchronous Image Displaying

In the second part of this series I will show how I got it working. We will be looking at the NSLayoutManager methods and build one that allows us to refresh the display and layout of specific characters.

There is very little information on the web on text attachments and none that I could find for dealing with remote images. I was able to figure out a technique to achieve such behavior, only using public APIs and fully compatible with UIKit.

I am looking forward to describing how to do it in part 2.

 

 

 

]]>
https://www.cocoanetics.com/2016/09/asynchronous-nstextattachment-1/feed/ 1 10269
An Update on Custom Modal Presentations https://www.cocoanetics.com/2016/07/an-update-on-custom-modal-presentations/ https://www.cocoanetics.com/2016/07/an-update-on-custom-modal-presentations/#respond Mon, 04 Jul 2016 20:23:38 +0000 https://www.cocoanetics.com/?p=10252 Customizing a modal presentation of a view controller would be tricky before iOS 7. It got much easier with the addition of the transitioningDelegate protocol. This delegate would be able to vend an animation controller and an interaction controller for presentation and dismissal on the view controller which implemented them.

Without that, if you wanted a “burger menu” then you had to implement a custom container controller like I did with DTSidePanel in DTFoundation 1.4, three years ago. Things have gotten much easier a year ago.

A client of ours liked the way the Linked In app showed related apps in a modal side panel and so I went to research how you would that most reusably nowadays.

I am providing a sample app in my Swift Examples repo on GitHub, named ModalSidePanelPresentation if you care to follow along.

modal side panel

I generally felt that there is duplicate functionality between custom UIStoryBoardSegues and using animation controllers. Where would you put the animation code? And what about supporting unwinding with custom modal presentations?

Both segues and delegates have another problem: both are quite short-lived. The transitioning delegate – like all delegates should be – is defined as weak. A segue is only created for a transition and is deallocated as soon as the transition is over.

Presentation Transitions

To clarify terms: when you present a view controller A from a view controller B, you have a transition animation from A to B. While B is visible and the user interacts with it you call B the presentedViewController and A is the presentingViewController. In short: the presentation is ongoing. It all ends when the user triggers a dismissal of B, either by an unwind seque defined in the storyboard or by calling dismissViewController. The subsequent transition animation leads us back from B to A.

Present → Transition → Presentation → Dismiss → Transition

As of iOS 7, there was something missing to model something that would control the state of the presentation going on between the two transitions. So, in iOS 8, Apple introduced UIPresentationController with UIPopoverPresentationController being a concrete example. The transitioning delegate also got a new delegate method to provide a presentation controller for a view controller.

We had gotten the ideal object for keeping track of the elements of the presentation and dismissal animations. Note though, that there was no way to specify transitioning delegates or presentation controllers from storyboards.

You don’t want to have to add presentation-related code to each view controller which you plan to be presenting a certain modal way. This would unnecessarily duplicate code as well as make it difficult to reuse view controllers with other presentations.

But, there is a way …

Using Segues for Custom Presentations

For a recent client project I worked out the following: we’ll have a custom modal presentation controller which is is also a transitioning delegate vending itself for both transitions. Then we’ll have a custom segue which creates such a presentation controller and sets the transitioning delegate for the presented view controller.

import UIKit

// private variable that sticks around even after the segue is has been released
internal var _modalPresentationController: ModalSidePanelPresentationController!

/// A custom segue that uses the ModalSidePanelPresentationController for presenting a 
/// modal view controller
public class ModalSidePanelPresentationSegue: UIStoryboardSegue
{
   /// Designated initializer
   override init(identifier: String?, source: UIViewController, 
                 destination: UIViewController)
   {
      super.init(identifier: identifier, source: source, 
                 destination: destination)
        
      _modalPresentationController = ModalSidePanelPresentationController(
         presentedViewController: destination, 
         presentingViewController: source)
   }
    
   /// Executes the presentation
   public override func perform()
   {
      destinationViewController.transitioningDelegate = _modalPresentationController
      sourceViewController.presentViewController(
         destinationViewController, animated: true, completion: nil)
   }
}

We overwrote the designated initializer of the segue to create our modal side panel presentation controller and store it in an internal global variable. The perform function simply sets the transitioning delegate and calls the usual function for presenting a view controller.

A Custom Presentation Controller

There is one more detail necessary to complete the setup: the presentation style of the presented view controller needs to be set to .Custom. We’ll do that in the presentation controller’s init. This way we don’t have to change it for all thusly presented view controllers in the story board.

class ModalSidePanelPresentationController: UIPresentationController
{
   override init(presentedViewController: UIViewController, 
                 presentingViewController: UIViewController) {
      super.init(presentedViewController: presentedViewController,
                 presentingViewController: presentingViewController)
        
      // style needs to be custom so that the presentationController transitioning
      // delegate method is being called
      presentedViewController.modalPresentationStyle = .Custom;
   }

The init of our presentation controller passes on the presented as well as the presenting view controller and conveniently stores them in two instance variables.

The implementation of the transitioning delegate is quite boring, please don’t be mad that I am still showing it here. Note though that in true Swift style we do it in an extension to the presentation controller class.

extension ModalSidePanelPresentationController: UIViewControllerTransitioningDelegate
{
   func animationControllerForPresentedController(presented: UIViewController,
      presentingController presenting: UIViewController, 
      sourceController source: UIViewController) 
      -> UIViewControllerAnimatedTransitioning?
   {
      return self
   }
    
   func animationControllerForDismissedController(dismissed: UIViewController)
      -> UIViewControllerAnimatedTransitioning?
   {
      return self
   }

    
   func presentationControllerForPresentedViewController(
      presented: UIViewController, 
      presentingViewController presenting: UIViewController, 
      sourceViewController source: UIViewController) 
      -> UIPresentationController? 
   {
      return self
   }
}

We will implement two methods of the transitioning delegate, one to specify the animation duration, one to carry out the animation. But to know which direction we are animating in (presenting/forward or dismissing/backward) we can use the presentation controller methods presentationTransitionWillBegin and dismissalTransitionWillBegin. This is the ideal place to story the current direction in an instance variable.

Look at these methods in the sample app to see this working. There you’ll also see the actual animation code which I split into separate functions for the forward and backwards animation.

extension ModalSidePanelPresentationController: UIViewControllerAnimatedTransitioning
{
   func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval
   {
      return 0.50
   }
    
   func animateTransition(transitionContext: UIViewControllerContextTransitioning)
   {
      if isPresenting
      {
         animatePresentingTransition(transitionContext)
      }
      else
      {
         animateDismissingTransition(transitionContext)
      }
   }

   // see sample for actual animation functions 
}

I found that this splitting of the animation functions made the code much simpler. In the presentation controller methods we also calculate a frame for the presented view in frameOfPresentedViewInContainerView. This is another feature of presentation controllers: you can do a non-full-screen presentation by specifying a frame. The default is to use the transition container’s bounds.

Conclusion

One more thing I only realized while writing this post was that originally I had a retain cycle. But this was broken by nilling the global variable after the dismissal transition was over.

The sample app demonstrates adding a black view underneath both view controllers to give the effect of stepping outside of the app. And a tap handler lets you dismiss the modal presentation. Oh and round corners during the presentation as well. Not to mention that the second view controller has a light status bar style…

The resulting combo of these two objects, a segue and a presentation controller makes it super simple to add this special effect to any modal segue. All you need to do is replace the segue class with this one.

 

]]>
https://www.cocoanetics.com/2016/07/an-update-on-custom-modal-presentations/feed/ 0 10252
Invalid File Frameworks https://www.cocoanetics.com/2016/05/invalid-file-frameworks/ https://www.cocoanetics.com/2016/05/invalid-file-frameworks/#comments Sat, 14 May 2016 20:10:38 +0000 https://www.cocoanetics.com/?p=10223 It’s been a while since I last submitted a build of prod.ly to the iTunes. So I figured, I should update Cocoapods to the latest version and do a pod update. The archiving went find, but then I saw a new iTunes error when I tried to upload the build.

The error came from the verification process that iTunes runs after you have endured the long wait of uploading the bundle.

Screen Shot 2016-05-14 at 20.53.42

WFT?! Apparently something must have changed, because that worked before!

On iOS, frameworks should never have any other frameworks embedded. Technically they can, but usually they would be redundant because the app that uses the framework probably has a copy embedded as well.

Indeed, when I checked the product, I found that Xcode had added duplicates of all the internal Swift libraries:

Screen Shot 2016-05-14 at 20.55.54

Well, those 13 libswift*.dylib are completely superfluous there. The problem has a very simple solution.

Xcode determines whether or not to include the Swift dynamic libraries from the Embedded Content Contains Swift Code build setting. This seems to default to Yes nowadays.

Now this setting’s name is somewhat misleading because the framework in question does contain Swift code. But I needed to set the setting to No nevertheless because the swift dylibs are completely useless for a framework.

Screen Shot 2016-05-14 at 21.18.13

This is because at runtime the dynamic libraries will be loaded by the app anyway, from within the app’s frameworks folder.

Conclusion

At some time between prod.ly 1.5.0 in (January 2016) and today (May 2016) either the default setting changed, or iTunes added a check looking for those disallowed superfluous files in embedded frameworks.

Each such set of Swift-dylibs adds 19 Megabytes to your app bundle. So it is a good thing that Apple now brings it to your attention when you are wasting space this way.

]]>
https://www.cocoanetics.com/2016/05/invalid-file-frameworks/feed/ 1 10223
Fixing Xcode 7.3 Warnings https://www.cocoanetics.com/2016/04/fixing-xcode-7-3-warnings/ https://www.cocoanetics.com/2016/04/fixing-xcode-7-3-warnings/#comments Sun, 10 Apr 2016 16:54:05 +0000 https://www.cocoanetics.com/?p=10196 The updates to LLVM packaged with Xcode 7.3 cause several new warnings. Most of them related to Swift, but there are also a few bugging us in legacy Objective-C code.

Xcode 7.3. updates Swift to 2.2. The warnings we are about to discuss are mostly deprecations which technically would still work, but will stop to work in Swift 3 which will be released “late 2016”. What’s particularly fascinating is that development of Swift is now driven by an open community process.

For the most part, Xcode provides perfect fix-its for the warnings. But let’s review them one by one as this might provide some enlightenment.

Deprecating C-Style

One of Swift’s design goals is to fit well into the family of C-languages. However some of the C-constructs were found to be confusing to newcomers or not providing any lasting benefits.

The first warning that catches our eye comes from a Swift Evolution proposal #4 by the LLVM master of ceremony himself, Mr. Chris Lattner. I love it to see that even Apple people are adhering to this new process of transparency.

Dropping ++

The proposed fix-it replace i++ with i+=1. Same effect, it increments i by 1.

In this case the same line triggered a second fix-it as well, related to the C-style for loop.

The second proposed fix is more radical, it relates to the entire C-style for loop. This Swift Evolution proposal #7 comes from none other than famous Erica Sadun. Apparently, when not writing books, she proposes enhancements to Swift.

The proposal was much more controversial with several people raising concerns about the performance of replacing C-style for loops with Swift’s for-in. But in the end the proposal prevailed.

Deprecated C-style for

The resulting code is 5 characters shorter (because of the omitted var and the i++) and yet more elegant:

for var i=0; i<numberOfSlices; i++ // before
for i in 0 ..< numberOfSlices      // after

Don’t you love it?

New Selector Syntax

Objective-C allowed us to use any string as selector. That would give use great flexibility in messaging methods along the responder chain, but more often than not might lead to an unexcited “unrecognized selector” crash.

Few people actively use messaging the first responder (und subsequently the responder chain) on iOS, much less so than on OSX where you are almost forced to using it. For example if you have a menu item that is connected to the copy: selector the message would travel up the responder chain until it found a controller (view, window or document) that implements it.

Doug Gregor, working at Apple, drafted Swift Evolution proposal #22 which introduces the #selector syntax. This new syntax lets the compiler check for the existence of selectors at compile time.

Hash selector fix-it

Whereas you previously would only name the method name, you now have to reference the class containing the selector as well. This would have been the target-part of the target/action pattern we have previously used for buttons, gesture recognizers and notifications.

There are two drawbacks of this new syntax which are apparently considered to be less severe than unrecognized selector exceptions. For one, having the class as part of the selector duplicates the target: piece. And it might even contradict the class of the target, when the target is of an unrelated type.

In a recent Mac app I messaged the first responder by leaving the to-target nil. Now I needed to specify any class that would implement the selector:

NSApp.sendAction("reviewBundle:", to: nil, from: nil)                  // before
NSApp.sendAction(#selector(OnlineTabViewController.reviewBundle(_:)), 
                 to: nil, from: nil)                                   // after

Now imagine an expert in responder chains having several different classes which implement this reviewBundle: method. Which would you choose then for the #selector? The consensus seems to be that the cleanest approach would be to define a Swift protocol containing those methods and name this in the #selector.

This is one of few evolutionary changes to Swift which doesn’t decrease the number of characters you need to type to get the job done. But I guess it has some upsides and Xcode forces us to embrace it.

Amongst Others

There are a few additional proposals which have been accepted for Swift 3, which already rear their heads via warnings and fix-its. The one that I bumped into was the one about removing the ability to tag function parameters with var or let. The latter is implicit, you cannot modify function parameter values inside the function. And making it modifiable with var serves no practical purpose because the scope of this variable is limited to the function anyway.

Another argument for this deprecation revolved around beginners being confused between var and inout. The latter will be moved to the right side of the colon in Swift 3.

Bogus Warnings

Those were the Swift 2.2. changes which resulted in deprecation warnings. I also encountered one issue in particular – in legacy Objective-C code – which does not seem to be connected to Swift Evolution.

In Objective-C you often had the pattern that you would query a value which would write the value into a variable by reference. Now I found – on several occasions – that the compiler would now complain that I was using an uninitialized variable, even though I was doing so in a then-block of an if that would only be called in case of success of the query.

To work around this new bogus warning, I changed the logic such that it would always initialize the variable. This example is from DTCoreText where the effective range of glyphs is determined belonging to a single hyperlink. The warning was getting logged in the block at the end of this commit, even though linkURL had to be non-nil to get there.

Fixing a bogus warning

It is very valuable to get informed when accessing non-initialized variables, but there are clearly some edge cases where the warning constitutes a false positive. But a few false positives are not a big deal if they prompt you to make your code easier to read for machine and humans alike.

Conclusions

On balance I am excited about the openness and involvement of people outside Apples inner circle that is the compiler team. Publicly inviting, discussing and implementing proposals for Swift’s betterment has yielded a few interesting improvements already.

For the most part – if you don’t have too much “clever” code you can get by with accepting all the fix-its attached to the deprecation warnings. Those warnings where this doesn’t work should prompt you to make your code more simple to reason about.

In the least we learn a great deal about the thought processes in Swift Evolution by reading the proposals and we get a nice preview of what will be in the future.

 

]]>
https://www.cocoanetics.com/2016/04/fixing-xcode-7-3-warnings/feed/ 1 10196
App-Level Passcode https://www.cocoanetics.com/2016/03/app-level-passcode/ https://www.cocoanetics.com/2016/03/app-level-passcode/#comments Sun, 13 Mar 2016 11:05:43 +0000 https://www.cocoanetics.com/?p=10054 In the latest version of iWoman we are finally coming around to implementing TouchID. Or more precisely: device owner authentication.

iOS 8 introduced the ability to let us use the user’s finger print for authentication. But if that wasn’t set up or otherwise unavailable, we still had to fall back to displaying a keypad via DTPinLock. iOS 9 finally gave us the ability to fall back on the device passcode, just like the user would use to unlock iPhone without TouchID.

The following figure shows the kinds of scenarios possible if the user activates iWoman’s passcode lock feature. If the app is running under iOS 9 (or higher) and if device owner authentication is possible, then we won’t need DTPinLock as fall back.

Passcode Activation

Remember: you cannot have TouchID without a passcode set. Being able to “evaluate the device owner authentication policy” means that the owner can authenticate with finger print or device passcode, but we don’t know which. We only see if authentication was successful.

The iPhone 6, introduced with iOS 8, was the first to introduce a fingerprint sensor. So TouchID is possible there, but the OS does not automatically fall back to the device PIN. This means we still have to show DTPinLock for the case that TouchID fails, or if it is not active.

We found this easy to test: all you need is to place an unregistered fingertip onto the TouchID sensor. This causes the secondary authentication button to appear, which you can freely label. We chose “Enter iWoman Passcode”. The same button also appears on iOS 9, but there we named it “Enter Device Passcode” to make it clear to the user that this is a different code.

For sake of completeness, on devices that have no TouchID support, we still use DTPinLock for the entire app authentication.

Since the app passcode is all about user privacy, we need to cover the app’s UI until the user unlocks it. For everything before iOS 9, we use DTPinLock (with optional background image) as cover. But because iOS 9 has its own passcode pad, we went with an ordinary blur view with vibrant labels.

The flow at app launch or resume is slightly more complex. I’ll break it down into two parts: blocking the app’s main view and starting authentication.

Blocking if Necessary

There are two policies which the LocalAuthentication framework can evaluate for us:

  1. DeviceOwnerAuthenticationWithBiometrics (>= iOS 8)
  2. DeviceOwnerAuthentication (>= iOS 9)

As often found in real life, longer is not necessarily better. The first one only has support for TouchID, the second one uses TouchID first and when this fails the device passcode is used as fallback.

Initially I had considered modifying DTPinLock to handle TouchID as well, but that proved to be too much hassle. So I decided on a simpler approach, to have a custom view controller with a visual effects blur view and some text explaining that the app is locked.

iWoman Modern Lock Screen

A tap gesture recognizer starts the unlocking process. I realized that this was necessary because you end up back with this block view visible if authentication fails or gets cancelled by the user.

Native Unlocking

The second part I’d rather show in Swift. At the beginning, it checks if we’re running iOS 9. If yes, then we use the better authentication policy and name the fallback button accordingly. This button appears once the fingerprint sender failed to detect a registered finger.

func startUnlocking()
{
   let context = LAContext()
   var policy: LAPolicy = .DeviceOwnerAuthenticationWithBiometrics
 
   if #available(iOS 9.0, *)
   {
      // use device passcode as fallback if available
      policy = LAPolicy.DeviceOwnerAuthentication
 
      context.localizedFallbackTitle = NSLocalizedString("ENTER_DEVICE_PASSCODE",
         comment: "Button Label for falling back to device passcode when Touch ID fails")
   }
   else
   {
      // we will be using DTPinLock as fallback, so we name it differently
      context.localizedFallbackTitle = NSLocalizedString("ENTER_APP_PASSCODE",
         comment: "Button Label for falling back to app passcode when Touch ID fails")
   }
 
   var error: NSError?
   let hasTouchID = context.canEvaluatePolicy(policy, error: &amp;error)
 
   if hasTouchID
   {
      let reason = NSLocalizedString("TOUCH_ID_REASON",
         comment: "Label shown below 'Touch ID for iWoman' stating the reason for asking for it")
      context.evaluatePolicy(policy, localizedReason: reason) {
         success, error in
 
         dispatch_async(dispatch_get_main_queue()) {
 
            self.needsToAuthenticate = false
 
            if success
            {
               self.hideBlockingView(true)
            }
         }
      }
   }
}

Note that nothing is done in case that hasTouchID comes out as negative. The reason for this is that the blocking view is the Pin Unlock Controller from DTPinLock and so we will get a delegate callback if the user enters the correct passcode there.

In case of successful “device owner authentication” we can dismiss the blocking view. This is slightly confusing, because on iOS 8 we get into the completion handler right after TouchID failed. On iOS 9 there the fallback authentication via device passcode precedes the completion handler. Put differently: on iOS 8 failure means that you need to present your fall back mechanism, on iOS 9 failure means that both TouchID and device passcode failed.

The whole thing gets slightly more complicated by the situation that the user might enable/disable TouchID/passcode while your app is backgrounded. I have code in there as well to deal with these transitions.

Conclusion

The introduction of device owner authentication in iOS 9 (by TouchID and/or device passcode) is a boon for apps which want to prevent somebody unauthorized from gleaning infos not meant for their eyes. Before iOS 9, you could work around the missing fallback mechanism by using a commercial component like DTPinLock.

On devices before iOS 9, we had the problem that there might not be a way to get back into the app if the user forgot the app-level code. This is nicely out of our hands with iOS 9 because the user cannot “forget” fingers. If the device passcode is forgotten, then this is not our problem as app developers.

I’m glad that we invested a couple of days to finally implement the entire process, fallback and all, in iWoman. DTPinLock is available for licensing on our component store.

]]>
https://www.cocoanetics.com/2016/03/app-level-passcode/feed/ 3 10054
Swift Golf https://www.cocoanetics.com/2015/12/swift-golf/ https://www.cocoanetics.com/2015/12/swift-golf/#comments Tue, 22 Dec 2015 09:36:25 +0000 https://www.cocoanetics.com/?p=10004 For a project I needed a way to get a reference to a UISwitch contained in the contentView of a UITableViewCell. I remembered from my research for my Swift talk that there was this handy is operator, so I was excited to tweet that I had finally found the first good use for it.

My original proposition was:

Bildschirmfoto 2015-12-22 um 09.44.33

I was slightly embarrassed by the fact that I had taken the screenshot before checking Xcode for warnings. One problem here is that view is a UIView subclass and requires casting to be returned as UISwitch. Also a function returning something needs to have returns for all code paths. So there is a return nil missing at the bottom.

It turns that there are multiple ways to skin a cat. From this initial post an interesting game of Code Golf ensued. One that taught me about several interesting ways to approach this problem, some more elegant than others. First of all, I didn’t know what Code Golf was before this time. It’s basically people competing for who can formulate the shortest solution to a programming problem. Strictly speaking, people are trying to use the least characters, but with Swift I place more value on elegance and uniqueness of the approach.

Swiftly Code Golfing

So, let’s go through the variants and judge them in terms of elegance and teaching value:

Variant 1: if-for-let

func switchInCell(cell: UITableViewCell) -> UISwitch?
{
   for view in cell.contentView.subviews
   {
      if let ret = view as? UISwitch
      {
         return ret
      }
   }
    
   return nil
}

This is a classic for loop where an if let tries to cast the enumerated item as UISwitch, which is evaluated as false if the cast fails.

Variant 2a: filter

func switchInCell(cell: UITableViewCell) -> UISwitch?
{
   return cell.contentView.subviews.filter { (view) -> Bool in
             return view is UISwitch
          }.first as? UISwitch
}

Here the loop is carried out by the functional filter function. The block is repeated for each element in the subviews array and returns true if view is UISwitch. From this you get an array containing zero or all switches. We take the first element (or the last if we wanted to save one more character), but again we have to cast it into UISwitch for the function return.

Variant 2b: shorter filter

func switchInCell(cell: UITableViewCell) -> UISwitch?
{
   return cell.contentView.subviews.filter { 
             $0 is UISwitch 
          }.first as? UISwitch
}

Orta, who has a background in Ruby and functional programming, gets extra points for using the dollar parameters in daily use. You can use these in lieu of the parameter list. Shorter and slightly more elegant than 2a.

Variant 3: for+where

Orta also had his hand in this contender by suggesting to use a where clause in combination with for.

func switchInCell(cell: UITableViewCell) -> UISwitch?
{
   for view in cell.contentView.subviews where view is UISwitch
   {
      return view as? UISwitch
   }
}

This was the first suggestion that taught me something new. I knew about using where with if-let statements, but not that you could use it in conjunction with for. But so far we still have to use this ugly cast with the return.

Variant 4: flat map

This “wart” was remedied in the suggestion by Joseph Lord. A flat map to the rescue!

func switchInCell(cell: UITableViewCell) -> UISwitch?
{
   cell.contentView.subviews.flatMap { $0 as? UISwitch }.first
}

The flatMap infers the element type of the resulting array from the returned values. Magic! We don’t need a type cast anymore.

Variant 5: for-case-let

Boom! My mind was blown a third time by Andy Calderbank

func switchInCell(cell: UITableViewCell) -> UISwitch?
{
   for case let retView as UISwitch in cell.contentView.subviews
   {
      return retView
   }
}

No type cast, and you see a combination of words that you have to read several times to grasp the ingenuity: for case let as in. The case selector lets you do the for enumeration but filter out certain elements via a let. If variant 1 and 3 had a child, this would be it.

Variant 6: generic extension

Several people contributed to my final solution, the one I ended up using. The first person suggesting a generic approach was Alexsander Akers. Davide De Franceschi gave me the idea of extending CollectionType as opposed to UIView. I had used the approach of passing T.Type in generic methods previously when writing my own Parse SDK for use in a client project.

extension CollectionType
{
   func firstElementOfType<T>(type: T.Type) -> T?
   {
      return flatMap { $0 as? T }.first
   }
}

func switchInCell(cell: UITableViewCell) -> UISwitch?
{
   return cell.contentView.subviews.firstElementOfType(UISwitch)
}

This takes flat map (variant 4) and makes it generic. This way any collection gains the ability to return the first element of a given type. And because of the flatMap the result already has the correct type, namely T.

Conclusion

We learned several new ways to pick out a specific element from a collection. I found the discussion exhilerating and uniquely instructive. In particular combining for + where and for + case let were new to me. I am sure that I will soon find more uses for these.

Playing Code Golf in Swift is not about brevity, but mostly about beauty, elegance and code readability. Because of this I ended up with an approach that reads like plain english to me: “from the cell’s content view’s subviews, get the first element of type UISwitch”. Anybody can understand that.

I am sure that this was not last last game of Swift Golf we played. Stay tuned on my Twitter @cocoanetics to take part in the next game we might play.

]]>
https://www.cocoanetics.com/2015/12/swift-golf/feed/ 2 10004
XLIFF Fix https://www.cocoanetics.com/2015/11/xliff-fix/ https://www.cocoanetics.com/2015/11/xliff-fix/#respond Tue, 24 Nov 2015 16:14:00 +0000 https://www.cocoanetics.com/?p=9993 I reported in an earlier post that there are some gotchas trying to localize an app with the combination of Xcode’s XLIFF export and POEditor.com. The latter stated that it is not their fault and somebody responsible for the former thanked me for my sample.

A future fix in Xcode notwithstanding, I found it necessary to create the Localizable and Main storyboard strings file from the XLIFF files exported from the translation site. This way I could be certain that the translations I care about all all accounted for.

The XLIFF format is a simple XML format. As such it can be parsed by means of NSXMLParser in contrast to strings files for which I had to create lots of custom code in the past. Then the exercise is simply to output the strings contained in the target tags into appropriately named strings files.

As a bonus exercise I wrote a command line utility in Swift which can even deal with multiple XLIFF files in sequence. It creates an lproj folder for each language and there it outputs the strings files contained in the XLIFF. The screenshot shows the main part of the utility.

XLIFFix main

In Swift, you access the argument count via Process.argc and the individual arguments via Process.arguments. Fortunately those are already expanded – if you have wild cards in the name – and in swift String format. Note that contrary to C there is no main function, the code just starts. I grouped the code for writing one strings file into a writeFile function.

The other thing I learned today is to properly write “for i in the range from 1 to – not including – something”. You use the key word “in” and the range operator.

Conclusions

It was interesting to write my first command line utility in Swift. Once you know about a few differences that Swift has here compared to C or Objective-C the rest is straightforward. You can leverage all you know from working with Apple’s Foundation framework.

The source code for XLIFFix utility is on available on GitHub. If you find it useful and have some comments please get in touch.

]]>
https://www.cocoanetics.com/2015/11/xliff-fix/feed/ 0 9993
Hero Rays https://www.cocoanetics.com/2015/11/hero-rays/ https://www.cocoanetics.com/2015/11/hero-rays/#comments Mon, 09 Nov 2015 07:31:05 +0000 https://www.cocoanetics.com/?p=9980 Our app prod.ly recently got the ability to award achievements to users for a variety of activities. Now, achievements have little impact if you cannot translate those into a rush of dopamine for the winning user. To achieve that, we are planning to have a pop up show as soon as the achievement is awarded, congratulating the user.

From psychology we know that if something is colourful and has animation it has a larger emotional impact than something that is static and B/W. One ingredient that is often used to heighten the effect are what I call “Hero Rays”. In this tutorial I am showing how I achieved them.

Hero Rays are arc slices emanating from a central point. They are displayed on a colored back and should be slowly rotating clockwise for extra effect. Here’s a gif I made of the final result. Note that this look is solely for the purpose of this sample.

Hero Rays

I had the first version of this ready after about one hour. I was using a CAShapeLayer with the rays being built from individual arc slices. The core function is the one to build these rays.

func raysPath()->UIBezierPath
{
    let center = CGPoint(x: CGRectGetMidX(self.bounds),
                         y: CGRectGetMidY(self.bounds))
    let radius = sqrt(center.x * center.x + center.y * center.y)
    let numberOfSlices = 16
    let raysPath = UIBezierPath()
    let oneRayRadians = (2.0 * CGFloat(M_PI)) / CGFloat(numberOfSlices)
 
    for var i=0; i < numberOfSlices; i++
    {
        // skip every second slice, for background to show
        if i%2 != 0
        {
            continue;
        }
 
        // make one slice
        let ray = UIBezierPath(arcCenter: center,
            radius: radius,
            startAngle: CGFloat(i)*oneRayRadians,
            endAngle: CGFloat(i+1)*oneRayRadians,
            clockwise: true)
        ray.addLineToPoint(center)
        ray.closePath()
 
        // add all slices to the main path
        raysPath.appendPath(ray)
    }
 
    return raysPath
}

Can you spot the use of Phytagoras? This I did so that the radius of the slices would always be sufficiently large as to cover the view, even if it is of rectangular shape. Every slice is achieved by following an arc segment along the radius, adding a line to the center and then closing the sub path. Every other slice is left out so that the background – in secondary color – can be visible.

Of course a custom view like this should be @IBDesignable so that it can be rendered in Interface Builder, as well as have some @IBInspectable properties so that you could quickly adjust the colors in the storyboard. I decided on being able to set a stroke color and ray color on top of the view’s background color which is inspectable by default.

Designing Hero Rays

On more than one occasion I found that you have to add copious amounts of if let checks to avoid the IB agent from crashing while updating the component. Supporting live rendering in Interface Builder seems to force you to be even more defensive in your Swift code.

Animated Conversation

There are three methods how you can get the rays to be rotating:

  1. NSTimer
  2. CADisplayLink
  3. CAAnimation

I came up with the first one of these by myself, but when I showed off my work on Twitter I was quickly informed that NSTimer is bad for driving animations because it might get deployed or misaligned with the device frame rate causing missing frames and thus small jumps in the ray rotation.

CADisplayLink can be configured to drive your animation on every frame of the device. Indeed, the animation did feel much smoother in Simulator when I tried it. With both kinds of timing ticks I simply rotated the shape layer around the z axis – the one that sticks out of the screen – by 0.01 radians.

But the most unique suggestion came from Philippe Converset, and it ended up the way I did it:

For an infinite rotation, use CABasicAnimation on transform.rotation.z form 0 to 2*M_PI with INFINITY repeatCount.

I am starting the rotation animation as soon as the view is added to a superview and I am stopping it – to be safe – as soon as it is removed.

override public func willMoveToSuperview(newSuperview: UIView?)
{
    super.willMoveToSuperview(newSuperview)
 
    // animate while visible
    if let _ = newSuperview
    {
        startAnimating()
    }
    else
    {
        stopAnimating()
    }
}

The referenced animation functions are:

func startAnimating()
{
    let anim = CABasicAnimation()
    anim.keyPath = "transform.rotation.z"
    anim.toValue = CGFloat(2.0 * M_PI)
    anim.duration = 12 // seconds per rotation
    anim.repeatCount = Float.infinity
 
    raysView.layer.addAnimation(anim, forKey: "rotate")
}
 
func stopAnimating()
{
    raysView.layer.removeAllAnimations()
}

By comparison, the variant using a CABasicAnimation is the one with the least code and this is why I prefer it. My instinct tells me that the system should be smart enough to drop frames – if it gets too busy to render at 60 FPS – to still preserve the feeling of the same rotation speed.

Layer Abandon

I was quite happy with the result so far, but it was not perfect. In particular the frame of the containing HeroRaysView animated at a different speed than the center of the rays if the size changed. There is no way how you can synchronise CALayer animations with those done by UIKit.

So I ended up replacing the CAShapeLayer with a regular UIView that has the drawing of the rays bezier path in its drawRect. This way I can adjust the size of the sub view in perfect harmony with the outer view that clipsToBounds. On the animated GIF below you can see how the center of the rays visually remains at he same spot behind the text label.

Rotating Hero Rays

Another method that might also have worked would have been to wrap the CAShapeLayer into a UIView by means of setting it’s layerClass. But the method I chose resulted in simpler code, which is why I preferred it.

Conclusion

We have achieved Hero Rays! The sample code for this tutorial is on my Swift Examples repo on GitHub. The public part is the HeroRaysView.swift, the drawing of the rays ended up in HeroRaysDiscView.swift. I think you will agree that the result is beautiful and – most importantly to us engineers – the final code is beautiful and simple, too.

]]>
https://www.cocoanetics.com/2015/11/hero-rays/feed/ 1 9980
Getting the Today Widget Look Right https://www.cocoanetics.com/2015/10/getting-the-today-widget-look-right/ https://www.cocoanetics.com/2015/10/getting-the-today-widget-look-right/#comments Sun, 25 Oct 2015 17:51:28 +0000 https://www.cocoanetics.com/?p=9932 One of my apps has a notification center widget. In this blog post I am exploring a few ingredients which are necessary to have the look go well with Apple’s original widgets.

Contrary to normal apps, today widgets get “assigned seating”, i.e. iOS places it into the today tab in Notification Center in the place where the user wants it to be.

People view Today widgets in the Today area of Notification Center. Because people configure the Today area so that it displays the information they value most, design your widget with the goal of earning a place among the user’s most important items.

The UI of your widget is contained in a view controller. In my case I derived it from UITableViewController so that I can get the typical table look that other widgets, like the Stocks Widget, examplify.

Today Widget

This screen shot shows several of the important factors for the widget to “feel at home”. The first being the left margin. The HIG instructs us to align the text with the label.

The problem with using a table view is that if you just go with the default margins you get a selection box that does not cover the entire width. To fix this we need to make our widget (and thus the table view) span the entire width, but inset the labels itself.

We learn about the correct inset value from a delegate method. I am storing away the original margin insets for later reference. I return zero for the left margin and only half the proposed value for the bottom. We have a “Last Update” label at the bottom and now separating line at the bottom, so with the original value the space would be way too large.

- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets 
{
   // store default margins for later reference
   _defaultMarginInsets = defaultMarginInsets;
 
   // margins are handled inside of table view
   defaultMarginInsets.left = 0;
 
   // reduce bottom margin because we have an "open" bottom row without separator
   defaultMarginInsets.bottom /= 2.0;
 
   return defaultMarginInsets;
}

iOS apparently automatically configures table view separators to have a vibrancy effect. On the screenshot you can see the horizontal lines changing in color to amplify the blurred springboard behind it. If you were to set the separatorEffect property to nil you get plain lines.

In the view controller’s viewDidLoad I am adding a footerView to the table to hide the bottom-most separator line.

   // automatic row sizing
self.tableView.estimatedRowHeight = 44.0;
 
// get separators to look like Apple's
self.tableView.separatorEffect = [UIVibrancyEffect notificationCenterVibrancyEffect];
self.tableView.separatorColor = [UIColor colorWithWhite:1.0 alpha:0.5];
 
// hide separator on bottom row
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 
                                                               self.tableView.frame.size.width, 1)];
self.tableView.tableFooterView.backgroundColor = [UIColor clearColor];

At first I did have custom table view cells for the main content, but I found that it is simpler to get the left inset if I instead use classic “right detail” table view cells. There you only have to set the separatorInset of the cell to get both separator as well as textLabel to line up with the title.

// setting separatorInset apparently also insets the entire cell
cell.separatorInset = UIEdgeInsetsMake(0, _defaultMarginInsets.left, 0, _defaultMarginInsets.right);

The separators already have the notification center vibrancy effect attached, there are two more things that need it. First the selectedBackgroundView of the cells needs to be a certain shade of vibrant. With some experimenting I found the following to be looking perfect:

UIVibrancyEffect * effect = [UIVibrancyEffect notificationCenterVibrancyEffect];
 
UIVisualEffectView * effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
effectView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
effectView.frame = cell.contentView.bounds;
 
UIView *view = [[UIView alloc] initWithFrame:effectView.bounds];
view.backgroundColor = self.tableView.separatorColor;
view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[effectView.contentView addSubview:view];
cell.selectedBackgroundView = effectView;
 
cell.selectionStyle = UITableViewCellSelectionStyleDefault;

It is still a mystery to me how UIVibrancy uses the contentView’s subview’s colors to arrive at the final effect. But since the section highlight should look exactly like the separators I simply used the seperatorColor for it. You can see the result behind the “Team Volume” row in the screenshot above.

The third vibrant item after separators and selection highlight, is also mentioned by the HIG:

In general, use the system font in white to display text. White text looks good on the default Notification Center background. For secondary text, use the system-provided vibrant appearance.

I already put the main labels in white. The detail labels on the right side depend on where the movement of the number went. This has a bit of a neon touch, primary colors work well there.

Screen Shot 2015-10-25 at 18.33.37

The secondary text in my example is the time the numbers where updated. I had this in solid gray before but in order to match up with the HIG I changed it to be vibrant, too. Also I had it centre-aligned, but since everything is left aligned with the title, I made this one left-aligned as well.

Now how do I get the alignment to work, but still get the vibrancy effect? Turns out it is quite simple if you move the standard textLabel from its usual place in the view hierarchy to be a subview of the effect view’s contentView.

@import NotificationCenter;
 
@interface VisualEffectCell : UITableViewCell
@end
 
@implementation VisualEffectCell
{
   UILabel *_label;
   UIVisualEffectView *_visualEffectView;
}
 
- (void)layoutSubviews
{
   [super layoutSubviews];
 
   if (!_visualEffectView)
   {
      UIVisualEffect *effect = [UIVibrancyEffect notificationCenterVibrancyEffect];        
      _visualEffectView = [[UIVisualEffectView alloc] initWithEffect:effect];
      _visualEffectView.frame = self.contentView.bounds;
      _visualEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
                                           UIViewAutoresizingFlexibleHeight;
      [self.contentView addSubview:_visualEffectView];
 
      [_visualEffectView.contentView addSubview:self.textLabel];
   }
}
 
@end

A final touch you cannot see in the screenshots is that the table view rows are linking to different places in the app. Only the time stamp row is inactive as it only contains supplementary information which is not reflected anywhere in the app.

There is a bug I found when using table view cells to make up your today widget. Due to the magic iOS does to the table view to make it translucent automagically you will find that the table view cells only react to touches if you tap the labels. The workaround is to give cells an invisible background color with a high enough alpha.

// to preserve tappability, without that only labels are active
cell.contentView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.01];

This is ugly and should not be necessary, but it works…

Conclusion

Thanks to Simon Booth who helped me understand the combination of vibrancy and labels. He also inspired me to look up the section on today widgets in the HIG.

If you adhere to the recommendations of the Apple iOS HIG and try to mimic Apple’s style in their own widgets then you can come pretty close to perfect. In summary the elements for perfect Today Widgets are:

  • Left-align all table view rows with the widget title
  • If using table view, make it wide enough for selection to cover whole width
  • Separators, selection background views and secondary text labels should be using the vibrancy effect
  • Link from information to relevant parts of your app

The today screen is the one place in iOS where content from different places is displayed next to each other. Which is why extra value should be placed on getting the look right, as to not insult the user’s sense of beauty.

]]>
https://www.cocoanetics.com/2015/10/getting-the-today-widget-look-right/feed/ 3 9932
Swiping Away SFSafariViewController https://www.cocoanetics.com/2015/10/swiping-away-sfsafariviewcontroller/ https://www.cocoanetics.com/2015/10/swiping-away-sfsafariviewcontroller/#comments Thu, 22 Oct 2015 09:58:48 +0000 https://www.cocoanetics.com/?p=9913 A client of ours asked to be able to dismiss Safari View Controller via swipe, as seen in Tweetboot 4. We first tried another open source project which supposedly was the solution that Paul from Tapbots came up with, but found some issues with that. Here’s now our working solution, by Cocoanetics team member Stefan Gugarel.

Paul Hadad from Tapbots wrote:

This is an explanation of the SFVC “hack” i’m using. @stringcode and I independently came up with same trick.

He referred us to a blog post by stringcode86. The article shows how SFSafariViewController can be swiped away on the left side.

When testing this project my client and me found some stuff that is not working like expected:

  • There were situations where the SFSafariViewController was not getting released. When you want to present a new one this does not work because you can only have one instance of SFSafariViewController at one point in time. So keep this in mind.
  • In an another situation a white ViewController was shown instead of SFSafariViewController.

This project is done with interactive transitions for modal presentation. I tried a lot of stuff to solve the problems but I ended up switching the presentation to push / pop. With this change it seems that all earlier problems are gone.

Swiping Away SFSafariViewController

One difficult part for me was that in the following sample I want to have a UINavigationBar on my first ViewController but not on the second (SFSafariViewController). The only way I found is to show / hide the NavigationBar in the corresponding ViewControllers in viewWillAppear methods. This also works for the interactive transition between our two view controllers.

override func viewWillAppear(animated: Bool) 
{
  super.viewWillAppear(animated)
 
  self.navigationController?.setNavigationBarHidden(true, animated: animated)
}

Another thing I found out is from what I mentioned earlier: When I come back from Safari and try to press the Show button quickly after that, it is not working. This is because the SFSafariViewController is not released at this point and I can’t show a new one. So I disabled my show button when I tap on it and enable it again in viewWillAppear. With this the user experience is best.

The SwipeSafari sample code can be found on our Swift Examples repo. If anybody ever figures out how to fix the above mentioned project we’d be interested to learn how you managed to fix it.

]]>
https://www.cocoanetics.com/2015/10/swiping-away-sfsafariviewcontroller/feed/ 1 9913
Introducing Core Image https://www.cocoanetics.com/2015/04/introducing-core-image/ https://www.cocoanetics.com/2015/04/introducing-core-image/#respond Thu, 30 Apr 2015 13:00:49 +0000 http://www.cocoanetics.com/?p=9616 Barcodes with iOSI published a section from my book Barcodes with iOS on DZone. My book is not only about barcodes but also gives short introductions to a plethora of other frameworks which you might not normally come into contact with.

The Core Graphics framework is written in pure C, meaning that it’s impossible to use CGImage instances directly with UIKit. Apple created UIImage as an Objective-C wrapper class around CGImage to bridge the gap. We explore Core Image in this article.

My book currently available at a discount via the promo code mentioned on DZone.

At their core, images consist of colored pixels. Depending on the color space, they might have different numbers of “channels,” most commonly red, green, blue, and alpha. Usually one byte is used per channel. The size in bytes for such a bitmapped image is calculated as width x height x bytes_per_channel x number_of_channels.

Core Graphics—a.k.a. Quartz—represents such bitmapped images as CGImage instances. The Core Graphics framework is written in pure C, meaning that it’s impossible to use CGImage instances directly with UIKit. Apple created UIImage as an Objective-C wrapper class around CGImage to bridge the gap. UIImage instances usually carry a CGImage in their belly that contains the actual image data.

Fig1

Figure 1 Finished QR Code Builder app with printed output

When manipulating images in UIKit or Quartz, you never get the benefit of the GPU. That’s why Apple created Core Image as a framework for manipulating images in real time with the full benefit of hardware acceleration by the graphics processor.
In Core Image you don’t deal with individual pixels but rather with manipulation steps. Each such step, represented by a CIFilter, is a recipe for manipulating images represented by CIImage instances. If you chain multiple manipulation steps, Core Image compiles those down to a single GPU program, called a shader. When you request the final output of such a filter chain, the initial input is loaded on the GPU, the compiled shader is run, and you receive the resulting output. Figure 5.4 shows a CGImage being turned into a CIImage, the chained filters doing their work on that, and a new CGImage being created via a CIContext.

CIImage instances can be created from a wide variety of sources. Static images will usually come from CGImage instances. You can also pass CVPixelBuffer instances if you want to handle live video coming from an AVCaptureDevice.
Most Core Image filters have an inputImage parameter for supplying the source image. One category of Core Image filters—the so-called generators—don’t, because they themselves are able to generate images. Generators can serve as input for other filters,

Figure 2 Core Image filter chain

Figure 2 Core Image filter chain

or you can simply poll their output. For example, you can use CIConstantColorGenerator for creating images consisting of a single solid color or CICheckerboardGenerator for creating an image with a checkerboard pattern.

Let’s try out a simple Core Image generator by creating an 8 x 8 checkerboard suitable for display with a UIImageView. Note the use of CIColor for specifying colors and CIVector for specifying an x-y offset. Those are the typical immutable parameter objects used by Core Image. Values are specified as NSNumber objects:

Listing

Generators output CIImage instances via their outputImage method. To use one with UIKit, you need to render it into a CGImage by means of a CIContext. As you can see in the preceding example, Core Image doesn’t have any knowledge of the device’s content scale, which would be 2 for Retina displays. Because of this, you need to double the size of the generated image and then specify this scale in the method that makes a UIImage out of the CGImage.

This should give you enough information about the general workings of Core Image generators.

My book currently available at a discount via the promo code mentioned on DZone.

]]>
https://www.cocoanetics.com/2015/04/introducing-core-image/feed/ 0 9616
Skipping Copy Phase Strip https://www.cocoanetics.com/2015/04/skipping-copy-phase-strip/ https://www.cocoanetics.com/2015/04/skipping-copy-phase-strip/#comments Thu, 30 Apr 2015 05:26:44 +0000 http://www.cocoanetics.com/?p=9602 If your app contains frameworks or extensions you are recently beginning to notice a new warning popping up when building for releases: “skipping copy phase strip, binary is code signed”. Here’s how to fix it.

For the longest time, only on OS X you could have extra binary code in your app bundle. App bundles have a dedicated location for frameworks, but Apple didn’t want anybody to create dynamic frameworks for iOS. This changed at WWDC 2014, when Apple needed to permit additional binaries.

Now that the Apple Watch is out, we know that those extra binaries can take these forms:

  • Dynamic Frameworks
  • iOS Extensions (like for Sharing or Today)
  • WatchKit Extensions

Xcode is evolving to better support the patterns necessary for creating modern apps which make use of these technologies.

Embedded Binaries

Before Xcode 6.3 you needed to have an extra step in your build phases to copy a dynamic framework into the app bundle.

Old-Style Embedding

If you forgot this then your app would build and link, but crash as soon as your code was accessing any of the framework functions. This extra build step was somewhat similar to copying bundle resources. You could already see the checkbox to insure that the copied framework would also be code signed.

In Xcode 6.3, this framework copying was moved to a different location. Apple deemed this process important enough to warrant its own Embedded Binaries section on the General tab.

Embedded Binaries

Now you no longer need to mess with the path of the binary product, but rather you can pick it from a list of products. This is especially handy if you have the framework code in a sub-project.

There is still a separate box for linking which can still include static and dynamic libraries and frameworks, even though it makes little sense to include an embedded framework without linking to it.

Stripping

Compiled code usually contains debug information. This debug stuff is helpful for inspecting running code in debugger, but less so for the optimized code you would ship in distribution builds. Therefore it gets stripped out when doing an Archive build.

The problem here is that PBXCp is unable to strip out debug symbols from signed binaries because this would invalidate the digital signature. So if you have a project that was created before Xcode 6.3 you will now get a warning like this:

Strip Warning

It does not matter which of the above mentioned methods you are using for the embedding. There is no stripping for either of them. Before Xcode 6.3 the default setting for “Strip Debug Symbols During Copy” was set to NO for Debug builds and YES for Release builds.

Strip Settings

This explains why you are only seeing this pesky warning when doing release builds (via Archive for TestFlight or App Store).

To fix the warning simply change both values to NO. Removing them does not work because the default value is still YES for both. The project templates that came with Xcode 6.3 have these turned off by default. Only projects that were started with older templates still have YES on the Release line.

Conclusion

Ever now and then Xcode improves and changes default settings of project templates. New warnings appear and we get annoyed by them. But every fixed warning teaches us something new the inner workings of the build process. So don’t go about ignoring warnings, but take a minute to figure out what their reason is and how to fix them.

]]>
https://www.cocoanetics.com/2015/04/skipping-copy-phase-strip/feed/ 12 9602
Implementing an In-App App Store https://www.cocoanetics.com/2015/03/implementing-an-in-app-app-store/ https://www.cocoanetics.com/2015/03/implementing-an-in-app-app-store/#respond Fri, 13 Mar 2015 11:11:34 +0000 http://www.cocoanetics.com/?p=9547 If you have an app like prod.ly, which shows a timeline of user’s messages you probably want interactive hyperlinks. In two previous posts I showed how I am customizing UILabel to customize hyperlinks drawing, as well as how to make them react to touches. For most links a UIWebView will do to show the content.

But not for content that people can purchase and/or download from iTunes or the Apple App Store. For this, there is SKStoreProductViewController. This post shows how to implement it.

Apple introduced this view controller in iOS 6. Indeed it might be one of the most overlooked additions to StoreKit, as I don’t remember see it every in use in an iOS app. The following figure shows how the prod.ly app looks like in this view controller. Almost like in the App Store app, which is why I like to call it my “In-App App Store”.

In-App App Store

Apple had switched to using PHG as provider of their affiliate platform in late 2013. At this time there was no way to earn money with presenting by presenting other people’s products. So the primary use case probably was for developers to promote their other apps and let users download those right from within their app.

Two years later, in iOS 8, Apple finally added the ability to add an affiliate ID and campaign identifier. I can imagine that many enhancement requests must have been filed to that effect. Especially for apps with a lot of usage making a cut on selling other people’s content can be worthwhile.

Regardless of with our without taking part in the affiliate program, the following details how to implement the view controller.

Parsing the ID out of iTunes URLs

For presenting the store view controller you need the iTunes ID. This is the long number contained in all app store or iTunes URLs. To get it out of an NSURL I use this code:

NSNumber *PRYiTunesIDFromURL(NSURL *URL) {
   if (![[[URL host] lowercaseString] containsString:@"apple.com"])
   {
      return nil;
   }
 
   NSString *string = [URL path];
 
   NSRegularExpression *regex = 
       [NSRegularExpression regularExpressionWithPattern:@"id([0-9_]+)" 
                                                 options:0 
                                                   error:NULL];
   NSRange entireString = NSMakeRange(0, [string length]);
   NSArray *matches = [regex matchesInString:string 
                                     options:0 
                                       range:entireString];
 
   if ([matches count])
   {
      NSTextCheckingResult *result = matches[0];
      NSRange range = [result rangeAtIndex:1];
 
      NSString *identifier = [string substringWithRange:range];
      long long l = [identifier longLongValue];
 
      if (l)
      {
         return @(l);
      }
   }
 
   return nil;
}

This function returns an NSNumber with the identifier we need. I opted to have it as a number object instead of a scalar value because this is how we’ll need it in the next section.

Presenting the Store

For getting the store view controller to show, you need to configure it and present it. Let’s first look at the code before I explain the reason why it looks like that, there are a few hurdles (read “bugs”) to circumnavigate.

NSNumber *iTunesID = PRYiTunesIDFromURL(URL);
 
if (iTunesID)
{
   SKStoreProductViewController *storeProductViewController = [[SKStoreProductViewController alloc] 
                                                               init];
 
   // Configure View Controller
   [storeProductViewController setDelegate:self];
   [storeProductViewController loadProductWithParameters:
                                  @{SKStoreProductParameterITunesItemIdentifier : iTunesID}
    completionBlock:^(BOOL result, NSError *error) {
       // log error
    }
   ];
 
   // present right away to avoid pause
   [self presentViewController:storeProductViewController animated:YES completion:nil];
}

For course you need the #import for the StoreKit.h header. And the current view controller where you put this code needs to be tagged as supporting the SKStoreProductViewControllerDelegate protocol. But since both of these need angle brackets – which are a pain in the backside to write on my blog – I am telling you about those instead of showing them.

In terms of API design, Apple’s intention is for you to first load the product into the view controller asynchronously. Then when it’s loaded you can could show the filled view. There are two problems with this approach:

  1. It might take a couple of seconds for the product to be loaded. So you would have to implement and show a modal activity indicator before the view controller can be presented. This would bring the UI flow to a screeching halt from the point of view of the user.
  2. There is a bug in SKStoreProductViewController that it does not call the completion block if the product for this iTunes ID does not exist. You have no way of knowing that and your UI would be stuck forever. (rdar://20089825)

The only course of action available to us is to present the view controller right after the load method. It does deal with problems gracefully, like having no internet connection. Also if it takes longer than a fraction of a seconds to load the product there is a small activity indicator on the view controller that tells the user that something is still alive there.

When I first experimented with the in-app store, I wanted to present it on a navigation controller. You can do this, but then you are missing out on some extra buttons iOS puts in the navigation bar for you. Besides the cancel button there is also an action button for people to gift this item or send a link to it to somebody. There is also a Store button that takes you to the item’s page on Apple’s store.

In short, just present it as shown and don’t worry about needing a navigation controller wrapper as you would usually require for presenting view controllers modally. SKStoreProductViewController takes care of all it needs to present the top nav bar and its contents.

Closing the Store

The store view controller displays a Cancel button in the top left corner of its nav bar. There is another bug hidden there (rdar://20090284). If you fail to implement the delegate method, then Canceling has the effect of destroying your view hierarchy. The user ends up with a blank view and no recourse.

I suggested to Apple to either make the delegate method mandatory or fix the default cancel behavior. I’d prefer the latter because in my opinion a view controller which shows a Cancel button during modal presentation should also dismiss itself properly.

So, as it stands now in iOS 8.2, you need this code for the delegate:

- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController
{
   [viewController dismissViewControllerAnimated:YES completion:NULL];
}

The same code – including the ID extraction – works for links to albums or music on iTunes, as well as apps on the app store. In the following figure I supplied the ID for an album of a famous Austrian artist, presented from within the same app:

In-App iTunes Store

Isn’t that smart?

Conclusion

Presenting an iTunes or App Store item from within your app is very simple to do. Fortunately the existing bugs can be circumvented easily. Because there is now an ability to specify affiliate information this store view controller should see much wider use now that people can earn some affiliate commissions with it.

]]>
https://www.cocoanetics.com/2015/03/implementing-an-in-app-app-store/feed/ 0 9547
Making App Previews https://www.cocoanetics.com/2015/03/making-app-previews/ https://www.cocoanetics.com/2015/03/making-app-previews/#comments Thu, 05 Mar 2015 18:17:05 +0000 http://www.cocoanetics.com/?p=9522 Last year, at WWDC 2014, Apple presented a cool new way how we can show off our brilliant apps to an unsuspecting audience. After working on the official prod.ly app for the better part of 3 months, today I created and published my first app preview video. Here’s how.

When launching a social networking app like prod.ly, I wanted to give users a good impression about what they would use it for. Like any good ad – so I figured – I wanted to tell a story. Really good advertisements, the ones that you remember, fit a narrative into very short time.

The WWDC video mentioned that you should draw a story board, but I had already the outline in my head.

The Story

Our target audience are young dynamic people who are savvy users of social networking to communicate with their friends. The setting of our short story would be the fictitious launch party for an unknown startup or app. Could be us, could be anybody else.

At or before this launch party the main character (played by me, user “drops”) grabs a small bottle of Champagne, scans the barcode and then comments that it is the perfect drink to toast the launch.

In the second scene he casually browses the prod.ly timeline and discovers an interesting drink. He adds this product to his “wanna try” list.

The third and final segment plays many months later, the startup has gotten millions of funding. It’s the day of the IPO. The user scans an elegant caraffe of premium orange juice and – tonge in cheek – muses that now they are able to even afford a luxurious product like that.

The message I wanted to transport is core to prod.ly: to give users a casual platform to communicate about products they like. And the story should chime with the aspirations that all entrepreneurs have.

The Making Of

Before filming the segments I needed to go shop for some props at my local supermarket. ProductLayer being an Austrian company I opted for several Austrian company’s brands.

iOS 8 added the ability to record live video from iPhone via Lightning cable. But until recently I was unable to record a full segment. Right after the navigation controller slide following a recognized barcode Quicktime would simply stop recording.

Fortunately Apple fixed this bug in iOS 8.3. Right after installing the BETA on my carry phone I tried this out and for the first time the recording kept going. Yay, Apple fixing Radars!

What’s awesome about recording directly from the phone is that you also get to see the video preview screen where I scan the product barcodes. That’s something you couldn’t do on a video recorded off iPhone simulator.

Then I created a new Final Cut Pro X project with the appropriate dimensions for an iPhone 6 screen. Cutting work began.

FCPX with preview project

The main problem creating the ad was getting to be exactly 30 seconds. This is the maximum time that preview videos for iTunes are allowed to run. Also, you want to keep the action fast-paced as not to bore the viewer. One feature of the prod.ly app is that the main actor is adding an opinion about two products. For this he needs to enter some text. I sped this up 10-fold. I considered cutting such that the words would appear, but that felt awkward.

The problem with cuts is that you don’t want the app to appear as if it would do something that it doesn’t. Because of this I kept all the navigation controller and modal presentation slides. I only cut out some pauses. Thus the spirit of the app is preserved and nothing looks fake. If anything you could only criticise that the preview might come across a bit hectic.

For the sound track I found a royalty piece of music, which I shortened by cutting out a few bars as to match up exactly with 30 seconds. I added a few seconds title at the beginning of the ad stating “What do you love?”

Two transitions though black happen between the three segments to communicate the distinction between the parts.

Upload

From Final Cut you can export a master file which contains the original pieces. Make sure you choose h.264 as video format though. You can only add artwork and preview videos with new app versions. The preview video accompanies version 1.0.1 of the prod.ly app, which is waiting for review at the time of this writing.

Apparently you only need to supply the video for one iPhone resolution and Apple will still show the preview to users of all screen sizes. You can, if you so choose, upload videos for different resolutions as well. But as far as I can tell there is no localising it.

To be able to show it off to the world I put it on YouTube as well. I was pleased to find that I didn’t need to re-encode the master file MOV for YouTube either. I simply uploaded the master file, it got processed and was immediately viewable. Only drawback: because the video is in portrait format you get black bars left and right.

So here it is, for your viewing pleasure. I hope you can appreciate the subtleties I hid in it, now that I have explained the reasoning driving its creation.

Oh and how about downloading the shown app as well right now? Then you can judge for yourself if this video gives a good impression of it. It’s available for free on the app store.

Conclusion

If you have an idea for a narrative then the actual work of creating a video clip for the preview is a cinch. Filling 30 seconds is no problem, rather the problem is to not fill more than that. Brutal trimming will inadvertently ensue.

I believe that this experiment proves that you can also create interesting preview apps for something as benign as a social media client. Most apps probably have more interesting visuals than that and show of less different views. So with the limited resources that you might find at your disposal you can still create something enticing.

ok, and now really, please, with sugar on top, download the app. 😉

]]>
https://www.cocoanetics.com/2015/03/making-app-previews/feed/ 2 9522
Tappable UILabel Hyperlinks https://www.cocoanetics.com/2015/03/tappable-uilabel-hyperlinks/ https://www.cocoanetics.com/2015/03/tappable-uilabel-hyperlinks/#comments Wed, 04 Mar 2015 09:22:20 +0000 http://www.cocoanetics.com/?p=9508 In the previous blog post of this series, I have demonstrated how to modify the way UILabel draws hyperlinks. In this article I am showing how to implement function to make the hyperlinks tappable.

We previously implemented a custom stack of NSLayoutManager, NSTextContainer and NSTextStorage. This was necessary because – unfortunately – UILabel does not give us access to its internal instances of these. Having our own layout manager in place is the key ingredient to making the hyperlinks tappable.

When the user brings his finger down on our customised UILabel we need to first determine which character index of the attributed string he landed on. Then we can look up the NSLinkAttributeName for this index. Finally we can change the appearance of this link based on the touch.

As in the previous article, I thank Matthew Styles for working this out for this KILabel implementation. I re-implented my solution from scratch, making many different – I like think more elegant – design choices, so please bear with me and don’t rush off cloning the KILabel project.

Determining String Index of Touch

The layout manager instance we created provides all the necessary methods to determine the character index into the attributed string.

- (NSUInteger)_stringIndexAtLocation:(CGPoint)location
{
   // Do nothing if we have no text
   if (self.textStorage.string.length == 0)
   {
      return NSNotFound;
   }
 
   // Work out the offset of the text in the view
   CGPoint textOffset;
   NSRange glyphRange = [self.layoutManager 
                         glyphRangeForTextContainer:self.textContainer];
   textOffset = [self _textOffsetForGlyphRange:glyphRange];
 
   // Get the touch location and use text offset to convert to text cotainer coords
   location.x -= textOffset.x;
   location.y -= textOffset.y;
 
   NSUInteger glyphIndex = [self.layoutManager glyphIndexForPoint:location 
                                        inTextContainer:self.textContainer];
 
   // If the touch is in white space after the last glyph on the line we don't
   // count it as a hit on the text
   NSRange lineRange;
   CGRect lineRect = [self.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex
                                                             effectiveRange:&amp;lineRange];
 
   if (!CGRectContainsPoint(lineRect, location))
   {
      return NSNotFound;
   }
 
   return [self.layoutManager characterIndexForGlyphAtIndex:glyphIndex];
}

In order to retrieve the NSURL for a given character index, I implemented a category method for NSAttributedString. Ok, it’s a one-liner, but a method of the same signature exists for OS X:

- (NSURL *)URLAtIndex:(NSUInteger)location 
       effectiveRange:(NSRangePointer)effectiveRange
{
   return [self attribute:NSLinkAttributeName atIndex:location 
           effectiveRange:effectiveRange];
}

To show a gray background as long as the link is touched we simply set a background color for the effective range. When the finger is lifted we reset it. The following helper method – the setter for the selectedRange property – takes care of setting and removing said background color attribute.

- (void)setSelectedRange:(NSRange)range
{
   // Remove the current selection if the selection is changing
   if (self.selectedRange.length &amp;&amp; !NSEqualRanges(self.selectedRange, range))
   {
       [self.textStorage removeAttribute:NSBackgroundColorAttributeName
                                   range:self.selectedRange];
   }
 
   // Apply the new selection to the text
   if (range.length)
   {
       [self.textStorage addAttribute:NSBackgroundColorAttributeName
                                value:self.selectedLinkBackgroundColor
                                range:range];
   }
 
   // Save the new range
   _selectedRange = range;
 
   [self setNeedsDisplay];
}

This is all it takes in terms of display because the drawing already takes care of displaying the boxes. Note the additional property selectedLinkBackgroundColor which allows customization.

Handling Gestures

The action to be executed on a user’s tapping on a hyperlink goes into a linkTapHandler property. This block property has one parameter for passing the tapped URL. The _commonSetup method gets called from both initWithFrame: and initWithCoder: to set up the gesture recogniser and default tap handler.

- (void)_commonSetup
{
   // Make sure user interaction is enabled so we can accept touches
   self.userInteractionEnabled = YES;
 
   // Default background colour looks good on a white background
   self.selectedLinkBackgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
 
   // Attach a default detection handler to help with debugging
   self.linkTapHandler = ^(NSURL *URL) {
        NSLog(@"Default handler for %@", URL);
   };
 
   TouchGestureRecognizer *touch = [[TouchGestureRecognizer alloc] initWithTarget:self
                                    action:@selector(_handleTouch:)];
   touch.delegate = self;
   [self addGestureRecognizer:touch];
}

The TouchGestureRecognizer is a very simple recogniser which simply wraps the touchesBegan, -Moved, -Ended and -Cancelled.

@implementation TouchGestureRecognizer
 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   self.state = UIGestureRecognizerStateBegan;
}
 
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
   self.state = UIGestureRecognizerStateFailed;
}
 
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
   self.state = UIGestureRecognizerStateEnded;
}
 
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
   self.state = UIGestureRecognizerStateCancelled;
}
 
@end

The gesture recognizer handling method ties the methods together which we have previously implemented.

- (void)_handleTouch:(TouchGestureRecognizer *)gesture
{
   // Get the info for the touched link if there is one
   CGPoint touchLocation = [gesture locationInView:self];
   NSInteger index = [self _stringIndexAtLocation:touchLocation];
 
   NSRange effectiveRange;
   NSURL *touchedURL = nil;
 
   if (index != NSNotFound)
   {
      touchedURL = [self.attributedText URLAtIndex:index effectiveRange:&amp;effectiveRange];
   }
 
   switch (gesture.state)
   {
      case UIGestureRecognizerStateBegan:
      {
         if (touchedURL)
         {
            self.selectedRange = effectiveRange;
         }
         else
         {
            // no URL, cancel gesture
            gesture.enabled = NO;
            gesture.enabled = YES;
         }
 
         break;
      }
 
      case UIGestureRecognizerStateEnded:
      {
         if (touchedURL &amp;&amp; _linkTapHandler)
         {
            _linkTapHandler(touchedURL);
         }
      }
 
      default:
      {
         self.selectedRange = NSMakeRange(0, 0);
 
         break;
      }
   }
}

We want to consider the gesture as failed if the touch does not start on a hyperlink. This is important when using our UILabel implementation in a table view. We event want to go one step further. Our touch gesture recognizer should always recognize at the same time as other gesture recognizers.

To prevent any adverse blocking effects on scroll gesture recognizers, we want only touches be delivered to the touch gesture recognizer if they are on top of hyperlinks. The code for determining this is quite similar to the gesture handling method.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
   shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
   return YES;
}
 
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
       shouldReceiveTouch:(UITouch *)touch
{
   CGPoint touchLocation = [touch locationInView:self];
 
   NSInteger index = [self _stringIndexAtLocation:touchLocation];
 
   NSRange effectiveRange;
   NSURL *touchedURL = nil;
 
   if (index != NSNotFound)
   {
      touchedURL = [self.attributedText URLAtIndex:index effectiveRange:&amp;effectiveRange];
   }
 
   if (touchedURL)
   {
      return YES;
   }
 
   return NO;
}

With these additional modifications in place our label can also be used in table views without interfering with vertical scrolling. Also horizontal swipes (for editing) are unimpeded as well.

Conclusion

This is all the code that is necessary to get tap handling working for hyperlinks. It’s quite a few lines of code, but they should not be too hard to wrap your head around.

It would all be much easier if Apple allowed us access to the TextKit stack of UILabel, specifically letting us replace the internal layout manager so that we can customize text drawing on glyph granularity. However, the presented technique has the advantage of being backwards compatible. So we’ll stop whining about Apple’s omissions right now.

At the moment there is no public sample project containing the code mentioned, because I feel it is somewhat specific to my needs for the prod.ly app. Let’s make a deal: I’ll trade you the working source code I have for purchasing a copy of my new book. I am also looking for my next iOS and/or Mac project to participate in. Either way, you can contact me at oliver@cocoanetics.com.

]]>
https://www.cocoanetics.com/2015/03/tappable-uilabel-hyperlinks/feed/ 2 9508
Customizing UILabel Hyperlinks https://www.cocoanetics.com/2015/03/customizing-uilabel-hyperlinks/ https://www.cocoanetics.com/2015/03/customizing-uilabel-hyperlinks/#comments Tue, 03 Mar 2015 15:52:56 +0000 http://www.cocoanetics.com/?p=9496 For the ProductLayer prod.ly app I wanted to show a timeline of user opines. Opines are – in spirit – similar to tweets and as such can also contain hyperlinks. In this post I am discussing how to customize hyperlink drawing for UILabels. In a second article on this subject I will then show how to implement tappable hyperlinks for UILabel as well.

UILabel is able to display attributed strings since iOS 6. Ranges which have an NSLinkAttributeName attribute get displayed in a different color and underlined. There are two problems with that: 1) UILabel does not implement user touch handling and 2) you have no way to customize the hyperlink style.

Consider the following style for example from prod.ly. Not only is the hyperlink tappable, I’ve also customized the look by removing the underline. Underlined hyperlinks are so … 80s.

UILabel customization in prod.ly app

This link opens in an in-app browser and while the users finger is down on the link it shows a gray background highlight. Similar to Tweetbot I am also shortening the URL it is too long, replacing the rest of the URL with an ellipsis.

Let’s begin with something easy first.

Customizing Link Drawing

NSLayoutManager, besides managing the layout as its name suggests, also draws the individual parts of the attributed string. The methods with which it does that can be found in the Drawing Support section of NSLayoutManager.h. Our attributed strings will not ever show any underlines, neither for hyperlinks nor for regular text. I created a PRYLayoutManager subclass and overwrote the appropriate method to not do anything:

- (void)drawUnderlineForGlyphRange:(NSRange)glyphRange 
                     underlineType:(NSUnderlineStyle)underlineVal 
                    baselineOffset:(CGFloat)baselineOffset
                  lineFragmentRect:(CGRect)lineRect 
            lineFragmentGlyphRange:(NSRange)lineGlyphRange
                   containerOrigin:(CGPoint)containerOrigin
{
	// ignore underlines
}

NSLayoutManager has a internal color hard-coded for glyph ranges belonging to a hyperlink. It ignores the NSForegroundColorAttributeName, we believe wrongfully so. All drawing functions are called with the CGContext already configured by layout manager. This includes setting the fill color for the glyphs. Since we want the foreground color to be used instead, we change it:

- (void)showCGGlyphs:(const CGGlyph *)glyphs 
           positions:(const CGPoint *)positions 
               count:(NSUInteger)glyphCount
                font:(UIFont *)font 
              matrix:(CGAffineTransform)textMatrix 
          attributes:(NSDictionary *)attributes
           inContext:(CGContextRef)graphicsContext
{
   UIColor *foregroundColor = attributes[NSForegroundColorAttributeName];
 
   if (foregroundColor)
   {
      CGContextSetFillColorWithColor(graphicsContext, foregroundColor.CGColor);
   }
 
   [super showCGGlyphs:glyphs 
             positions:positions 
                 count:glyphCount 
                  font:font 
                matrix:textMatrix 
            attributes:attributes 
             inContext:graphicsContext];
}

If there is a foreground color attribute for the attributes of this glyph run, then it is set as the CGContext’s fill color. This causes the glyphs belonging to the hyperlink to show in the correct color.

Next we need to get UILabel to use our custom layout manager instead of its own internal one.

NSLayoutManager Surgery on UILabel

UILabel uses an internal NSLayoutManager for laying out the text and drawing it. Unfortunately Apple does not provide a property for us to access or customize it. For touch handling, the layout manager gives us the index in the attributed string. The layout manager is also in charge of sizing the label (intrinsic content size), layout out the text contents (line wrapping and truncation) and finally, drawing the text contents. This leaves us no choice but to replace the internal layout manager of UILabel with out own so that we can do all of the above mentioned things.

The layout manager needs two further companions for the layout dance: NSTextStorage and NSTextContainer. The storage is a subclass of NSMutableAttributedString. As such it has all the basic abilities of containing a string where certain ranges have attributes, i.e. a text color. It adds the ability of informing a layout manager if there are changes to the text. The container is nothing more than something that contains text, in our example a simple rectangle. This figure shows how the 3 classes work together.

Layout Classes Connections

We do not know for certain, but it is highly likely that UILabel has these instances of these 3 classes for internal use. The add* methods suggest that you can have multiple layout managers for each storage and multiple text containers for each layout manager. However for grafting our own stack on UILabel we will only need one of each. A UILabel can only have one attributed string and one rectangle, that is the frame of the label itself.

Since we cannot access these we are going to implement 3 lazy properties which instantiate our own versions:

- (NSTextStorage *)textStorage
{
   if (!_textStorage)
   {
      _textStorage = [[NSTextStorage alloc] init];
      [_textStorage addLayoutManager:self.layoutManager];
      [self.layoutManager setTextStorage:_textStorage];
   }
 
   return _textStorage;
}
 
- (NSTextContainer *)textContainer
{
   if (!_textContainer)
   {
      _textContainer = [[NSTextContainer alloc] init];
      _textContainer.lineFragmentPadding = 0;
      _textContainer.maximumNumberOfLines = self.numberOfLines;
      _textContainer.lineBreakMode = self.lineBreakMode;
      _textContainer.widthTracksTextView = YES;
      _textContainer.size = self.frame.size;
 
      [_textContainer setLayoutManager:self.layoutManager];
   }
 
   return _textContainer;
}
 
- (NSLayoutManager *)layoutManager
{
   if (!_layoutManager)
   {
      // Create a layout manager for rendering
      _layoutManager = [[PRYLayoutManager alloc] init];
      _layoutManager.delegate = self;
      [_layoutManager addTextContainer:self.textContainer];
   }
 
   return _layoutManager;
}

One tricky part is that you have to synchronize the properties on the text container with the UILabel properties. I’ve implemented only the most important ones. Every time one of these gets set our custom layout manager needs to be updated as well. In particular, necessary for determine the opine cell size heights, I am making use of the preferredMaxLayoutWidth property.

- (void)layoutSubviews
{
   [super layoutSubviews];
 
   // Update our container size when the view frame changes
   self.textContainer.size = self.bounds.size;
}
 
- (void)setFrame:(CGRect)frame
{
   [super setFrame:frame];
 
   CGSize size = frame.size;
   size.width = MIN(size.width, self.preferredMaxLayoutWidth);
   size.height = 0;
   self.textContainer.size = size;
}
 
- (void)setBounds:(CGRect)bounds
{
   [super setBounds:bounds];
 
   CGSize size = bounds.size;
   size.width = MIN(size.width, self.preferredMaxLayoutWidth);
   size.height = 0;
   self.textContainer.size = size;
}
 
- (void)setPreferredMaxLayoutWidth:(CGFloat)preferredMaxLayoutWidth
{
   [super setPreferredMaxLayoutWidth:preferredMaxLayoutWidth];
 
   CGSize size = self.bounds.size;
   size.width = MIN(size.width, self.preferredMaxLayoutWidth);
   self.textContainer.size = size;
}

A key method for determining the size of a label is the following. I found this implementation in KILabel.m and – sorry to say – don’t fully understand it, in particular the need for a @try/@catch. But it works, so that’s good enough:

- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
{
  // Use our text container to calculate the bounds required. First save our
  // current text container setup
  CGSize savedTextContainerSize = self.textContainer.size;
  NSInteger savedTextContainerNumberOfLines = self.textContainer.maximumNumberOfLines;
 
  // Apply the new potential bounds and number of lines
  self.textContainer.size = bounds.size;
  self.textContainer.maximumNumberOfLines = numberOfLines;
 
  // Measure the text with the new state
  CGRect textBounds;
  @try
  {
      NSRange glyphRange = [self.layoutManager 
                            glyphRangeForTextContainer:self.textContainer];
      textBounds = [self.layoutManager boundingRectForGlyphRange:glyphRange
                                       inTextContainer:self.textContainer];
 
      // Position the bounds and round up the size for good measure
      textBounds.origin = bounds.origin;
      textBounds.size.width = ceilf(textBounds.size.width);
      textBounds.size.height = ceilf(textBounds.size.height);
  }
  @finally
  {
      // Restore the old container state before we exit under any circumstances
      self.textContainer.size = savedTextContainerSize;
      self.textContainer.maximumNumberOfLines = savedTextContainerNumberOfLines;
   }
 
   return textBounds;
}

Autolayout appears to be calling to this method from an internal method having to do with the intrinsic content size.

For sake of simplicity we assume that the number of lines and the line break mode are not going to be modified at runtime. If we wanted a perfect implementation we would have to pass these through to the layout manager as well. We are also knowingly omitting dealing with setText: which is the classic method of setting an NSString on the label. It is possible to construct the appropriate attributes dictionary for turning the plain text string into an attributed one that matches the label properties. But for the sake of this tutorial we only care about this:

- (void)setAttributedText:(NSAttributedString *)attributedText
{
    // Pass the text to the super class first
    [super setAttributedText:attributedText];
 
    [self.textStorage setAttributedString:attributedText];
}

Passing through the attributed string to the super implementation causes the internal layout manager to work the same as our custom one. I think that this is necessary to keep auto layout working. In my implementation I am sizing the opine cells via auto layout.

The last part is to replace the text drawing with our own. When drawing the contents the contents is always vertically centered. To calculate the necessary offset we have the following helper function:

- (CGPoint)_textOffsetForGlyphRange:(NSRange)glyphRange
{
   CGPoint textOffset = CGPointZero;
 
   CGRect textBounds = [self.layoutManager boundingRectForGlyphRange:glyphRange 
                                                     inTextContainer:self.textContainer];
   CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0f;
   if (paddingHeight &gt; 0)
   {
       textOffset.y = paddingHeight;
   }
 
   return textOffset;
}

Finally, we get to the actual drawing:

- (void)drawTextInRect:(CGRect)rect
{
   // Calculate the offset of the text in the view
   CGPoint textOffset;
   NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
   textOffset = [self _textOffsetForGlyphRange:glyphRange];
 
   // Drawing code
   [self.layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textOffset];
 
   // for debugging the following 2 line should produce the same results
   [self.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textOffset];
   //[super drawTextInRect:rect];
}

This first draws all backgrounds for all glyph ranges of the entire attributed string, since we only have one string, one container, one layout manager. Then we draw the actual glyphs. For debugging you can call the super’s implementation of -drawTextInRect: and you should see the two outputs line up (except for the look of the hyperlinks).

Conclusion

Matthew Styles deserves most of the credit for figuring out this technique. I based my implementation in large part on his KILabel albeit with many improvements.

At this point we have bent UILabel to our will insofar as we have customized the appearance of hyperlinks. The custom drawing of glyph decorations, backgrounds and the glyphs itself can be modified – based on the glyph’s attributes – to suit your particular needs.

In the next blog article we will implement a gesture recognizer for interaction with the hyperlinks. We want to be able to highlight the link while it is being touched and also perform an action if the link was tapped. All of this is now possible and easy to do that we have our own layout manager instance.

]]>
https://www.cocoanetics.com/2015/03/customizing-uilabel-hyperlinks/feed/ 9 9496