Nick Teissler - Big Nerd Ranch Tue, 19 Oct 2021 17:47:10 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 It Looks Like You’re Still Trying to Use a Framework https://bignerdranch.com/blog/it-looks-like-youre-still-trying-to-use-a-framework/ https://bignerdranch.com/blog/it-looks-like-youre-still-trying-to-use-a-framework/#respond Mon, 07 Jan 2019 09:00:00 +0000 https://nerdranchighq.wpengine.com/blog/it-looks-like-youre-still-trying-to-use-a-framework/ Build a framework on the command line to gain an understanding of build errors and Swift and Objective-C interoperability.

The post It Looks Like You’re Still Trying to Use a Framework appeared first on Big Nerd Ranch.

]]>

Libby the lifelike framework spews some common Xcode errors related to frameworks: "No such module", "ld: framework not found (use -v to see invocation)", "Module not found", and "file was built for archive which is not the architecture being linked (arm64)"

In It Looks Like You Are Trying to Use a Framework we learned about object files, static and dynamic libraries, frameworks, and their structure. It was a detailed, albeit theoretical view of frameworks.

We’re going to get our hands dirty to understand the inner-workings of frameworks. This will give you guidance on fixing esoteric build errors related to frameworks, and knowledge of how Swift and Objective-C interoperability works at the interface level. We’re going to build a framework without Xcode. Our good friend Libby is not impressed by this undertaking. Why? There is no reason to ever do this in practice. Xcode’s build process for frameworks abstracts most of the work of building a framework into Cmd-B. We do this here so that when you come up against compile time and link time errors, obscured by absolute paths, muttering about dyld, “Module not found”, “Framework not at path”, or my personal least favorite “Here’s your exit code and use -v to see invocation”, you won’t go to StackOverflow (which many times doesn’t help because so many scenarios can yield the same error), but instead tap into your knowledge of frameworks to deduce what is wrong, fix the problem, and get on with coding.

Creating the Empty Framework Bundle

I recommend following along in your favorite shell and Finder to feel that rush when the handmade .framework appears as a functional framework bundle. To prove that the framework works, we’ll use an executable iOS project. Create an empty iOS project in Xcode called “The Shell”. We’re building a rowing app. We will develop a framework called “Coxswain”. The Coxswain is the person who sits in the rear of the boat who steers and coordinates the rowers rowing together. We need a coxswain to steer and make calls in the boat (also called a shell) before our shell can get moving on the water.

The University of Washington and 1936 US Olympic Rowing Team, led by their Coxswain, Bob Moch

In Xcode’s project navigator, create a folder-backed group called Frameworks. On the command line cd into that folder and type mkdir Coxswain.framework. In Xcode, use the File -> Add Files menu to add your framework to the project. Your filesystem should have this structure

The Shell
├── Frameworks
│   └── Coxswain.framework
├── The Shell
│   ├── AppDelegate.swift
│   ├── Assets.xcassets
│   ├── Base.lproj
│   │   ├── LaunchScreen.storyboard
│   │   └── Main.storyboard
│   ├── Info.plist
│   └── ViewController.swift
└── The Shell.xcodeproj

and be mirrored in Xcode.

The Xcode Project Navigator mirroring the file system structure

Adding the Swift Module Definition

When you added the framework to the project, Xcode should have automatically added it to the Linked Frameworks and Libraries under the project settings. If we import Coxswain in ViewController.swift and try to build, we get the error

❗️ No such module 'Coxswain'

Which makes sense. Our linked empty framework doesn’t include a single line of code, much less a module. Let’s make one. Create a directory called Scratch Space, but don’t add it to Xcode. Use your favorite text editor to create a swift file.

CoxCalls.swift

import Foundation

@objc public class Coxswain: NSObject {

    public func steer(left: Bool) {
        print("Steering (left ? "left": "right")")
    }

    @objc public func stroke(count: Int) {
        print("Gimme (count) strong strokes!")
    }

    func talk(to seat: Int) {
        print("Adjust your technique seat (seat)")
    }
}

The name of this file doesn’t affect the module name, which is specified later.

We create a variety of basic functions and signatures to see how each is exposed in Swift and Objective-C interfaces. In Scratch Space, generate the interface files.

$ swiftc -module-name Coxswain -c CoxCalls.swift -target x86_64-apple-ios12.1-simulator 
  -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/
  Developer/SDKs/iPhoneSimulator12.1.sdk 
  -emit-module -emit-objc-header -emit-objc-header-path Coxswain-Swift.h

You might not have the SDK version and simulator listed above. You should safely be able to substitute the version of iOS with which you are working into those path arguments.

This command creates the Swift module and the Objective-C header for our single source file. Let’s break down the arguments:

  • -module-name Coxswain: The name of the module, what comes after import (or @import). Typically this will match the name of the framework.
  • -c CoxCalls.swift The source files to compile
  • -target x86_64-apple-ios12.1-simulator -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk These are the target architecture and the SDK to link against. This will compile specifically for the iOS 12 simulator and won’t run on any earlier simulator. This will use the iOS 12 SDK, including the Foundation framework we linked to.
  • -emit-module will emit a .swiftdoc and .swiftmodule for the code we just wrote.
  • -emit-objc-header will emit an Objective-C header, including only those symbols marked @objc.
  • -emit-objc-header-path Notice that we choose to follow Xcode’s naming convention here for exposing Swift symbols to Objective-C by appending our framework name with -Swift.h.
  • If you look in Xcode’s build logs, you’ll see many more build flags. See our post Build Log Groveling for Fun and Profit for an analysis of these flags and additional build steps.

We know from Part 1 of this post that each generated file has a proper place in a
framework. We need to rename our .swiftmodule and .swiftdoc files created
by swiftc to the architecture they represent and keep them in a bundle called Coxswain.swiftmodule.
There is little documentation on this structure. I reverse engineered it by looking
at frameworks created by Xcode. Change the prefix of both of the Swift module files
from Coxswain to x86_64 keeping each extension, then put them both in a folder
called Coxswain.swiftmodule. Copy this to a new Modules/ directory at the
root of the framework directory. Then copy the Objective-C header into a Headers/
directory in the framework. The new framework structure should be:

Coxswain.framework
├── Headers
│   └── Coxswain-Swift.h
└── Modules
    └── Coxswain.swiftmodule
        ├── x86_64.swiftdoc
        └── x86_64.swiftmodule

Xcode now knows about our symbols. Add let cox = Coxswain() to ViewController.viewDidLoad() in the project; notice that Xcode can autocomplete that class name. Try to build again in Xcode. No more module error! Instead we have a new one.

❗️ld: framework not found Coxswain
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Creating the Library

This error looks more intimidating. ld and clang have chimed in. When you see errors like this, take a breath and remember, you know what is going on! The module definition is there, and the compiler knows what symbols it should be finding (the Coxswain class and its public methods). The problem is, at link time, they aren’t there. Thinking back again to a framework’s structure, what is missing? The library! Let’s make that. In Scratch Space, compile the code (run this command with the -v flag to see interesting verbose output and additional flags that will be familiar from Xcode build logs).

$ swiftc -module-name Coxswain -parse-as-library -c CoxCalls.swift -target 
  x86_64-apple-ios12.1-simulator -sdk /Applications/Xcode.app/Contents/
  Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk -emit-object

We kept most of the arguments from the previous command, only now we pass -emit-object to output an object file, and -parse-as-library so the compiler interprets our file as a library, not an executable. An ls reveals the new object file, CoxCalls.o. Drawing on knowledge from Part 1, we will archive this into the library format that Xcode expects. We’ll use libtool which is intended to replace the classic Unix archive tool, ar.

$ libtool -static CoxCalls.o -arch_only x86_64 -o Coxswain

Normally, the extension for this file would be .a, but the default name Xcode expects in frameworks is the name of the module only, no extension. Without creating an Info.plist for our framework, we’re constrained to these defaults.

I’ve chosen to create a static framework, rather than the more common dynamically linked framework because it simplifies this step. For dynamic libraries, the location of the libraries used by the object files must be specified when the library is created; these include the Swift core libraries and the wrappers around the Objective-C runtime and Foundation. With a static library, Xcode will handle this linking when it builds the app.

Using the Framework

Copy the Coxswain library into the framework bundle. The structure of our operational, artisanal, organic, free-range, grass-fed framework is:

Coxswain.framework/
├── Coxswain
├── Headers
│   └── Coxswain-Swift.h
└── Modules
    └── Coxswain.swiftmodule
        ├── x86_64.swiftdoc
        └── x86_64.swiftmodule

In Xcode, build the application. The compilation errors should be gone and you will be left with an unused variable warning in ViewController.swift where we added the cox variable earlier. Add a method call to one of the framework’s methods to convince yourself that we were successful.

(1..<10).reversed().forEach { cox.stroke(count: $0) }

Now run the app and look in the console for the coxswain’s calls.

Gimme 9 strong strokes!
Gimme 8 strong strokes!
Gimme 7 strong strokes!
...

Congratulations! Look back at the files and folders you’ve created with some pride, this is not easy.

Swift and Objective-C Interoperability

Throughout this process, we took care to make sure the framework would work with Swift and Objective-C code. Making frameworks work with both can be a delicate process. You must adopt modules if you want a hybrid project. When bringing old Objective-C code to a Swift project, this means setting the “Defines Module” (DEFINES_MODULE) build setting to YES in the framework target. This instructs Xcode to install a module.modulemap file (and possibly a module.private.modulemap) alongside the headers in the framework. Objective-C frameworks need to define a module to be used by Swift. Next, we put our framework to the test.

Add an Objective-C class to your app (allowing Xcode to create the bridging header for you). If you use the older #import <Coxswain/Coxswain-Swift.h> syntax for including the framework, the project will build and compile and Objective-C will have access to the functions you declared public and @objc. The newer module import syntax @import Coxswain however, will yield:

❗️Module 'Coxswain' not found

The .modulemap file is missing.

For our Objective-C code to import Coxswain as a module, we need to define a module map, which “describes how a collection of existing headers maps on to the logical structure of a module” (Clang Documentation: Modules). For the benefits of Clang modules and how they fit into Xcode, there is a useful WWDC 2013 session

According to the clang modules documentation, the module map language isn’t guaranteed to be stable yet, so it’s best to let Xcode generate these as well. Our framework structure is simple, so creating a module map isn’t bad (still, I did look to a pre-existing Xcode project’s build products for the structure and syntax of the file):

module.modulemap

framework module Coxswain { // Define a module with framework semantics
    umbrella header "Coxswain.h" // Use the imported headers in the umbrella header to define the module
    export * // Re-export all symbols from submodules into the main module
    module * { export * } // Create a submodule for every header in the umbrella header
}

module Coxswain.Swift { // Define a submodule called Coxswain.Swift
    header "Coxswain-Swift.h" // Use the imported headers to define the module
    requires objc
}

Move this into the Modules directory within the framework, at the same level as Coxswain.swiftmodule.

Adding the Umbrella Header

An umbrella header is also required by the typical module map structure. It is possible to omit this header, but it complicates the module map. Here, we declare constants that clients of frameworks expect to be defined in the framework.

Coxswain.h

#import <UIKit/UIKit.h>

//! Project version number for Coxswain.
FOUNDATION_EXPORT double CoxswainVersionNumber;

//! Project version string for Coxswain.
FOUNDATION_EXPORT const unsigned char CoxswainVersionString[];

Move this into the Headers directory within the framework, at the same level as Coxswain-Swift.h.

We don’t strictly have to import the header Coxswain-Swift.h which declares the symbols
for the Coxswain framework. This is because we defined the Coxswain module
to include the Coxswain.Swift submodule which includes Coxswain-Swift.h in the modulemap. If
not for modules, we would have to import Coxswain-Swift.h.
The final structure of the framework on the filesystem is:

Coxswain.framework/
├── Coxswain
├── Headers
│   ├── Coxswain-Swift.h
│   └── Coxswain.h
└── Modules
    ├── Coxswain.swiftmodule
    │   ├── x86_64.swiftdoc
    │   └── x86_64.swiftmodule
    └── module.modulemap

Objective-C classes in the project should now be able to access Coxswain.framework using modules. If you are tracking this demo project with version control, you might notice that Xcode helpfully set the build setting “Enable Modules (C and Objective-C)” (CLANG_ENABLE_MODULES) to YES when you introduced Objective-C into the Swift project.

Wrapping Up

You’re done! You have successfully created a framework. You have little reason to tremble at load, link, and compile time errors when working with frameworks and hybrid applications. The source code for the sample project can be found on GitHub with tags corresponding to each section above.

For the More Curious

If you have read any of our Big Nerd Ranch Guides you have seen the For the More Curious sections. This is where you’re encouraged to dig deeper after completing a chapter. This post is getting a bit long, but I couldn’t resist including these questions that will solidify the concepts covered in this post and the previous one.

  1. If you navigate to the built products directory (In the Xcode Project Navigator, right-click on your app and select Show in Finder) and look for signs of the Coxswain framework, you won’t find it. Why?

  2. We declared the symbols CoxswainVersionNumber and CoxswainVersionString in
    the framework’s umbrella header, but never defined them. Luckily, no code ever referenced
    these symbols, so our app didn’t crash. Create a new framework project in Xcode.
    Xcode creates a similar umbrella header. Find the definition (where the actual value
    assignment is done) of the version number and string. A clue to where the
    missing symbols are can be found in the build logs. Look for a file ending in _vers.c.
    The DERIVED_FILE_DIR Build Settings Reference and our Understanding the Swift/Objective-C Build Pipeline posts may help.

  3. If you feel comfortable with this process. Dive even deeper. We built our framework for the x86_64 architecture. This allowed us to build and run on the iOS Simulator in Xcode. If we switch the deployment target in Xcode to a real device and try to deploy our app, you’ll receive the error:

❗️file was built for archive which is not the architecture being linked (arm64): {absolute/path}/Coxswain.framework/Coxswain ld: warning: ignoring file {absolute/path}/Coxswain.framework/Coxswain

or perhaps

❗️'Coxswain' is unavailable: cannot find Swift declaration for this class

As we built the entire Framework, we know that we don’t have an object file or Swift module for a real device’s arm64 architecture, thus the compiler can’t find the Swift declaration for it. Compile CoxCalls.swift for the arm64 architecture. Use macOS’s libtool and lipo to build a fat binary that includes both the simulator and device architectures. If you make something you’re proud of, share your solution in the comments!

The post It Looks Like You’re Still Trying to Use a Framework appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/it-looks-like-youre-still-trying-to-use-a-framework/feed/ 0
Pro Pattern-Matching in Swift https://bignerdranch.com/blog/pro-pattern-matching-in-swift/ https://bignerdranch.com/blog/pro-pattern-matching-in-swift/#respond Mon, 20 Aug 2018 10:00:00 +0000 https://nerdranchighq.wpengine.com/blog/pro-pattern-matching-in-swift/ The switch statement is an indubitable strength of the Swift language. Behind the switch statement is Swift's pattern-matching, which makes for more readable, safer code. It is possible to take the switch's pattern-matching readability and power and apply it elsewhere in your code.

The post Pro Pattern-Matching in Swift appeared first on Big Nerd Ranch.

]]>

The switch statement is an indubitable strength of the Swift language. Behind
the switch statement is Swift’s pattern-matching, which makes for more readable,
safer code. It is possible to take the switch’s pattern-matching readability
and power and apply it elsewhere in your code.

The Swift language reference
specifies eight different varieties of patterns. It can be hard to know the
right syntax to use in a pattern match expression. In a typical use-case you
might need to know type information, to unwrap a variable, or to simply confirm
an optional is non-nil. Using the right pattern, you can avoid awkward unwraps
and unused variables.

There are two participants in a pattern match: the pattern and the value. The
value is the expression that follows the switch keyword, or the = operator
if the value is being tested outside a switch statement. The pattern is the
expression that follows a case label. The pattern and value are evaluated
against each other using rules of the Swift language. As of July
15th, 2018 the reference
contains some errors about how and where patterns can be used in the prose, but
these can be discovered with some experimentation.[1]

We’ll look at applying patterns in if, guard, and while statements, but
before we do, let us warm up with some non-vanilla uses of the switch statement.

Matching only non-nil Variables

If the value trying to be matched is possibly nil, we can use an Optional
Pattern
to only match the value if it is non-nil and, as a bonus, unwrap it.
This is especially useful when dealing with legacy (and some not-so-legacy)
Objective-C methods and functions. As of Swift 4.2 with the
reimplementation of IUOs ! will be a
synonym for ?. For Objective-C functions without nullable
annotations
, you may have to
handle this behavior.

The example here is especially trivial as this new behavior can be unintuitive
coming from Swift < 4.2. Take this Objective-C function:

- (NSString *)aLegacyObjcFunction {
    return @"I haven't been updated with modern obj-c annotations!";
}

The Swift signature will be: func aLegacyObjcFunction() -> String!, and in
Swift 4.1, this function will compile:

func switchExample() -> String {
    switch aLegacyObjcFunction() {
    case "okay":
       return "fine"
    case let output:
        return output  // implicitly unwrap the optional, producing a String
    }
}

In Swift 4.2, you’ll get an error: “Value of optional type ‘String?’ not
unwrapped; did you mean to use ‘!’ or ‘?’?”. case let output is a simple
variable-assignment pattern match. It will match the String? type returned
by aLegacyObjcFunction without unwrapping the value. The unintuitive part is that
return aLegacyObjcFunction() will compile because it skips the variable
assignment (pattern match), and with it, the type inference so the returned type is
a String! and treated as such by the compiler. We should handle this more gracefully, especially if the Objective-C
function in question actually can return nil.

func switchExample2() -> String {
    switch aLegacyObjcFunction() {
    case "okay":
        return "fine"
    case let output?:
        return output 
    case nil:
        return "Phew, that could have been a segfault."
    }
}

This time, we are handling the optionality intentionally. Notice that we don’t
have to use if let to unwrap the return value of aLegacyObcFunction. The nil
pattern match case let output?: handles that for us; output is a String.

Catching Custom Error Types with Precision

Pattern-matching can be extremely useful and expressive when catching custom
error types. A common design pattern is to define custom error types with
enums. This works especially well in Swift, as it’s easy to attach associated
values to enum cases to provide more detail about an error.

Here we use two flavors of the Type-Casting Pattern as well as two flavors of
the Enumeration Case Pattern to handle any errors that may be thrown:

enum Error: Swift.Error {
    case badError(code: Int)
    case closeShave(explanation: String)
    case fatal
    case unknown
}

enum OtherError: Swift.Error { case base }

func makeURLRequest() throws { ... }

func getUserDetails() {
    do {
        try makeURLRequest()
    }
    // Enumeration Case Pattern: where clause
    catch Error.badError(let code) where code == 50 {
         print("(code)") }
    // Enumeration Case Pattern: associated value
     catch Error.closeShave(let explanation) {
         print("There's an explanation! (explanation)")
     }
     // Type Matching Pattern: variable binding
     catch let error as OtherError {
         print("This (error) is a base error")
     }
     // Type Matching Pattern: only type check
     catch is Error {
         print("We don't want to know much more, it must be fatal or unknown")
     }
     // is Swift.Error. The compiler gives us the variable error for free here
     catch {
         print(error)
     }
}

Above in each catch, we have matched and caught only as much information as we
needed, and nothing extra. Now to move on from switch and see where else we
can use pattern-matching.

The One-Off Match

There are many times when you might want to do a one-off pattern match. Possibly
a change should only be applied given a single enumeration value and you don’t
care about the others. At that point, the beautifully readable switch statement
suddenly becomes cumbersome boilerplate.

We can use if case to unpack a tuple value only if it is non-nil:

if case (_, let value?) = stringAndInt {
    print("The int value of the string is (value)")
}

The above uses three patterns in one statement! At the top is a Tuple Pattern
containing an Optional Pattern (not unlike the one above in Matching a
non-nil Variable
), and a sneaky Wildcard Pattern, the _. Had we
used switch stringAndInt { ... }, the compiler would have forced us to handle
all possible cases explicitly, or with a default label.

Alternatively, if a guard case suits your purposes better, there’s not much to
change:

guard case (_, let value?) = stringAndInt else {
    print("We have no value, exiting early.")
    exit(0)
}

You can use patterns to define stopping conditions of while loops and for-in
loops. This can be useful with ranges. An Expression Pattern allows us to
avoid the traditional variable >= 0 && variable <= 10 construct[2]:

var guess: Int = 0

while case 0...10 = guess  {
    print("Guess a number")
    guess = Int(readLine()!)!
}
print("You guessed a number out of the range!")

In all of these examples, the pattern immediately follows the case label and
the value comes after the =. The only time syntax departs from this is when
there is an is, as, or in keyword in the expression. In those situations,
the structure is the same if you think of those keywords as a substitute
for the =. Remembering this, and with nudges from the compiler, you can
use all 8 varieties of patterns without having to consult the language
reference.

There is something unique about the Range matching Expression Pattern above
that we haven’t seen so far in previous examples: its pattern-matching
implementation is not a built-in feature, at least not built-in to the compiler.
The Expression Pattern uses the Swift Standard Library ~=
operator
.
The ~= operator is a free, generic function defined as:

func ~= <T>(a: T, b: T) -> Bool where T : Equatable

You can see the Swift Standard Library Range type overrides this
operator

to provide a custom behavior that checks if the particular value is within the
given range.

Matching Regular Expressions

Let’s create a Regex type that implements the ~= operator. It will be a
paper-thin wrapper around
NSRegularExpression
that uses pattern-matching to make for more readable regex code, something we
should always be interested in when working with the arcane regular expression.

struct Regex: ExpressibleByStringLiteral, Equatable {

    fileprivate let expression: NSRegularExpression

    init(stringLiteral: String) {
        do {
            self.expression = try NSRegularExpression(pattern: stringLiteral, options: [])
        } catch {
            print("Failed to parse (stringLiteral) as a regular expression")
            self.expression = try! NSRegularExpression(pattern: ".*", options: [])
        }
    }

    fileprivate func match(_ input: String) -> Bool {
        let result = expression.rangeOfFirstMatch(in: input, options: [],
                                range NSRange(input.startIndex..., in: input))
        return !NSEqualRanges(result, NSMakeRange(NSNotFound, 0))
    }
}

There’s our Regex struct. It has a single NSRegularExpression property. It
can be initialized as a string literal, with the consequence that if we fail to
pass a valid regular expression, we’ll get a failure message and a match-all
regex instead. Next, we implement the pattern-matching operator, nesting it in
an extension so it’s clear where we want the operator to be used.

extension Regex {
    static func ~=(pattern: Regex, value: String) -> Bool {
        return pattern.match(value)
    }
}

We want this struct to be useful out of the box, so I’ll define two class
constants that can handle some common regex validation needs. The email regex
was borrowed from Matt Gallagher’s Cocoa with
Love

article and checks email addresses as defined in RFC
2822
.

If you’re working with regular expressions in Swift, you can’t simply copy-pasta
from your choice of Stack Overflow Regex posts. Swift strings define escape
sequences like the newline (n), tab (t), and unicode scalars
(u{1F4A9}). These clash with the syntax of regular expressions which is heavy
with backslashes and all types of brackets. Other languages like python have
convenient raw string syntax. A raw string will take every character literally
and does not parse escape sequences, so regular expressions can be inserted in
their “pure” form. In Swift, any lone backslashes in a string indicate an escape
sequence, so for the compiler to accept most regular expressions, you will need
to escape the escape sequences, as well as a few other special characters. There
was an
attempt
to bring raw strings to Swift that fizzled out. It’s possible that as Swift
continues to become a multi-platform, multi-purpose language, interest will be
renewed in this feature. Until then, the already complex email-matching regular
expression, becomes this ascii art monster:

static let email: Regex = """
^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[x01-x08
x0bx0cx0e-x1fx21x23-x5bx5d-x7f]|[x01-x09x0bx0cx0e-x7f])*")@
(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|[(?:(?:25[0-5]|2[0
-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?
:[x01-x08x0bx0cx0e-x1fx21-x5ax53-x7f]|[x01-x09x0bx0cx0e-x7
f])+)])$
"""

We can use a simpler expression to match phone numbers, borrowed from Stack
Overflow

and double escaped as previously described:

static let phone: Regex = "^(+d{1,2}s)?(?d{3})?[s.-]?d{3}[s.-]?d{4}$"

Now we can identify phone numbers or emails with our handy, readable pattern
syntax:

let input = Bool.random() ? "nerd@bignerdranch.com" : "(770) 817-6373"
switch input {
    case Regex.email:
        print("Send (input) and email!")
    case Regex.phone:
        print("Give Big Nerd Ranch a call at (input)")
    default:
        print("An unknown format.")
}

You might be wondering why you don’t see the ~= operator above. It’s an
implementation detail of the Expression Pattern and is used implicitly.

Remember the Basics!

With all these fancy patterns, we shouldn’t forget
ways we can use the classic switch. When the pattern-matching ~= operator is
not defined, Swift falls back to using the == operator in switch statements.
To reiterate, we are no longer in the domain of pattern-matching.

Below is an example. The switch statement here is being used as a
demultiplexer for delegate
callbacks. It switches over the textField variable, a subclass of NSObject.
Equality is therefore defined as identity comparison, which will check if the
pointer values of the two variables are equal. As an example, take an object
which serves as the delegate for three UITextFields. Each text field needs to
validate its text in a different way. The delegate will receive the same
callback for each text field when a user edits text

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
    switch textField {
        case emailTextField:
            return validateEmail()
        case phoneTextField:
            return validatePhone()
        case passwordTextField:
            return validatePassword()
        default:
            preconditionFailure("Unaccounted for Text Field")
    }
}

And can validate each text field differently.

Concluding

We looked at a few of the patterns available in Swift and
examined the structure of pattern-matching syntax. With this knowledge, all of
the 8 varieties of patterns are available to use! Patterns come with many
benefits and they are an indispensable part of any Swift developer’s toolbox.
There is still content this post didn’t cover, like the niceties of compiler
checked exhaustive
logic

and patterns combined with where clauses.

Thank you to Erica Sadun for introducing me to the guard case syntax in her
blog post, Afternoon
Whoa
,
which inspired this one.

All of the examples for this post can be found in this
gist. The
code can be run as a playground or picked through to suit your needs.

[1] The guide claims for enums with associated values, “the corresponding
enumeration case pattern must specify a tuple pattern that contains one element
for each associated value.” Just including the enum case without any associated
values compiles and matches assuming you don’t need the associated value.

Another small correction is that the custom expression operator (~=) may “appear
only in switch statement case labels”. Above, we use it in an if statement as
well. The language
grammar

specifies both of the above usages correctly, and the error only exists in the
prose.

[2] The readLine function won’t work in a playground. If you want to run this
example, try it from a macOS command-line application.

The post Pro Pattern-Matching in Swift appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/pro-pattern-matching-in-swift/feed/ 0
It Looks Like You Are Trying to Use a Framework https://bignerdranch.com/blog/it-looks-like-you-are-trying-to-use-a-framework/ https://bignerdranch.com/blog/it-looks-like-you-are-trying-to-use-a-framework/#respond Thu, 18 Jan 2018 10:00:53 +0000 https://nerdranchighq.wpengine.com/blog/it-looks-like-you-are-trying-to-use-a-framework/ Take a deep dive into the internals of iOS frameworks and learn how to best use (and not use) frameworks for cleaner, faster code and debugging.

The post It Looks Like You Are Trying to Use a Framework appeared first on Big Nerd Ranch.

]]>

Libby the lifelike framework offers help (The framework icon cartoonified similar to Microsoft Word's Clippy asks: what is a framework, will it affect performance, and I just want to resolve these build errors!)

Libby the lifelike framework asks good questions. Apple has made it simple to split apart iOS and macOS code into separate modules, libraries, and frameworks. Frameworks are designed for more than packaging resources and modularizing code, and definitely are not designed to protect against recompiling certain files of your project when you build. In order to simplify your code base, speed up debugging, and package your code for maximum reusability, frameworks need to be understood on a deeper level than a toolbox-icon that is dragged and dropped into Xcode. To understand Frameworks, we’ll need to cover:

  • Static Libraries
  • Dynamic Libraries
  • Frameworks and Their Structure
  • Linking vs Embedding Frameworks in Xcode
  • Questions to Ask Before Using a Framework
  • Some Handy Command Line Tools for the More Curious

Note: Most of the content below is applicable to both macOS, tvOS and iOS programming. Any time there are significant differences they will be noted.

Static Libraries

To look at static libraries, we will start with object files (If you aren’t familiar with these already, we have a post that explains what an object file is). At their most basic, object files are a structured collection of blocks of bytes on disk. Some of these blocks contain program code, others contain data structures that are used down the pipeline by the linker and loader. For a bird’s eye view of an object file, try running this in the terminal

$ objdump -macho -section-headers /bin/ls

Object files come in four forms: relocatable, executable, shared, and bundle. Relocatable object files contain code and data that can be statically linked at compile time with other relocatable object files in order to create an executable. Executable object files are ready to be loaded into memory and executed. Shared object files are a special version of relocatable object files that will be covered in the next section. We won’t cover bundles, but on macOS, this filetype is typically used for plugins.

If you compile a C source code file with no main function, then the result is a relocatable object file, for example:

fancy.c

void fancySwap(int *xp, int *yp) {
    if (xp == yp) return; // try it!
    *xp = *xp ^ *yp;  
    *yp = *xp ^ *yp;  
    *xp = *xp ^ *yp;  
}
cc -c fancy.c

The resulting fancy.o will contain the symbol fancySwap and its implementation.

To work with relocatable object files in a more convenient format, multiple object files can be archived into .a (a is for archive. See man ar) files. The .a file is known as a static library or static archive. When functions or data from a .a file are used as part of an executable, the linker is smart enough to only link the symbols used. However, the linker includes (copies and relocates) all of this code in the executable object file at a static address. The result of this is a larger executable that is slower to load into memory. To make matters worse, if ten programs use this archive, there are effectively ten copies of the code on your system. Additionally, if the author of the .a file used updates the source, the executable must be recompiled and redistributed. The dynamic library was invented as an alternative to the rigidity of static libraries.

As of Xcode 9.0, static Swift libraries are now supported (Xcode Release Notes – 33297067). If you pick the Static Library project template make sure the PRODUCT_MODULE_NAME is set so users of your library have something to import. As of Xcode 9.2, this is handled in iOS, but not in macOS templates.

Dynamic Libraries

A dynamic library (also referred to as: shared library, shared object, and dynamically linked library) is an archive of object files that can be loaded at an arbitrary memory address and linked with a program in memory at load-time or runtime. This is called dynamic linking and is done by the dynamic linker (dyld on macOS). Shared libraries have the .dylib extension on macOS, the elusive .DLL extension in Windows, and .so on Linux. Whereas static libraries are statically linked by a static linker at compile time, dynamic libraries are dynamically linked by a dynamic linker at load or runtime. Linking at runtime is a cumbersome process; man dlopen will give a quick overview of dynamic loading. An application where runtime link/loading is practical and performant is high performance web servers. Dynamic libraries can be unloaded and upgraded without any downtime to the running application. Dynamic libraries can also be loaded on a per-request basis. If a client of the web-server requests a particular resource, the server can load in the required library to create the resource, then unload it once it has responded to the request to reduce its memory footprint. A less contrived example of dynamic loading is an application supporting plugins which are dynamically loaded.

Shared object files solve the problems mentioned above with static object files. Multiple executables can dynamically link to a single copy of a shared library on disk. Shared libraries can be updated without having to make changes to the executable. Shared libraries even have their dependencies loaded and linked automatically, assuming the dependencies are present in the search path. macOS makes extensive use of shared libraries; check out the contents of the /usr/lib folder. If you want your own shared library to be used, you have to ensure it is somehow embedded in your app, or create an installer package that drops your dylib in /usr/local/lib. Why not /usr/lib? The system reserves the right to drop whatever it needs in /usr/lib, (that directory is owned by root) so you are better off using local.

After reading the pervious paragraph, you might think, “Great, I may not have to recompile, but now I have to keep in mind backwards compatibility for all users of my dylib.” You are correct, you do have to think about API stability as you make updates to a shared library. Or maybe this is why you came here. To learn about the framework, a bundling format that addresses compatibility and more.

Need to know how to keep your app development project on track and in budget? Download your eBook.

Frameworks

Most frameworks are shared libraries with hassle-free packaging. This is easy to confirm. A framework is a folder with a particular structure that encapsulates shared resources. These can be images, xibs, dynamic libraries, static libraries, documentation files, localization files, and more. They are packaged as a bundle on the file system, but unlike most bundles (like a .app file), frameworks don’t have anything to hide and reveal that they are in fact a folder.

Note: The structured directory “bundle” referred to here is not the same as the bundle object file type discussed at the beginning of this post.

A framework expanded in finder(├── Libby.app └── MyCustomFramework.framework ├── Headers -> Versions/Current/Headers ├── Modules -> Versions/Current/Modules ├── MyCustomFramework -> Versions/Current/MyCustomFramework ├── Resources -> Versions/Current/Resources └── Versions ├── A │ ├── Headers │ │ ├── MyCustomFramework-Swift.h │ │ └── MyCustomFramework.h │ ├── Modules │ │ ├── MyCustomFramework.swiftmodule │ │ │ ├── x86_64.swiftdoc │ │ │ └── x86_64.swiftmodule │ │ └── module.modulemap │ ├── MyCustomFramework │ └── Resources │ └── Info.plist └── Current -> A )

Below is a walkthrough of the subdirectories and symbolic links that make up a framework bundle.

Headers

This folder contains the C and Obj-C headers that the framework exposes to the public. Swift does not use these headers. If a framework is written in Swift, Xcode will create this folder by default for interoperability. If you are sometimes mystified by when to add @objc in Swift code, try this: look in Build Settings to discover the value for SWIFT_OBJC_INTERFACE_HEADER_NAME, add and remove @objc annotations and other interop directives (@objc(your:objc:name), @objcMembers) from your source code and see how this affects the objective-c header named by the SWIFT_OBJC_INTERFACE_HEADER_NAME.

Modules

This folder contains LLVM and Swift module information. .modulemap files are used by clang, and are discussed at length here. The files contained in the .swiftmodule directory are similar to header files. However, unlike header files, the files in here are in a binary format that is “undocumented and subject to change”. This is what Xcode uses when you Cmd-click a swift function whose module, but not source code, is availabe.

Although these are binary files, they are structured as llvm bitcode, and as such, we can gather information from them with llvm-bcanalyzer and llvm-strings.

MyCustomFramework

This is a symlink to the dylib which Finder labels a Unix executable, but it is actually a relocatable shared object file.

Resources

Localized resources, xibs, documentation files, images, and other assets can go in here.

The symlink destinations are shown in the picture. Historically, they existed to handle versioning. If a new major version was introduced, it would be added to the folder under directory Versions/B. Current will also be updated to point at B. Meanwhile, applications dynamically linked to A will still link to version A rather than Current. How? When an executable is built, the dynamic linker records the path of the version of the framework known to be compatible with the executable. Today even Apple rarely, if ever, uses this feature.

Linking vs Embedding Frameworks in Xcode

Here we discuss a commonly configured part of Xcode, your target’s general settings.

Xcode's Target General Settings View

Almost always select “Linked Frameworks and Libraries” if you need to use a framework unless you plan on linking and loading at runtime (dlopen strikes again). What then is the difference between these two options? Why does Xcode automatically add a framework to the Link section if you choose to Embed it? Think about what you’ve just read about where dylibs are stored and what has to be done if you want to package your own dylib.

Correct! Embedding actually adds a copy of that framework into your application bundle under the Frameworks directory by default. Your application won’t be able to do anything with that Framework unless it links to it. Xcode takes the liberty of doing this for you. System frameworks are already present on either iOS or macOS so you can safely link to these at the absolute path that they are kept on the system. Your own custom frameworks won’t be present on a user’s system, and therefore they have to be embedded in the application bundle.

Linking and embedding indirectly imply dynamic and/or static linking. We now know that embedding wouldn’t make any sense for a static library becuase the symbols from the static library are compiled into the executable, so Xcode won’t let you drop a static library under the Embed section. It will however, let you embed a framework with static libraries. This is an inefficient use of frameworks.

Just because you can, doesn’t mean you should: Technically on macOS you can load a static .a file at runtime and then mark the area of memory it occupies as executable, effectively loading a static library at runtime. This technique is what JIT compilers do, but marking memory segments as executable is not allowed on iOS.

It Looks like You are Trying to Use a Framework

What are factors to consider before using a framework or moving your code into a framework?

  • Are you developing a suite of apps? If so, this is part of the essence of frameworks and Libby says you should proceed with enthusiasm keeping in mind that to truly share the frameworks, you will have to build an installer package that places the shared frameworks in /Library/Frameworks or ~/Library/Frameworks.
  • Do you have a little bit of common code that you share amongst apps? A framework might not be right here. If you’re just trying to share small amounts of code the overhead of packaging and versioning a framework might outweigh the tasks of keeping the source in sync.
  • Are you trying to modularize your code? If you are moving code into a framework to modularize it, you might be better off using Swift’s access control. Frameworks shouldn’t be used as a namespacing mechanism. That said, creating a private framework internally to separate out reusable code from app specific code is not unheard of.
  • Don’t use frameworks as “recompile shields!” Some projects will split a large part of the project’s core code that is close to finished out into a framework to speed up compile times. This “works” because as we know, frameworks don’t have to be recompiled with the executable, but this complicates code modularity, debugging, and autocompletion among other things.
  • Are you packaing your code as third-party for use by others as-is? If this is the case, you might be choosing between dylib, static library, and Framework. It is currently hip to dynamic frameworks for both mac and iOS because of their portability and pervasiveness and ease-of-use. Also consider sharing the source code too to let others make the decision that works best for them!

A parting thought to pair with these usage tips: frameworks are incredibly common to iOS and macOS programming, but their advantages and potential performance benefits are rarely exercised while their portability and ease of creation are abused. I’ve described framework utopia above. For a less idealized take on their usage, read this article by Wil Shipley from 2005 that has aged particularly well.

A Command Line Tool-box

Here are some command line tools that, with the exception of llvm-bcanalyzer come with macOS out of the box.

  • file: Running this with a filename as the first argument is a great first move. It simply outputs the type of file with details of architecture, object file format and more.
  • nm: List symbols defined in an object file. Running with the -u flag will show the undefined symbols, meaning symbols that a file expects to have defined by another shared object file.
  • lipo: You won’t run into this too often in the wild, but it is a neat one. It can perform various operations related to fat object archives. For instance if you have a universal (intended for multiple ISAs) binary, you can use lipo to extract only the architecture you care about so your executable isn’t bloated with unneeded archives.
  • objdump: Considered by many to be the great-grand-daddy of inspecting object files
  • ar: This is the tool used to bundle multiple relocatable object files into a more portable archive file. It outputs .a library files.
  • llvm-bcanalyzer: This can dump llvm bitcode files into a semi-human-readable format. Though powerful, objdump won’t let you analyze llvm bitcode files, so it’s good to know about this one.

Questions?

Check out the sources below if you have more questions than answers after reading this. Also leave comments and I’ll do my best to address them.

Sources

The post It Looks Like You Are Trying to Use a Framework appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/it-looks-like-you-are-trying-to-use-a-framework/feed/ 0
Custom Collection View Layouts with tvOS: Part 2 https://bignerdranch.com/blog/custom-collection-view-layouts-with-tvos-part-2/ https://bignerdranch.com/blog/custom-collection-view-layouts-with-tvos-part-2/#respond Mon, 21 Aug 2017 08:00:53 +0000 https://nerdranchighq.wpengine.com/blog/custom-collection-view-layouts-with-tvos-part-2/ Develop more intuitive and creative tvOS apps by adding animation and custom focus behaviors. This tutorial walks you through controlling focus between a split view controller and animating the custom collection view layout from part 1.

The post Custom Collection View Layouts with tvOS: Part 2 appeared first on Big Nerd Ranch.

]]>

The tvOS focus engine and collection views can bring a robust and interactive control to the television screen, but to design any layout that is non-linear, you will need to create a custom layout. Part 1 of this series walked through creating a custom collection view layout that arranged a variable number of cells in a wheel.

In this part, we’ll add rotation animations so that focus remains in the same place and the content cycles around underneath it. We’ll also walk though how to guide focus around your app with focus engine properties and callbacks. To follow along, the source code for this tutorial can be found on GitHub here.

Adding Scroll-Rotation

Moving focus around the wheel worked well, but another common way to naviagte on tvOS is to move view underneath a stationary area of focus. When a user ‘scrolls’ around in our collection view, we want the content to move and focus remain stationary as shown below.

stationaryFocus

UICollectionView has a method scrollToItem(at:at:animated:) that is responsible for scrolling the collection view contents to the specified item. We’ll override this method to rotate the collection view to bring the specified cell into focus. Subclass UICollectionView and add the method header and a property to track the current orientation of the collection view.

class WheelCollectionView: UICollectionView {

    var orientationRadians: CGFloat = 0.0 {
        didSet {
            transform = CGAffineTransform(rotationAngle: orientationRadians)
        }
    }
    
    override func scrollToItem(at indexPath: IndexPath, at scrollPosition: UICollectionViewScrollPosition, animated: Bool) {
        
    }
}

The didSet on orientationRadians rotates the entire view. We’ll wrap any calls to set this property in an animation closure provided by the coordinator object passed as an argument in focus engine callbacks. When scrollToItem(as:at:animated:) is called, the view should to position the focused item at 3 o’clock (0 radians on the unit circle).

To define the scroll behavior, we need a mathematical function of the indexPath.row variable that will output an angle with range 0-2π, with an offset to have the focused view’s center at 3 o’clock, rather than it’s top left corner.

index f(index)
0 11π/6
1 9π/6
2 7π/6
3 5π/6
4 3π/6
5 1π/6

The function that gives this is: π * ( 2 – ( 2 * index + 1) / n ). We can implement scrollToItem(at:at:animated:) now.

override func scrollToItem(at indexPath: IndexPath, at scrollPosition: UICollectionViewScrollPosition, animated: Bool) {
    let n = CGFloat(numberOfItems(inSection: indexPath.section))
    let index = CGFloat(indexPath.row)
    let radians = .pi * (2.0 - (2.0 * index + 1) / n)
    orientationRadians = radians
    
    for cell in self.visibleCells as! [CollectionViewWheelCell] {
        cell.cellImage?.transform = CGAffineTransform(rotationAngle: -CGFloat(radians))
    }
}

You’ll notice a counter-rotation of the cells in the opposite direction. To convince yourself this is needed, comment this out later. scrollToItem(at:at:animated:) isn’t called for us. We need to call it everytime the user updates focus. The focus engine provides didUpdateFocus(in:with:) for all UIFocusEnvironment adopting classes. We’ll use this to trigger a scroll guarding on the condition that the next focuesed view is a cell in the collection view.

override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
        guard let cell = context.nextFocusedView as? CollectionViewWheelCell,
            let index = indexPath(for: cell) else {
            return
        }
       
        coordinator.addCoordinatedAnimations({
            // Adjust the animation speed following Apple's guidelines:
            // https://developer.apple.com/reference/uikit/uifocusanimationcoordinator
            let duration : TimeInterval = UIView.inheritedAnimationDuration;
            UIView.animate(withDuration: (4.0*duration), delay: 0.0, options: .overrideInheritedDuration, animations: {
                self.scrollToItem(at: index, at: .right, animated: true)
            }, completion: nil)
        }, completion: nil)
    }

Don’t forget to change the type of collectionView in ViewController.swift and Main.storyboard to your UICollectionView subclass. Build and run to see your collection view at work.

Send in the Split View Controller

It’s now time to add more elements to the screen to make this control practical for use in an application. In interface builder, drag a split view controller on the screen. Delete the table view controller and replace it with a segue to your original view controller with the collection view. At this point, make sure the “Scrolling Enabled” box is unchecked in the collection view attributes inspector, as it can lead to unexpected behaviors if the collection view is too close to the edge of the screen.

Reconstrain your collection view if necessary to be contained in the left section of the screen. Delete the segue between the split view controller and the empty view controller. Reassign the initial view controller and build and run. You should see the same behavior as before, but now we have some space to grow on the screen and remain organized.

We’ll use a table view with colored cells and no data to occupy the right side of
the split view controller. Create a view controller with a table view to your liking. Be sure to add a cellColor property for visualization purposes later.

class ColorViewController: UIViewController, UITableViewDataSource, UITableViewDelegate  {

    @IBOutlet var tableView: UITableView!
    var cellColor: UIColor = UIColor.clear
    
    override func viewDidLoad() {
        tableView.dataSource = self
        tableView.delegate = self
        tableView.rowHeight = 190
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell")!
        cell.contentView.backgroundColor = cellColor
        return cell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
}

Configure the right side of the split view controller in interface builder with a
constrained table view and hook up the outlet.

Interface builder configuration after adding the split view controller

When a cell from the collection view is selected with a press on the remote, the
split view controller will load that cell’s content. We’ll handle these callbacks
in ViewController. Add conformance to UICollectionViewDelegate and the required delegate methods and set the collection view’s delegate to self in viewDidLoad().

extension ViewController: UICollectionViewDelegate {

    override func viewDidLoad() {
        ...
        collectionView.delegate = self
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        pushSplitViewController(for: indexPath)
    }
    
    private func pushSplitViewController(for indexPath: IndexPath) {
        guard let colorViewController = storyboard?.instantiateViewController(withIdentifier: "ColorViewController") as? ColorViewController, let splitViewController = splitViewController  else {
            return
        }
        let color: UIColor
        switch (indexPath.row) {
        case 0:
            color = UIColor(colorLiteralRed: 33/255, green: 173/255, blue: 251/255, alpha: 0.6)
        case 1:
            color = UIColor.purple.withAlphaComponent(0.6)
        case 2:
            color = UIColor(colorLiteralRed: 118/255, green: 199/255, blue: 44/255, alpha: 0.6)
        case 3:
            color = UIColor.white.withAlphaComponent(0.6)
        case 4:
            color = UIColor.red.withAlphaComponent(0.6)
        case 5:
            color = UIColor.orange.withAlphaComponent(0.6)
        default:
            color = UIColor.black
        }
        colorViewController.cellColor = color
        splitViewController.showDetailViewController(colorViewController, sender: self)
    }
}

Set the Storyboard ID for your color view controller in interface builder to
match the name you use above. Your tvOS application is behaving pretty well now: navigation is intuitive and the focus engine lets you transition easily back and forth between your view controllers. However, we want some custom focus behavior. When a user selects a cell, they will expect the focus to change to the content they want to browse. Time to make this happen.

Manipulating the Focus

Apple doesn’t let you simply set focus to an element on the screen. There is no myView.grabFocusForcefully() method. Instead they provide the UIFocusEnvironment protocol to let you control how focus moves around. The idea behind this is that if you’re setting focus programatically, you are likely confusing the user because they expect focus to translate from element
to element on the screen, not jump suddenly to an unrelated far-off element.

There are cases, like ours, where we do want to move the focus for the user, and for that we will use UIFocusEnvironment as well as methods defined specifically on collection views to manipulate the focus to do our bidding.

When the user selects a view, focus needs to transition from our collection view, to the table view controller. If we make our collection view unfocusable, focus will have no choice but to jump to the only other focusable element on screen: the table view. There must always be one focused view on screen (it is possible to have no focused views on the screen during video playback, but in cases like these, gesture recognizers and other mechanisms are set up to allow the user to interact with the application). In the delegate extension,
define when it is acceptable for the collection view to have focus and a property to track the state.

var scrollingAround = true
...

extension ViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, canFocusItemAt indexPath: IndexPath) -> Bool {
        return scrollingAround
    }
}

The state changes when the user selects a cell. Add that and a call to the focus
engine to update the focus in collectionView(_:didSelectItemAt:).

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        pushSplitViewController(for: indexPath)
        scrollingAround = false
        setNeedsFocusUpdate()
    }

Build and run. Excellent… we’ve accomplished what we want, with one problem: there is no way to get back to the collection view. However, if we track state more attentively, we can solve this.

The conditions for tableView(_:canFocusRowAt:) and collectionView(_:canFocusItemAt:) are more complex than we first made them. An item or cell should be focusable at all times, except when transitioning between the two views. The didUpdateFocusIn
delegate methods for collection views and table views help to track this state. The code will look similar for collection and table views.

In ColorViewController add hasFocus and browsing propertes and these method changes to track and update state.

var hasFocus = false
var browsing = false

func tableView(_ tableView: UITableView, canFocusRowAt indexPath: IndexPath) -> Bool {
    return browsing || !hasFocus
}

func tableView(_ tableView: UITableView, didUpdateFocusIn context: UITableViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
    browsing = context.nextFocusedIndexPath != nil
    hasFocus = context.nextFocusedIndexPath != nil
}

In ViewController do almost the same, the analog to browsing is scrollingAround which we already have.

var hasFocus = false

func collectionView(_ collectionView: UICollectionView, canFocusItemAt indexPath: IndexPath) -> Bool {
    return scrollingAround || !hasFocus
}

func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
    hasFocus = context.nextFocusedIndexPath != nil
    scrollingAround = context.nextFocusedIndexPath != nil
}

There is another intuitive way to exit the color view controller back to the wheel: “menuing out”. This is a press of the menu button. Recognizing a menu button press is done with UITapGestureRecognizer. Add a tap gesture recognizer with an action to move focus away from the color view controller.

override func viewDidLoad() {
    ...
    let menuRecognizer = UITapGestureRecognizer(target: self, action: #selector(menuOut(sender:)))
    menuRecognizer.allowedPressTypes = [NSNumber(integerLiteral:UIPressType.menu.rawValue)]
    self.view.addGestureRecognizer(menuRecognizer)

}

func menuOut(sender: UITapGestureRecognizer) {
    browsing = false
    setNeedsFocusUpdate()
}

If you experiment with the behavior, you might see something you wouldn’t expect. Menuing out switches back to the collection view, but seems to randomly choose which cell to focus on. Collection views and table views have a property designed just for this purpose: remembersLastFocusedIndexPath. Set that to true in viewDidLoad() of ViewController to complete this tutorial.

override func viewDidLoad() {
    ...
    collectionView.remembersLastFocusedIndexPath = true
}

Wrap It Up

If you’ve followed along with both parts, you have an idea of how to create unique navigation controls in a tvOS app using collection views and custom layouts. Along with this comes the requirement of handling focus. You saw by tracking application state, focus can be directed toward the desired views on screen.

Head over to the project’s GitHub to see a few more customization points. The project has been updated to make user selections more obvious and control focus further to prevent confusion between the collection view selection and table view content.

And if you’re having trouble implementing these and other features into your tvOS app, Big Nerd Ranch is happy to help. Get in touch to see how our team can build a tvOS app for you, or impement new features into an existing app.

The post Custom Collection View Layouts with tvOS: Part 2 appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/custom-collection-view-layouts-with-tvos-part-2/feed/ 0
Custom Collection View Layouts with tvOS: Part 1 https://bignerdranch.com/blog/custom-collection-view-layouts-with-tvos-part-1/ https://bignerdranch.com/blog/custom-collection-view-layouts-with-tvos-part-1/#respond Sun, 02 Jul 2017 10:00:53 +0000 https://nerdranchighq.wpengine.com/blog/custom-collection-view-layouts-with-tvos-part-1/ The tvOS focus engine and collection views can bring a robust and interactive control to the television screen, but to design any layout that is non-linear, you will need to create a custom layout. In this post, we'll create a custom control for a tvOS app to show how to create a custom layout in the shape of a circle.

The post Custom Collection View Layouts with tvOS: Part 1 appeared first on Big Nerd Ranch.

]]>

The tvOS focus engine and collection views can bring a robust and interactive control to the television screen, but to design any layout that is non-linear, you will need to create a custom layout. In this post, we’ll create a custom control for a tvOS app, using the Big Nerd Ranch areas of development as example content, to show how to create a custom layout in the shape of a circle. The end goal will look like this:

tvOS collection view demo

When we finish the project, we want to see a wheel of content that the user can swipe through. When they make a selection, the focus should transition to a table of content related to their selection. To follow along, the source code for this tutorial can be found on GitHub here.

UICollectionView

UIKit uses collection views to present ordered data in your app. If you’ve used the Photos or Music apps or an iPhone, chances are you have interacted with a Collection View. At any given time, a collection view has a single layout object which tells it where to place items and how they should look. You can use custom collection view layouts and their collaborating objects to present data on the screen in a creative way that distinguishes your app from a vanilla grid of cells.

UICollectionView is a powerful class with many opt-in behaviors that can make your app more engaging and visually attractive to a user. As a tvOS developer, this is generally one of your goals. Once you have laid the groundwork for a custom collection view layout, you have a flexible interface to handle callbacks, add decoration or supplementary views, adjust selection and highlight styles, and customize reordering behavior, among the myriad options. Collection views are important enough to get their own focus engine callbacks and properties in tvOS. collectionView(:canFocusItemAt:) and collectionView(:didUpdateFocusIn:with:)are methods you will be glad to have when we implement navigation in tvOS in the second part of this blog series.

Implementing a custom collection view can be a hefty undertaking and you should make sure that Apple’s provided UICollectionViewFlowLayout and UICollectionViewDelegateFlowLayout classes are not capable of what you’re trying to do. The most common situations that call for a custom layout are when your desired layout looks nothing like a grid or line-based layout, or if you want to change the positions of cells frequently enough that it becomes more work to constantly update the information in the flow layout than to make your own. With that, let’s take a look at the ecosystem of UICollectionView and
implement our own.

The Collection View Team

Collection views are a coordination of many classes, each assists in displaying data
or handling user interaction. The classes shown here aren’t a complete set, but do
the majority of a collection view’s work. They are also the classes we will be subclassing
or using heavily throughout this walkthrough. We won’t be using the classes associated
with Flow Layout, but they are shown to give you an idea where they fit in. This
diagram may be helpful to reference as we write our custom layout.

Step 1: The Custom Layout

Create a blank single-view tvOS application in Xcode. Don’t worry about the boiler plate for now. The personality of our collection view is going to come from its custom layout, so create a new file called CollectionViewWheelLayout.swift and add the following code:

import UIKit

class CollectionViewWheelLayout: UICollectionViewLayout {

    var itemAttributesCache = [UICollectionViewLayoutAttributes]()

    override var collectionViewContentSize: CGSize {
        guard let collectionView = collectionView else {
            preconditionFailure("collection view layout doesn't have a collection view!")
        }
        return collectionView.frame.size
    }
    
    override func prepare() {
        // create a layout attribute for each cell of our collection view
    } 

    override func invalidateLayout() {
        super.invalidateLayout()
        itemAttributesCache = []
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let itemAttributes = itemAttributesCache.filter {
            $0.frame.intersects(rect)
        }
        return itemAttributes
    }
    
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return itemAttributesCache.first {
            $0.indexPath == indexPath
        }
    }

}

The role of a UICollectionViewLayout subclass is to provide the collection view with the size and position of its elements in the form of UICollectionViewLayoutAttributes objects. These are the methods that must be implemented to subclass UICollectionViewLayout. We generate the positions for our collection cells in prepare(). The superclass implementation of prepare() does nothing. In a subclass, at a minimum prepare() should compute enough information to return the overall size of the collection view’s content area. At a maximum, it should create and store all layout attribute objects your view will use. We’ll cache this data in the itemAttributesCache. If at any time we should invlidate this layout, all we have to do is clear the cache.

The collectionViewContentSize property is a ‘gimme’ in this situation. It represents the size of the entire collection view displayed. Contrast this with the visble rectangle of a scroll view. In our case, our view doesn’t scroll vertically or horizontally and is visible in its entirety at all times.Our collectionViewContentSize is the frame of the collection view.

The layoutAttributesForElements(in:) and layoutAttributesForItem(at:) methods perform lookups in this cache based on what the caller is asking for and return the requested layout attributes.

We use a lazy variable to ensure that the calculation of coordinates only gets done once. We will use other means to rotate and animate the view later, rather than juggling rotation matrices in our collection view layout. configureLayout contains trigonometry for arranging squares’ corners around a circle. Add a spacing property and a definition for configureLayout.

Implement prepare() below and add an itemSpacingDegrees property to allow for customization of the sizes of the collection view cells.

var itemSpacingDegrees: CGFloat = 3.0
...

override func prepare() {
    guard self.itemAttributesCache.isEmpty, let collectionView = self.collectionView else { return }
    for section in 0..<collectionView.numberOfSections {
        let itemCount = collectionView.numberOfItems(inSection: section)
        let cgItemCount = CGFloat(itemCount)
        
        for i in 0..<itemCount {
            let bounds = collectionView.bounds
            let O = CGPoint(x: bounds.midX, y: bounds.midY)
            let R = bounds.width / 2.0
            let radiansSoFar = CGFloat((i * 360/itemCount)).degreesToRadians
            let endAngle = radiansSoFar + (360.0/cgItemCount - self.itemSpacingDegrees).degreesToRadians
            let θ = (endAngle - radiansSoFar)
            let r = (R * sin(θ/2.0)) / (sin(θ/2.0) + 1)
            let OC = R - r
            let x = cos(radiansSoFar + θ / 2.0) * OC - r + O.x
            let y = sin(radiansSoFar + θ / 2.0) * OC - r + O.y
            let frameOrigin = CGPoint(x: x, y: y)
            let cellFrame = CGRect(origin: frameOrigin, size: CGSize(width: 2*r, height: 2*r))
            
            let indexPath = IndexPath(item: i, section: section)
            let layoutAttributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            layoutAttributes.frame = cellFrame
            self.itemAttributesCache.append(layoutAttributes)
        }
    }
}

guarding on an empty item attributes cache is an extra safety measure. prepare() is only called when the layout is explicitly invalidated or if there are changes to the view. Because our view stays constant on the screen and we have a constant content area, prepare() should only get called the first time the view is loaded.

Feel free to omit the unicode theta, or press Cmd-Ctrl-Space to bring up the drawer and select your favorite style of theta from the 25 available choices. You will also need two extensions to keep degrees to radians conversions out of sight:

extension Int {
    var degreesToRadians: Double { return Double(self) * .pi / 180 }
}
extension CGFloat {
    var degreesToRadians: CGFloat { return self * .pi / 180 }
}

This math is lengthy, but not overly complex. Each iteration of the loop calculates the position of the upper-left corner of the frame for each collection view cell around a center point. If you want to create non-linear collection view layouts, you’ll have to revisit sohcahtoa. To match each variable to a calculated dimension, see the figure below.

geometry

Step 2: The Data Source

Extend the ViewController class to conform to UICollectionViewDataSource and give it the two necessary methods.

extension ViewController: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return images.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WheelCell", for: indexPath) as! CollectionViewWheelCell
        let image = images[indexPath.row]
        let imageView = UIImageView(image: image)
        cell.cellImage = imageView
        return cell
    }
}

Download the .lsr images from this project’s GitHub or create your own. Using Apple’s layered image format gets you parallax and depth effects on your images in tvOS. Drag the images folder into the Assets.xcassets folder in Xcode. Now add the images array as a property on ViewController with image literals. Then type in the first few letters of the name of each image, and select the autocomplete
suggesion. Xcode support for layered image literals isn’t visually ideal, but makes
instantiating images easy.
imageliterals

Create the cell class CollectionViewWheelCell. This cell will hold only the image.

class CollectionViewWheelCell: UICollectionViewCell {
    var cellImage: UIImageView? {
        didSet {
            configureImage()
        }
    }
    
    override var reuseIdentifier: String? {
        return "WheelCell"
    }
    
    private func configureImage() {
        guard let cellImage = cellImage else {
            return
        }
	cellImage.frame = contentView.bounds
        contentView.addSubview(cellImage)
        
        cellImage.translatesAutoresizingMaskIntoConstraints = false
        cellImage.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        cellImage.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        cellImage.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
        cellImage.heightAnchor.constraint(equalTo: heightAnchor).isActive = true

        cellImage.adjustsImageWhenAncestorFocused = true
    }
}

Step 3: Assembling the Collection View

In Main.storyboard, add a collection view with a width and height of 550 and constrain it towards to left of the screen. 95 points from the top layout guide and 20 points from the left will be ideal for part 2 of this tutorial when we introduce a split view controller. In the Attributes Inspector, configure the collection view to have 0 items and a Custom layout. We manually registered our cells earlier. Select your CollectionViewWheelLayout from the dropdown. Finally, set the background color to Default or transparent.

The final steps are hooking up a collection view outlet to your view controller and configuring it in ViewControler.swift’s viewDidLoad().

@IBOutlet var collectionView: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.register(CollectionViewWheelCell.self, forCellWithReuseIdentifier: "WheelCell")
    collectionView.dataSource = self
    collectionView.clipsToBounds = false
}

Build and run the application. Press Cmd-Shift-R to get the simulated remote on the sceeen if it isn’t there already. Try holding down option and panning around the remote. Check out all of that free behavior the focus engine gives you because you used a collection view and positioned your views well! If you are curious to see how fliexible our collection view is, try adjusting the return value of collectionView(:numeberOfItemsInSection:) and add a modulus operator to the array subscript in collectionView(:cellForItemAt:). (let image = images[indexPath.row % images.count]) if you increase the item count.

What’s Next

You now have a collection view using a custom layout displayed on the screen, it looks good, and you can add selection callbacks right now. In the next post in the series, we’ll learn how to create the behavior in the animation at the top of this page.

Learn more about why you should update your apps for iOS 11 before launch day, or download our ebook for a deeper look into how the changes affect your business.

The post Custom Collection View Layouts with tvOS: Part 1 appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/custom-collection-view-layouts-with-tvos-part-1/feed/ 0