Thứ Sáu, 31 tháng 10, 2014

You Can Jailbreak iPhone 6 & iPhone 6 Plus on iOS 8.1 with Pangu… For Windows

Pangu Jailbreak for iOS 8.1 in Windows

The Pangu group has released a utility that will jailbreak iOS 8.1 on any iPhone or iPad device which can run the latest iOS release, including the new iPhone 6 and iPhone 6 Plus. The jailbreak is untethered and includes Cydia, but there’s a catch… at the moment the Pangu 1.1 tool runs only on Windows, though a Mac version is said to be in the works and will debut soon.

It’s important to note that while jailbreaking iOS 8.1 (or any other version of iOS 8.0) is fairly easy with the free Pangu tool, users should exercise caution and understand there are many reasons why they should not jailbreak, including a variety of risks and limitations that may occur when using unofficial third party tools to modify their devices system software. Apple also does not condone the practice, and jailbreaking an iPhone or iPad may void the devices warranty. Users considering a jailbreak should have an advantaged knowledge of iOS with a complete understanding of the risks associated with the practice, and should always perform a backup before proceeding with the process. Adequate backups allow for a user to unjailbreak the device if desired or necessary.

Compatible devices include iPhone 6, iPhone 6 Plus, iPad Air, iPad Air 2, iPad Mini Retina, iPad Mini Retina 2, iPad 3, iPad 4, or iPod touch 5th gen, running either iOS 8.0 through iOS 8.1. As already mentioned this specific version is currently for Windows only, so unless you have a Windows PC, or Windows running in Boot Camp or a virtual machine on your Mac, those with OS X only would have to sit on the sidelines until a full Mac version is released. The only other requirement is a USB cable to temporarily connect the device to the computer to complete the jailbreak.

Pangu Jailbreak iOS 8.1 compatible devices list

Those interested in the jailbreak can download and find the Pangu tool on the Pangu.io website here. The windows based utility looks easy to use and includes on-screen instructions.

iOS has gained many features natively that were long known to be accessibly exclusively to jailbreakers, ranging from things like Wi-Fi Personal Hotspot, interactive Notifications, lock screen camera access, and much more. There continues to be functionality and features that are limited to jailbroken devices, particularly relating to changes to the iOS appearance, which leads many iPhone and iPad owners to still want to jailbreak their devices.

We do not recommend jailbreaking, mostly because it can cause a device to behave erratically and crash more often, thereby potentially degrading the user experience of iOS. It’s important to know that Apple is also very opposed to jailbreaks for a variety of reasons and will not support devices that are jailbroken. Nonetheless, many advanced iPhone and iPad users wish to modify their iOS software using these tools, and those users may find the Pangu tool to be suitable for them.

Source : osxdaily[dot]com

Thứ Năm, 30 tháng 10, 2014

How to Use Handoff Between a Mac with OS X Yosemite and iOS 8.1

Handoff iOS to OS X

Handoff is a really great feature of iOS 8.1 and OS X Yosemite that allows a Mac user to pass off or resume an app session to their iPhone or iPad, or vice versa. For example, you can start typing an email on your iPhone, then pass it off to your Mac and finish writing that email or send it off. Or if you’re reading an article in Safari on your Mac and need to rush out the door, you can quickly hand it off to your iPad and read the rest on your horse as you commute to the office. There’s a lot of potential with Handoff as part of the broader Continuity experience, and it really aims to bring greater productivity to those who have an array of Apple devices.


To use Handoff, you must have the latest version of OS X and iOS on your devices, all Macs, iPhones, and iPads must be logged into the same iCloud account, the devices must be on the same Wi-Fi network, and they all must have Bluetooth 4.0 hardware that supports the feature. For some Macs that don’t officially support Handoff, this unofficial modification can make it work anyway, though some devices would require a new Bluetooth module which makes it somewhat impractical.

Enabling HandOff in iOS & OS X

To get started, you’ll want to enable the feature in both OS X and iOS, and then turn on Bluetooth and join the same Wi-Fi networks:

  1. From the Mac, go the  Apple menu > System Preferences > General and be sure that “Allow Handoff between this Mac and your iCloud devices” is enabled
  2. Enable Handoff in Mac OS X

  3. From iOS, go to the Settings app > General > Handoff & Suggested Apps > and be sure that Handoff is set to ON
  4. Enable Handoff in iOS

  5. Turn Bluetooth ON for all hardware:
    • Enable Bluetooth in iOS by swiping up to Control Center and toggling it ON
    • Enable Bluetooth in OS X by  Apple menu > System Preferences > Bluetooth and choosing “Turn Bluetooth On” (you’ll probably want to enable ‘Show Bluetooth in menu bar’ while here for ease of troubleshooting
    • Make sure both the Mac and iOS device have wi-fi enabled and are connected to the same network

That’s the basics to get Handoff enabled on a Mac and iOS (remember, the minimum requirements are the Mac running with OS X Yosemite and the iPhone / iPad running with iOS 8.1 or newer), so once you have that setup let’s trigger the feature and pass a session between two devices.

Using Handoff with iOS & OS X

Using Handoff requires that the applications support the feature, and many apps that share iOS and OS X versions do, including Safari, Mail, Pages, the iWork suite, etc. More apps will support Handoff as time goes on too.

For the example here, we’ll use Safari that is being passed from a Mac to an iPhone, but the basic idea is the same for any other supported app or device too.

  1. Have Safari on the Mac loaded with a webpage and active as the forefront application
  2. Pick up the iPhone or iPad and look at the lock screen – you should see a little Safari icon in the bottom left corner indicating Handoff is ready to pass the Macs Safari session off to the iOS device
  3. Swipe up on that icon from the corner of the lock screen to open the webpage from the Mac onto the iOS device

The Handoff icon is directly across from the Camera icon, it’s small and somewhat subtle:

Handoff icon on the lock screen of iOS

If the iPhone or iPad is already unlocked, you can find Handoff in the multitasking screen where you’d typically quit apps that you don’t want running, just swipe all the way to the left to find the Handoff option.

Handoff shown on the multitasking screen of iOS

Finally, passing a session from iOS to a Mac shows up in one of two ways: in the Command+Tab multitasking app switcher, or in the far left of the OS X Dock:

Handoff shown in the OS X Dock

This may be obvious, but the Handoff icon shown will represent the app that is active that wishes to pass off the session to the other device, meaning you’ll see the Safari icon for a Safari session, Mail icon for an email session, etc.

Triggering Handoff can be a bit tricky sometimes, we’ll touch on that in a moment.

Handoff Not Working? Handoff Unreliable? Some Troubleshooting Tips

While Handoff works flawlessly for some users, it’s very unreliable for others, and for another group of Mac and iOS users they can’t get it working at all no matter what they do. If you’re in the group having difficulties, here are a variety of troubleshooting tricks that may resolve the problems experienced with Handoff not working:

  • Make sure devices are on the same Wi-Fi network
  • Quit and relaunch the app(s) trying to use Handoff
  • Disable and re-enable Handoff (the feature is often enabled by default but doesn’t immediately work, toggling off and ON again often resolves that)
  • Disable and re-enable Bluetooth
  • Log out and back into iCloud accounts on devices
  • Reboot the Mac
  • Reboot the iPhone or iPad

Having tested Handoff considerably, I have mixed results. On one Mac it works almost all the time going in either direction (passing a session from iOS to OS X or vice versa, about 90% success rate), while on another Mac I can get it to work about 50% of the time when going from a Mac to iOS device, but only about 30% of the time going from iOS to OS X. For the latter scenario that’s obviously a pretty high failure rate, but we can almost certainly expect this great feature to become considerably better and more reliable as both iOS and OS X receive further updates and bug fixes.

Oh and for what it’s worth, there are many users who have reported that Handoff didn’t work initially, but the feature magically starts working spontaneously and seemingly out of the blue without having changed any settings. That even applies to some of the Macs that are using the unofficial Handoff enabler tool for otherwise unsupported 2011 model MacBook Air and Mac Mini computers.

What has been your experience with Handoff? Do you love it? Does it work for you? Let us know your experiences in the comments!

Source : osxdaily[dot]com

Creating a Custom Keyboard Using iOS 8 App Extension

0 Flares 0 Flares ×

Prior to iOS 8, developers could provide custom keyboards or supplement the system keyboard with custom keys within only their application. With iOS 8, Apple has made it possible to create custom keyboards that will be able to be used system wide in other apps. You can now ship a custom keyboard with your app and users will be able to choose it as the keyboard to use for every app that requires text input.

To create a successful keyboard that users are more likely to keep on using, you have to meet some of the expectations that they have for what a keyboard should do. Since they will have been using the system keyboard for a long time, it is a good place to look at when deducing what your keyboard design and functionality should be.

custom-keyboard-featured

The system keyboard is fast, responsive and capable. It doesn’t interrupt the user with requests or information. Users have come to expect the following features from their keyboard. These are not requirements for creating custom keyboards, but just examples of what you could include in your keyboard to increase the chances of it being popular in a competitive market.

  • Auto suggestion and auto correction
  • Inserting period upon double space
  • Caps lock support
  • Keycap artwork
  • Multistage input for ideographic languages
  • Appropriate layout and features based on keyboard type trait for example presenting appropriate keys for easy email input when the user switches to an email field.

Custom Keyboard Limitations

There are some system keyboard features that are unavailable to custom keyboards. These include:

  • Custom keyboards don’t have access to most of the general keyboard settings in the Settings app such as Auto-capitalization, Enable Caps Lock or dictionary reset. However you can provide your own settings bundle that can be displayed in Settings.app. You can check the Implementing an iOS Settings Bundle guide for more on this.
  • A Custom Keyboard cannot be used to type into certain text input objects. These include the secure text input objects (any object that has its secureText property set to YES) and phone pad objects (any object that has a keyboard type trait of UIKeyboardTypePhonePad or UIKeyboardTypeNamePhonePad). When the user types in any of these text input objects, the system temporarily replaces your custom keyboard with the system keyboard, and on typing in a non-secure or non-phone pad object, your keyboard resumes.
  • Input dictation isn’t possible for a custom keyboard since, like all extensions in iOS 8, it has no access to the device microphone.
  • Selecting text is also not possible. Text selection is under the control of the app that is using the keyboard.
  • Closely related to the above point, editing menu options i.e. Cut, Copy, Paste are inaccessible. If an app provides an editing menu interface, the keyboard has no access to it.
  • You cannot display key artwork above the top edge of a custom keyboard’s primary view the same way Apple does when you tap and hold a key in the top view.
  • App developers can reject the use of custom keyboards in their app. This can especially be done in apps that are sensitive to security such as banking apps.

Essential Features of Custom Keyboards

The Apple Extensions guide specifies two development essentials for every custom keyboard:

  • Trust. A custom keyboard gives you access to what a user types. You should therefore establish and maintain their trust by being visible about privacy policies. Users should be assured that their keystroke data will only be used for the document they are typing and not for other purposes not obvious to them. If the keyboard employs use of other user data such as Location Service or Address Book database, then this should be conveyed to the user.
  • Next Keyboard key.This is a mandatory feature that allows users to switch to another keyboard. On the system keyboard, this is seen as the button with a globe on it. You tap once to switch to the next keyboard and long press it to view available keyboards.

Creating the Keyboard

We are going to look at two ways to build a keyboard’s UI – programmatically and using an Interface Builder document. The method you pick is a matter of preference, but I find that if the keyboard UI is complicated, it is better to use an Interface Builder document where you’ll be able to use Auto Layout and preview the effects of the constraints you’ve added without running the app, while in the case of creating the UI in code, you’ll end up writing a lot of code to set up constraints, which you can only see the effects of by running the project. Debugging this can be a bit of a headache.

To get started, create a new iPhone Simple View Application. Name it AC Custom Keyboard. This will be the container app for the custom keyboard. All extensions must be bundled within a containing app, and the containing app must provide some functionality. Apple are strict on this and so you cannot just have a skeleton container app whose sole purpose is to distribute your extension.

Next we’ll add an extension target to the project. Click on the project in the Project Navigator then select Editor > Add Target > iOS > Application Extension > Custom Keyboard and click Next

custom_extension

On the next window, set AC Keyboard as the product name and leave the other fields as they are and click Finish. You’ll be prompted to activate the new targets’ scheme, click Cancel.

A new group will have been created on the Project Navigator with the name of your keyboard – AC Keyboard. In it is the keyboard’s view controller and a property list file. The Info.plist file contains settings for the keyboard. Notable ones are:

  • Bundle display name – This is the display name for the keyboard, change it if you want a different name to be seen by users.
  • isASCIICapable – This is a key found within the NSExtension/NSExtensionAttributes dictionary. It determines if the custom keyboard can support ASCII characters.
  • PrefersRightToLeft – Specifies the direction of the primary language of the keyboard.
  • PrimaryLanguage – The primary language of the keyboard. The default is en-US.
  • RequestsOpenAccess – Determines the network access of the keyboard.

In Main.storyboard, add a TextView to the main View. Center it so that it completely covers the view. Run the application and bring up the keyboard by tapping anywhere on the screen. To change keyboards, tap the globe button on the system keyboard. If there is no globe button, tap the button with a smiley icon. If you want to see all available keyboards, hold the globe key and a list of keyboards will appear. Our keyboard isn’t on the list. You need to first add it in Settings.

Navigate to Settings > General > Keyboard > Keyboards > Add New Keyboard and select AC Custom Keyboard. This will add it to the list of available keyboards. Go back to your app and bring up the keyboard by tapping the text view. Tap and hold the globe key and select AC Keyboard from the list that pops up.

set-custom-keyboard

On switching from the system keyboard to the custom one, you will see an almost empty keyboard with only one button for the Next Keyboard.

keyboard_default_view

To add some keys to the keyboard, open KeyboardViewController.swift and make the following changes.

In viewDidLoad() add the following at the bottom of the function right after the code for the next keyboard button.

1
2
3
4
5
6
7
8
9
10
11
let buttonTitles = ["Q", "W", "E", "R", "T", "Y"]
var buttons = createButtons(buttonTitles)
var topRow = UIView(frame: CGRectMake(0, 0, 320, 40))
       
for button in buttons {
    topRow.addSubview(button)
}
       
self.view.addSubview(topRow)
       
addConstraints(buttons, containingView: topRow)

Next add the function below which will create buttons with titles of the strings passed into it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func createButtons(titles: [String]) -> [UIButton] {
       
    var buttons = [UIButton]()
       
    for title in titles {
        let button = UIButton.buttonWithType(.System) as UIButton
        button.setTitle(title, forState: .Normal)
        button.setTranslatesAutoresizingMaskIntoConstraints(false)
        button.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
        button.setTitleColor(UIColor.darkGrayColor(), forState: .Normal)
        button.addTarget(self, action: "keyPressed:", forControlEvents: .TouchUpInside)
        buttons.append(button)
    }
       
    return buttons
}

This function iterates through an array of Strings and creates buttons with the respective titles. It also adds a target to each button so that when it is tapped, the function keyPressed() will be called. Next add this function.

1
2
3
4
5
func keyPressed(sender: AnyObject?) {
    let button = sender as UIButton
    let title = button.titleForState(.Normal)
    (textDocumentProxy as UIKeyInput).insertText(title!)
}

Here you get the title of the tapped button and insert it at the insertion point of the current text input object via the textDocumentProperty. This is an object conforming to the UITextDocumentProxy protocol, which acts as a proxy between the keyboard and the text input object that summoned it.

Next we add the addConstraints() method which will add constraints to the buttons and the containingView.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func addConstraints(buttons: [UIButton], containingView: UIView){
       
        for (index, button) in enumerate(buttons) {
           
            var topConstraint = NSLayoutConstraint(item: button, attribute: .Top, relatedBy: .Equal, toItem: containingView, attribute: .Top, multiplier: 1.0, constant: 1)
           
            var bottomConstraint = NSLayoutConstraint(item: button, attribute: .Bottom, relatedBy: .Equal, toItem: containingView, attribute: .Bottom, multiplier: 1.0, constant: -1)
           
            var leftConstraint : NSLayoutConstraint!
           
            if index == 0 {
               
                leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: containingView, attribute: .Left, multiplier: 1.0, constant: 1)
               
            }else{
               
                leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: buttons[index-1], attribute: .Right, multiplier: 1.0, constant: 1)
               
                var widthConstraint = NSLayoutConstraint(item: buttons[0], attribute: .Width, relatedBy: .Equal, toItem: button, attribute: .Width, multiplier: 1.0, constant: 0)
               
                containingView.addConstraint(widthConstraint)
            }
           
            var rightConstraint : NSLayoutConstraint!
           
            if index == buttons.count - 1 {
               
                rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: containingView, attribute: .Right, multiplier: 1.0, constant: -1)
               
            }else{
               
                rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: buttons[index+1], attribute: .Left, multiplier: 1.0, constant: -1)
            }
           
            containingView.addConstraints([topConstraint, bottomConstraint, rightConstraint, leftConstraint])
        }
    }

Run the app and you should see a similar keyboard as shown below

programmatic_ui

As you can see, adding views programmatically requires you to set up constraints in code and if your keyboard’s UI is complicated and has many keys, this can get complicated and hard to debug. Next we will look at using the Interface Builder to create buttons.

Create a nib file by navigating to File > New > File > iOS > User Interface > View. Name it KeyboardView and make sure it is under the AC Keyboard target.

Select the nib’s view and in the Attributes Inspector, set its Size as Freeform and Status Bar to None. Then go to the Size Inspector and set its width to 320 and height to 220.

In KeyboardViewController.swift add the following in viewDidLoad() after the call to super.viewDidLoad(). This sets the nib file as the view of the view controller.

1
2
3
let nib = UINib(nibName: "KeyboardView", bundle: nil)
let objects = nib.instantiateWithOwner(self, options: nil)
view = objects[0] as UIView;

Add a view to the nib’s main view. Click on the Pin menu at the bottom of the Interface Builder interface and give it the following constraints – Height of 40, Trailing space of 0, Leading space of 0 and Top space to container of 42. Make sure Constrain to margin is not checked.

add_constraints

Add 9 buttons to the view. Set their text color to Dark Gray and change their titles to these letters respectively: ‘A’, ‘S’, ‘D’, ‘F’, ‘G’, ‘H’, ‘J’, ‘K’, ‘L’. Now for the constraints.

With each button, select the Align menu and select Vertical Center in Container.

alignment auto layout

Select the A button and add a Leading space to Superview fo 5. Select L and add a Leading space to Superview of 5. Control-drag from A to S and select Horizontal Spacing. We’ll change its value later. Do the same with S to D, D to F, F to G, and so on.

Select A and in the Size Inspector, make sure that its Leading Space to S ie set to a constant of 5. Select the next letter and set its Trailing space to the letter following it to 5. When you are done, all letters should have a Leading Space and Trailing space set to 5. Select the main view then Editor > Resolve Auto Layout Issues > All Views > Update Frames.

Run the application and you should see the keys you just added set up below the ones you added in code.

interface_builder_ui

To create outlets and actions for the keys, select the File’s Owner from the Document Outline, then in the Identity Inspector, set the class as KeyboardViewController. You can then create actions and outlets like you usually do in storyboard files by Control-dragging from a control to the view controller class. (You’ll see more of this in the example that follows).

Now that we have seen how to create a keyboard’s UI programmatically and with a nib file, let us add some functionality to it. For this, I have a starter project that we will be using. The project is a of a simple keyboard that we will add some functionality to. It is shown below. You should note that to keep it simple, I didn’t design for all size classes so, while it looks nice on the iPhone 5S, it doesn’t look quite the same for bigger screens. You can download the code here. Also note that the keyboard’s name is Appcoda Keyboard and not the AC Keyboard we had earlier.

keyboard_starter

I have already set up the actions and outlets we’ll require, but haven’t written the code for them (except for the Next Keyboard key).

First, you’ll notice that the Next Keyboard key has been replaced with a key titled KB. The action method for this can be seen it the view controller file as shown below.

1
2
3
    @IBAction func nextKeyboardPressed(button: UIButton) {
        advanceToNextInputMode()
    }

We are first going to set up the actions for keys with letters and symbols i.e. any key you tap and see its title as the typed text. I created an action method for all these keys called keyPressed(). Modify this method as shown.

1
2
3
4
    @IBAction func keyPressed(button: UIButton) {
        var string = button.titleLabel!.text
        (textDocumentProxy as UIKeyInput).insertText("\(string!)")
    }

This is similar to what we had before. The title of the button is inserted at the insertion point of the current text input object via the textDocumentProperty. All the letters we type will be in Caps, but we will fix this shortly. Next modify the following functions to set up actions for the backspace(BS), space(SPACE) and return(RTN) keys respectively.

1
2
3
4
5
6
7
8
9
10
11
    @IBAction func backSpacePressed(button: UIButton) {
        (textDocumentProxy as UIKeyInput).deleteBackward()
    }
   
    @IBAction func spacePressed(button: UIButton) {
        (textDocumentProxy as UIKeyInput).insertText(" ")
    }
   
    @IBAction func returnPressed(button: UIButton) {
        (textDocumentProxy as UIKeyInput).insertText("\n")
    }

Run the app and test the keys.

In the view file, you will notice two views labelled Char Set 1 and Char Set 2. These are on the same row with one on top of the other. In viewDidLoad() the second view is hidden. Modify the charSetPressed() function as shown so that when the user presses the key labelled 1/2, the key’s text will change to 2/2 and a new set of characters will appear on the first row of the keyboard.

1
2
3
4
5
6
7
8
9
10
11
    @IBAction func charSetPressed(button: UIButton) {
        if button.titleLabel!.text == "1/2" {
            charSet1.hidden = true
            charSet2.hidden = false
            button.setTitle("2/2", forState: .Normal)
        } else if button.titleLabel!.text == "2/2" {
            charSet1.hidden = false
            charSet2.hidden = true
            button.setTitle("1/2", forState: .Normal)
        }
    }

charset
If you look at the system keyboard, there is usually an indication, in the form of a brief animation when you tap a key. We should add some sort of feedback so the user knows that they tapped the right key. Add the following at the end of the keyPressed() method.

1
2
3
4
5
6
    UIView.animateWithDuration(0.2, animations: {
            button.transform = CGAffineTransformScale(CGAffineTransformIdentity, 2.0, 2.0)
            }, completion: {(_) -> Void in
                button.transform =
                    CGAffineTransformScale(CGAffineTransformIdentity, 1, 1)
        })

This makes a key scale up briefly when tapped before going back to its original size.

Lastly we’ll implement the Capslock key(CL). Modify the capsLockPressed() function as follows.

1
2
3
4
5
6
7
8
    @IBAction func capsLockPressed(button: UIButton) {
        capsLockOn = !capsLockOn
       
        changeCaps(row1)
        changeCaps(row2)
        changeCaps(row3)
        changeCaps(row4)
    }

Notice that at the beginning of the file, we set capsLockOn to true. The above method toggles this value to be true/false and then calls a function that will change the case of the typed text as well as the title of the buttons. Add the following method to the class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    func changeCaps(containerView: UIView) {
        for view in containerView.subviews {
            if let button = view as? UIButton {
                let buttonTitle = button.titleLabel!.text
                if capsLockOn {
                    let text = buttonTitle!.uppercaseString
                    button.setTitle("\(text)", forState: .Normal)
                } else {
                    let text = buttonTitle!.lowercaseString
                    button.setTitle("\(text)", forState: .Normal)
                }
            }
        }
    }

Run the app and you should be able to change the case of the letters.

custom keyboard - capslock

Conclusion

This tutorial doesn’t cover all that you can do with the keyboard extension. There is still a lot you can do to make the keyboard better. You can have it detect the beginning of sentences so that the first letter of the next typed word is capitalized, you can add auto-correction, auto-completion e.t.c. This is a new technology on the Apple platform, and it will be interesting to see what developers come up with.

For your reference, you can download the Xcode project here.

Source : appcoda[dot]com

Thứ Tư, 29 tháng 10, 2014

How to Hide Photos on iPhone & iPad with the iOS Hidden Album

How to hide pictures in iOS

Everyone likely has a few photos sitting on their iPhone they’d rather nobody else see, whether it’s embarrassing selfies, poorly filtered or edited pics, a picture of a receipt or personal paperwork, or anything else in the realm of private photos. Those pictures can make showing someone another picture on your iPhone (or iPad) an awkward experience, as you’re hoping they don’t start flipping through your Camera Roll to discover that terrible photo of you after winning the pie eating contest. Fortunately the newest versions of iOS include a new way to mitigate that potential awkwardness by hiding select photos.

The photo hiding feature must be enabled individually for each picture, as it’s set on a per-image basis. At the moment there is no bulk hide function like there is with the ability to bulk remove a bunch of photos all at once, so you may want to get in the habit of regularly hiding the pictures you don’t want to show up in your general photos app views.

Note this feature is available only to iOS 8 and newer, and though we’re going to focus on the iPhone here, it works the same on the iPad and iPod touch too.

Hiding a Photo in iOS

  1. Open Photos and go to Camera Roll or Albums as usual
  2. Tap on the picture you wish to hide, this will open it as usual
  3. Tap and hold on the photo itself to bring up an action menu, choose “Hide”
  4. Confirm that you want to hide the picture by tapping “Hide Photo”

Hide photos in iOS

Now that a picture or many are hidden, they’ll become invisible to the Collections, Years views, and instead are placed in a separate “Hidden” album.

Accessing Your Hidden Photos in iOS

  1. Open the Photos app and tap on “Albums” view
  2. Locate in the list of Albums the folder called “Hidden” (note the thumbnail is not automatically generated for that folder, offering additional privacy)
  3. Find your hidden photos in the Hidden Album

This is where all of your hidden photos will be stored.

Hidden photos in iOS Albums

Note that while a picture is hidden, it can still be shared or sent through messages as usual, as long as you access it from this hidden album.

Unhiding a Picture in iOS

  1. From the Hidden photo album, tap on the picture you want to unhide
  2. Tap and hold on the picture and choose “Unhide” from the submenu that pops up

Unhide photos in iOS
This sends the picture back to the general Camera Roll and it becomes accessible to all albums and collections views again.

Is the Photo Really Hidden on the iPhone? Kind Of

It’s important to understand how the hide photo function works: the photo(s) are hidden from the camera roll, Moments, Collections, and Year view, but are still visible in a photo album not-so-discretely called “Hidden”. In other words, while this is very effective at hiding photos from casual iPhone use and from flipping through your pictures in iOS, anyone who knows to look for the ‘Hidden’ album can still view the hidden images.

This is a decent way of handling your truly private pictures, but if you’re concerned about someone discovering the Hidden photos album, consider using the send-to-self trick to avoid offering photo album and camera roll access, or maybe just message them the pictures instead.

Enjoy this? Don’t miss our tons of other Photos app tips.

Source : osxdaily[dot]com

Thứ Ba, 28 tháng 10, 2014

Set Up Apple Pay on iPhone

Apple Pay

Apple Pay is a contactless payment platform that is newly available to iPhone 6 users. It works well and is incredibly simple; once you’ve added a card to Apple Pay, you just need to wave your iPhone over an Apple Pay compatible NFC payment terminal to pay for whatever you’re buying. The iPhones built-in TouchID sensor serves as an ID mechanism to prevent unauthorized usage, and you never need to pull out a credit or debit card after its configured. And yes, it really works that well once you have it setup, and that’s what we’re going to cover here.

To use Apple Pay, you’ll need at least iOS 8.1 on a new iPhone 6 or iPhone 6 Plus (the newest iPhone models have NFC payment chips, older models do not), and an Apple Pay compatible credit or debit card. Note that you only need the card once for setup purposes, after that you can leave it behind. Supported cards vary but Apple is maintaining a list of banks and bankcards that support the feature here, the list below is current as of now but is sure to change as more banks add additional card support.

Apple Pay compatible cards at the moment

Oh and if you have a Wells Fargo credit card, they’ll even give you $20 to try out Apple Pay, or $10 if you only have a Wells Fargo debit card. Free money is great, right?

Adding a Card to Apple Pay on Your iPhone

Got a compatible credit or debit card, iOS 8.1, and a new iPhone? You’re good to go, the rest is a piece of cake:

  1. Open the Passbook app and tap on “Set Up Apple Pay”
  2. Choose “Add a new Credit or Debit Card”
  3. Set up Apple Pay on the iPhone

  4. Don’t fill anything out, you can do it automatically with your iPhone Camera! Get out your credit/debit card and place it in reasonably bright lighting, then click on the Camera icon next to “Card Number” and line everything up, give it a moment to recognize the card and be sure all details are correct
  5. Autofill the Apple Pay credit card details with camera

  6. Agree to the Terms and Conditions, then go through the verification process to enable Apple Pay for the card – some cards make you call a number to verify, others have an app to verify

The setup is really quite simple, if you feel like it you can go through the process again to add another card or several cards to Apple Pay. At the moment there appears to be an 8 card limit, so plastic jugglers may need to prioritize which cards they want to add to their iPhone. Each card gets added to Passbook and you can toggle between them during payment if you want to use one rewards card over another.

Making a Payment with Apple Pay

So how about the payment side of things? Paying with Apple Pay is a piece of cake after you have it setup properly. When it comes time to checkout, hover your iPhone over the NFC payment terminal, the iPhone will get a little alert indicating a payment is ready, then you just put your finger on Touch ID to complete the checkout. That’s it. No signing anything, no swiping anything, no handing over a piece of plastic.

Using Apple Pay with an iPhone and NFC Terminal

Support for Apple Pay is still rolling out across retailers, stores, and restaurants, but something like 200,000 stores currently support it already, and that number is likely to grow more rather quickly. Much like the supported cards list, Apple is also maintaining a list of stores that allow usage of Apple Pay, at the moment it includes everything from Whole Foods, McDonalds, Macy’s, Bloomingdales, Nike, Texaco, Walgreens, Subway, Petco, Office Depot, Chevron, Toys R Us, the Apple Store, and quite a few more.

Apple Pay Retailers List

Though Apple Pay is officially supported in the USA at the moment, multiple reports have indicated that Apple Pay is usable right now outside of the USA in a variety of countries, including the UK, Australia, UAE, and Canada, if the card is issued with US credentials.

If you have a compatible card and a new iPhone, take the time to set up Apple Pay, it’s pretty nifty and feels a bit like the future. And speaking of the future, soon you’ll be able to use the Apple Watch for making payments this way too.

Source : osxdaily[dot]com

Chủ Nhật, 26 tháng 10, 2014

Spotlight Search Results Blank, or Spotlight Not Working in iOS 8? Here’s a Really Silly Temporary Fix

Spotlight not working in iOS

Spotlight gained many new features and improvements with the latest version of iOS, but along with those changes came a curious bug that seems to randomly prevent Spotlight from working at all on an iPhone or iPad with empty search results.


The situation is difficult to reliably reproduce since it does appear to be completely random, but the symptom is always the same; Spotlight is summoned as usual by pulling down on the Home Screen of iOS, but no matter what is typed into spotlight, nothing is returned at all, you get just a blank screen with no search results. Even searching for something obvious like “phone” (which should reveal the phone app as well as anything that mentions “phone” including emails, messages, apps, something on wikipedia or the web, and whatever else, returns absolutely nothing – the keyword or text you type doesn’t matter, nothing works in Spotlight when you hit this bug.

Having experienced this regularly on an iPhone 6 Plus with the latest version of iOS available (8.1, more on that in a moment), I have found a really dumb workaround to resolve the problem consistently: Send yourself an email from the iPhone to your iPhone email address

Yes seriously. I know that sounds absurd, but sending your iOS device an email actually makes Spotlight search start working again. All you need to do is send yourself an email to whatever mail account is setup on the iOS device, and once the new email is detected by iOS Mail app (as indicated by a Notification or the new mail chime), suddenly Spotlight works again.

iOS Spotlight not working vs Spotlight working in iOS

This is very obviously a bug with Spotlight, but based on emails and comments we’re getting here, it seems to be fairly widespread, as users report that Spotlight has either stopped working entirely or that Spotlight won’t return any results they were accustomed to seeing, instead users get the entirely unhelpful blank screen. Some users have reported that changing the Search results priority can also resolve issues with iOS Spotlight, but that was less reliable in testing. What makes this bug even more odd is that the iOS 8.1 release notes specifically mention a fix for Search not displaying results is included, but perhaps that was a different problem than this one.

I figured out this silly email trick accidentally when troubleshooting the blank Spotlight results problem by emailing myself a few notes. Once the new mail notification came through, Spotlight magically worked to show search results again. I have been able to replicate the fix on multiple occasions, which, while fairly goofy, is helpful and certainly preferable to the other solution that works to repair Spotlight malfunctioning in iOS, which is to reboot the iPhone (or iPad).

If Spotlight is showing you a blank screen without any search results or is randomly not working for you in iOS, give this a try, and let us know if it works for you too. Be sure to mention what iOS version you’re on too.

Source : osxdaily[dot]com

Thứ Sáu, 24 tháng 10, 2014

Creating Hello World App in Swift Using Xcode 6

0 Flares 0 Flares ×

The Hello World tutorial was the first programming article written for our iOS programming course. As Apple released Xcode 6, the tutorial is no longer up-to-date. We received quite a lot of emails about the tutorial update. So here you are. Instead rewriting the same tutorial in Objective-C, we’ll show you how to create the Hello World app in Swift. What’s more, we create a screencast for you.

If this is the first time you come across the tutorial, you may wonder why we teach you building a Hello World app. This programming tutorial is written for absolute beginners. We want to encourage you to learn programming. So the first app should be very simple. Despite its simplicity, the “Hello World” app serves a few purposes:

  • It gives you an overview about the syntax and structure of Swift, the new programming language of iOS.
  • It also gives you a basic introduction to the Xcode 6 environment. You’ll learn how to create a Xcode project and lay out your user interface using Storyboard. Even if you’ve used Xcode 5 before, you’ll learn what’s new in the latest version of Xcode.
  • You’ll learn how to compile a program, build the app and test it using the Simulator.
  • Lastly, it makes you think programming is not difficult. I don’t want to scare you away from learning programming. It’ll be fun.

You’ll need to use Xcode 6 (or up) to work on the Hello World project. If you haven’t upgraded to Xcode 6, just download it via this direct iTunes link.

Okay, let’s get started.

For your reference, you can download the sample Xcode project from here. I hope you enjoy our first screencast. Feel free to leave us comment and share your thought about the tutorial.

If you like the screencast, check out our new Swift programming book and get the free chapter to understand how the Hello World app works.

Source : appcoda[dot]com

Pangu8 Bootloop Fix: do not open Cydia until you have done this!

Screen Shot 2014-10-22 at 2.56.10 PM

Over on Reddit at /r/jailbreak, it is a great place. It is where the jailbreak community thrives in helping one another with issues, where one can propose an idea for a tweak and be made a reality and where one can get the latest scoop on all thing jailbreaking.

I would like to credit the people who have helped out on this post here, including the original poster, Noeliel, ReddestDream for his diagnosis and clayfreeman for his fixes.

Pangu8 was released in the past few days for developers to update their tweaks and of course, many users under excitement have decided to jailbreak their iOS 8 devices. For those who’ve gone ahead and done so, you’ve probably also installed Cydia. Cydia Substrate was also released.

If you are up to this step and have or plan to:

  • Use a passcode of any type
  • Use Touch ID
  • Use Find My iPhone

Do not open Cydia until you have performed the fix below to prevent your jailbroken iOS 8 device from entering a boot loop. If you have already opened Cydia and it has already ‘prepared’ your filesystem, scroll down for an alternate fix.

Due to Apple’s new security with the iOS 8 filesystem, the process of Cydia ‘preparing’ your filesystem will cause a bootloop when you attempt to reboot your device. Take the time to read this comment posted by ReddestDream on this post on /r/jailbreak, detailing why this is happening with Pangu8. Here’s a short snippet on the highly informative comment.

Historically, Cydia has solved this problem by relocating large files on the System Partition, such as Stock Wallpaper, Stock Ringtones, System Applications, and Winterboard themes, to the User Partition and then connecting them back to the System Partition through a system of Symbolic Links (Unix Shortcuts).

This process doesn’t work so well in iOS 8, which added a security feature that encrypts the User Partition when the device is protected by a Passcode, TouchID, or Find my iPhone.

This why the Bootlooping is occurring with PanGu8 on iOS 8 devices after Cydia rearranges the filesystem to make more room on the System Partition. iOS cannot access the system files necessary for booting that have been stashed to the User Partition until the user decrypts them by entering the Passcode, and the user cannot enter the Passcode until iOS boots.

Until a permanent fix comes out, here is how to avoid your device being sent into a bootloop if you have installed Cydia but have not yet opened the application and allowed it to prepare your filesystem. These instructions are snippets from user comments of clayfreeman on the same Reddit post as above. Here’s a link to the original comment for your reference. If you are uncomfortable with taking the risk, either do not enable Touch ID, passcode and Find My iPhone until a fix is made or restore to stock iOS until a proper fix is deployed.

  1. Make a full backup of your iOS device on iTunes before attempting any of these steps. They are to be performed at your own risk. 
  2. Download the patched move.sh and replace the existing one with it in /usr/libexec/cydia on your device (use an SFTP client as detailed in our installation of Cydia tutorial)
  3. Whilst you are connected to your device, ensure you do not have a  /User/Library/Caches/<something>.csstore file at that respective location. If so, delete it before proceeding.
  4. Reboot your device. Do not open Cydia yet!
  5. After the reboot, now allow Cydia to open and prepare your device’s filesystem. It should respring after completion.
  6. Now, you may enable Touch ID and Find My iPhone, to additionally use a passcode lock on your device.

Here is how to avoid your device being sent into a bootloop if you have installed Cydia and have opened the application and allowed it to prepare your filesystem. (this may or may not work if you are already in a bootloop and are SSHing into your device over USB). These instructions are snippets from user comments of clayfreeman on the same Reddit post as above. Here’s a link to the original comment for your reference.

  1. Make a full backup of your iOS device on iTunes (if possible) before attempting any of these steps. They are to be performed at your own risk.
  2. Create a stash on the system partition by typing mkdir -p /stash
  3. Move each stash on the user partition to the system partition by typing mv /var/stash/* /stash
  4. Type the following commands to redirect symlinks put in place by Cydia:

rm /Applications; ln -s /stash/*/*/Applications /Applications

rm /Library/Ringtones; ln -s /stash/*/*/Ringtones /Library/Ringtones

rm /Library/Wallpaper; ln -s /stash/*/*/Wallpaper /Library/Wallpaper

rm /usr/include; ln -s /stash/*/*/include /usr/include

rm /usr/lib/pam; ln -s /stash/*/*/pam /usr/lib/pam

rm /usr/share; ln -s /stash/*/*/share /usr/share

Whilst you are connected to your device, ensure you do not have a  /User/Library/Caches/<something>.csstore file at that respective location. If so, delete it before proceeding. If you aren’t already in a bootloop,  test that SpringBoard will restart by typing killall -9 SpringBoard (case sensitive). If you are able to respring, reboot your phone by typing reboot.

If you wish to revert the fix, run these commands as of below when connected to your device via SSH. This must be done when an official update is released to combat this issue!

rm /Applications /Library/Ringtones /Library/Wallpaper /usr/lib/pam /usr/include /usr/share

mv /stash/*/*/Applications /

mv /stash/*/*/Ringtones /Library

mv /stash/*/*/Wallpaper /Library

mv /stash/*/*/pam /usr/lib

mv /stash/*/*/include /usr

mv /stash/*/*/share /usr

 

Let us know if you face any issues so we can follow up. If worse comes to worse, if you are already in a bootloop, try downloading your device’s iOS 8.1 IPSW from a legal source and selecting the IPSW in iTunes to ‘update’ to (using command click or shift click) even if you are already on iOS 8.1. This should allow your device to boot up again, without a jailbreak.

If this doesn’t work, attempt to make a backup of your device and restore to the latest firmware (or maybe head onto the Reddit post and check for progress)

Source : jailbreaknation[dot]com

Thứ Năm, 23 tháng 10, 2014

Install Cydia On Jailbroken iOS 8 / 8.1

cydia

This tutorial is for anyone that completed our iOS 8 Jailbreak tutorial and wants to use Cydia the main installer. Cydia Substrate was recently updated for iOS 8 making a great amount of tweaks viable. This tutorial will install Cydia via SSH.

This tutorial works for all iPhone, iPod Touches, and iPads

1. Open the Pangu App on your jailbroken iPhone.

2. Click Open SSH.

3. Click install.

4. Download Cyberduck for your computer.

5. Connect your iPhone to the safe WIFI as your computer.

6. Open Cyberduck click new connection SFTP / SSH enter your devices ip (found on settings on the device), then use root as name and alpine as password.

7. Navigate to  private/var/root and drag in these two files

8.  Execute the line of code in the menu bar Go > Send Line Of Code

dpkg --install cydia-lproj_1.1.12_iphoneos-arm.deb cydia_1.1.13_iphoneos-arm.deb

9.  Reboot your device

10. Open Cydia install Cydia substrate then send the command

killall backboardd

 Cydia will now work!

Source : jailbreaknation[dot]com

Enable Continuity & Handoff on Unsupported Macs with Continuity Activation Tool

Continuity in Mac OS X and iOS

Continuity and Handoff are two great features of OS X Yosemite and iOS 8 that allow an iPhone or iPad to ‘handoff’ an application, like a half-written email, over to the Mac to be completed in the Mac Mail app. It’s one of the major reasons for iOS and Mac users to upgrade to OS X Yosemite and it greatly improves productivity, but not all Macs support the feature. The Continuity Activation Tool changes that, it’s a third party utility that brings Handoff and Continuity support to some Macs that aren’t supposed to have the feature.


There are a few Macs that can use this utility right out to the box to enable the Handoff feature, whereas some other Macs would require a hardware change to a newer Bluetooth card, making it somewhat impractical unless that was on your upgrade agenda anyway. The two Macs that will get the most use out of this immediately since they don’t require a hardware change are the 2011 MacBook Air line, and the 2011 Mac Mini line, and you’ll obviously need an iOS 8 device as well. The full Mac compatibility list is below.

The utility requires you to right-click and choose “Open” to get around the developer warning, then runs in the Terminal. Hit 1 to begin the activation process and follow the onscreen instructions.

Continuity Activation Tool running in OS X

For what it’s worth, I had to run the tool twice to get the Handoff feature to actually show up in the System Preferences and then start working on a 2011 MacBook Air, but your experience may be different. This is completely unsupported by Apple, so you may want to back up your Mac before trying it out.

With the feature now possible to enable, you’ll have to follow a few additional onscreen steps to get it all working, including enabling the feature in System Preferences on the Mac, being sure it’s on in iOS, then logging out and back into iCloud on the Mac again. You can test the feature by having someone call your iPhone, it will now ring on your Mac if it wasn’t before, and if you didn’t turn off the Mac ringing feature already (you probably soon will if you get a lot of calls). You can also open Safari or Mail app on the iPhone or iPad to trigger the current session handoff to the Mac with OS X yosemite.

Since you can enable Handoff on some Macs with the help of a simple tool, it makes you wonder why Apple didn’t support those Macs to begin with, but there very well could be a reason that isn’t immediately obvious. Typically, a Mac must have Bluetooth 4.0 compatibility to have the feature accessible. The following is a list of Macs that will work with the tool, though as noted earlier some may require a different hardware adapter:

continuity-tool-compatibility-list

This is a great find from MacRumors, the tool was created by some of their enterprising forum members and works as advertised.

Source : osxdaily[dot]com

Thứ Tư, 22 tháng 10, 2014

Pangu iOS 8 – 8.1 Jailbreak Released

image

Earlier today the Pangu Jailbreak team released an update to their Jailbreak software that works for devices running any version of iOS 8. The initial launch was very buggy and included problems such as the deleted photos. Since the initial release a new version was uploaded which solved most of the problems. Unfortunately the jailbreak still does not support Cydia making it not very noob friendly. Instead it grants ssh access on the device.

Saurik announced that he is working on support, but until then Apple has time to patch the jailbreak. This is mainly because of the lack of communication between the two teams and the sporadic releases Pangu is known to make.

Even though Cydia is not yet working we will post a Jailbreak tutorial shortly.

Pangu supports iPhone 4s, iPhone 5, iPhone 5c, iPhone 5s, iPhone 6, iPhone 6 plus, all iPads and iPod touches.

Source : jailbreaknation[dot]com

Thứ Ba, 21 tháng 10, 2014

Did iOS 8.1 Reduce Your Battery Life? This May Help

iOS 8.1 battery life

Though the iOS 8.1 update includes many bug fixes that resolve some of the frustrating annoyances that popped up in earlier versions, a handful of users have experienced something else with iOS 8.1; quickly reducing battery life. No, we’re not talking about shaving a few minutes off how long your iPad or iPhone lasts, we’re talking dramatically reduced battery life with rapid draining.


I experienced this fast battery draining thing myself with iOS 8.1 on an iPhone 6 Plus, which after the update started to run physically warm to the touch and was losing battery at a highly unusual rate, where you can basically watch the percentage indicator tick down in real time. Several of our readers reported the same problem. That’s obviously not normal behavior, but with just a few adjustments I was able to remedy the situation and get the iPhone 6 Plus back to it’s excellent battery life. Presumably many other users who are experiencing a similar issue will find these tips to be effective as well.

The iPhone Runs Hot or Feels Warm? Let it Sit, Then Maybe Force Reboot

First, if the iPhone is physically warmer than usual, this strongly suggests there’s some intensive CPU activity going on in the background of iOS. This is most likely to happen upon the first boot after an iOS update, and it’s probably iOS running cleanup, Spotlight, and if you have it enabled – automatic updates. Give the iPhone (or iPad) some time to complete whatever process it’s doing, in my case I just let the iPhone sit with the screen locked for about 30 minutes, and it ended up cooling itself off – but in the meantime it took a major hit to the remaining battery life.

If you’ve let the iPhone / iPad do it’s thing for a while and it’s still running notably warm to the touch, a force reboot may get things back to normal. Just hold down the Power button and Home button until the device restarts itself and you see the Apple logo  indicating a reboot has occurred.

Force restart an iPhone

When the iPhone boots up, it should very quickly cool off and run at normal temperature – and you’ll almost certainly notice an instant difference in how quickly the battery was going down.

Check for Resurfaced Old Location Based Reminders

We all know that location services can be a drain to battery, which makes this a bit of a weird enough one to probably be a bug; I discovered that multiple (very old) location based Reminders were suddenly back and reactivated to run in the background, frequently tapping into GPS and location services to determine the location of the iPhone for a now ancient reminder to function when a destination was hit. Figuring out of this is part of the problem is fairly easy, you’ll first see the familiar little arrow icon in the iOS status bar, then you see if Reminders is the reason why by doing the following:

  • Go to Settings > Privacy > Location Services > and look next to “Reminders” to see if there is a purple arrow next to the name
  • Check Location Services for Reminders

  • If the arrow next to Reminders is purple, open the Reminders app and look for old Location based Reminders that have mysteriously resurfaced and reactivated – check them off to insure they are again completed

Because you’ve almost certainly already long checked off these reminders, this is a bit odd to have them resurface. Presumably it’s a bug or maybe something to do with iCloud syncing, who knows, but it’s easy to fix. In my case, I had two ancient location specific reminders from Siri that were made ages ago which were nearby enough that the iPhone was frequently checking for. Weird. Check them off, and that was that.

Check Your Background Refresh Settings

Some iOS updates have a habit of adjusting settings again, typically turning things on that you already had turned off. This doesn’t always happen, but in my case it did with iOS 8.1 again, discovering that quite a few refresh settings had reactivated themselves. Check these yourself to see if they were reset during the update process:

  • Go to Settings > General > Background App Refresh and toggle off apps that you don’t want to refresh themselves when not in use

Check Background App Refresh for changes
In my experience, every single app that could was set to refresh in the background, despite having adjusted those long ago to not happen. Simply switching most of them off again had positive results.

By the way, these same tricks can help improve general sluggishness sometimes too, but if an iOS device feels abnormally slow, it can usually be sped up with these tips.

The above trio resolved my battery performance issues rather quickly, and I’m back to the amazing battery life of the iPhone 6 Plus which is one of the two main reasons which make it such an appealing iPhone to begin with.

If you’re having any similar battery draining problems post-iOS 8.1, let us know in the comments if you tried the above steps and if they helped you out, or if you’ve found something else to work, let us know that too.

Source : osxdaily[dot]com

Introduction to iOS 8 App Extension: Creating a Today Widget

0 Flares 0 Flares ×

In iOS 8, Apple introduced app extensions which let you extend functionality beyond your app and make it available to users from other parts of the system like in other apps or from the Notification Center. iOS defines different types of extensions, each tied to an area of the system such as the keyboard, Notification Center, e.t.c. A system area that supports extensions is called an extension point. Below is a list of the extension points in iOS.

  • Today – Shows brief information and can allow performing of quick tasks in the Today view of Notification Center
  • Share – Share content with others or post to a sharing website
  • Action – Manipulate content in a host app
  • Photo Editing – Edit photos or videos within the Photos app
  • Document Provider – Provide access to and manage a repository of files
  • Custom Keyboard – Provide a custom keyboard to replace the iOS system keyboard

app-extension-today-featured

We will cover all these extension points in this and subsequent tutorials. With this article, we will focus on the Today Extension.

How Extensions Work

Before getting started with the Today Extensions, first let’s take a look at how extensions work as some of the concepts covered here will be used later in the tutorial.

To start off, extensions cannot be stand alone apps. They are delivered via the App Store as part of the app bundle. The app that the extension is bundled with is known as the container app while the app that invokes the extension is the host app. You can have more than one extension in a container app.

When an extension is running, it doesn’t run in the same process as the container app. Every instance of your extension runs as its own process. It is also possible to have one extension run in multiple processes at the same time. For instance, if let’s say, you have a sharing extension which is invoked in Safari. An instance of the extension, a new process, is going to be created to serve Safari. Now, if the user goes over to Mail and launches your share extension, a new process of the extension is created again. These two processes aren’t going to share address spaces.

An extension cannot communicate directly with its container app, nor can it enable communication between the host app and container app. However, indirect communication with its container app is possible via openURL() or via a shared data container like the use of NSUserDefaults to store data which both extension and container app can read and write to.

The Today Extension

Today extensions, also known as widgets, appear on the Today View of the Notification Center. They provide brief pieces of information to the user and they even allow some interaction, though limited, right from the Notification Center. You’ve seen these available in previous versions of iOS, for e.g. Reminders, Stocks and Weather. In iOS 8, third party apps can now have their own widgets.

We are going to see how to create a widget. So that we can focus on this and not on creating an app from scratch, I have provided a starter project that you can download here. The project is of a simple weather app which shows various weather information of a particular location. You will need an internet connection for the data to be fetched. To keep it simple, I didn’t include any GeoLocation functionality, and so user’s location is assumed to be Cupertino, CA. (You can easily modify the app to get weather data of your current location by using core location and placing the latitude and longitude values in the API url. A good tutorial on how to do this can be found here ).

The project comes with an API key that you can use, but should it stop working, you can register at forecast.io to get your own API key. Then replace the key in the url in the WeatherService.swift file

On running the app, you should see a view with weather details as shown below.

App Extension Demo - Starter Project

We are going to create a Today extension of the app that will show a brief summary of the weather in a view that can be expanded to reveal more data. We’ll also see how we can share data between the container app and extension. We’ll use this shared data to enable the user to set the location they want weather information on.

Embedded Frameworks and Code Reuse

Extensions are created in their own targets separate from the container app. This means that you can’t access common code files as you normally would in your project. To allow for code reuse between the container app and extension, you create an embedded framework which can be used across both targets. Place code that will need to be used by both the container app and extension in the framework to avoid code repetition.

In our app, both the extension and container app make a call to an API and update properties with the data from the API. Without using a framework, we would have to maintain two code bases with similar code, which would be inefficient and prone to errors.

To create a framework, select your project in The Project Navigator and add a new target by selecting Editor > Add Target.

Select iOS > Framework & Library > Cocoa Touch Framework from the window that appears. Set its name as WeatherDataKit and check that the language is Swift (we use Swift here, but you can select whichever language you prefer). Leave the rest of the options as they are and click Finish.

App Extension - Weather Data Kit

You will see a new target in the list of targets and a new group folder in the Project Navigator. When you expand the WeatherDataKit group, you will see WeatherDataKit.h. If you are using objective C or if you have any objective C files in your framework, then you will include all public headers of your framework here. We don’t have objective C code in our framework, so we won’t be editing this file.

You should note that app extensions are somewhat limited in what they can do and therefore not all Cocoa Touch APIs are available for use in extensions. For instance extensions cannot do the following:

  • Access the camera or microphone on an iOS device
  • Receive data using AirDrop (It can however send data using AirDrop)
  • Perform long-running background tasks
  • Use any API marked in header files with the NS_EXTENSION_UNAVAILABLE macro, or similar unavailability macro, or any API in an unavailable framework for example EventKit and HealthKit are unavailable to app extensions.
  • Access a sharedApplication object, and so cannot use any of the methods on that object

You can have the compiler let you know when you use disallowed APIs by selecting your framework from the list of targets. On the General tab, on the Deployment Info section, check Allow app extension API only.

app_extension_api

In the starter project, I had abstracted out common code into the files that are under the WeatherData folder group. Drag this folder into the WeatherDataKit group.

App Extension - Weather Group

Merely dragging a file from one target to another doesn’t make the file part of that target though. You have to change the file’s target membership yourself. To do this, select the WeatherDataViewController.swift file from the Project Navigator. Then open the File Inspector and change the files target in the Target Membership section by unchecking the Weather target and checking the WeatherDataKit target. Do the same for the WeatherService.Swift and WeatherData.swift files.

After doing this, there will be some error messages in ViewController.swift. Include the following import statement at the top of the file.

1
import WeatherDataKit

Build the app and it should work as before.

Creating the Widget

To create a widget, we’ll use the Today extension point template that Xcode provides. Select the project in the Project Navigator and add a new target by selecting Editor > Add Target. Select iOS > Application Extension > Today Extension and then click Next.

App Extension - Create Widget

Set the Product Name to Weather Widget and leave the rest of the settings as they are. Click Finish.

App Extension - Name Widget

You will get a prompt asking if you want to activate the Weather Widget scheme. Press Activate. Another Xcode scheme has been created for you and you can switch schemes by navigating to Product > Scheme and then selecting the scheme you want to switch to. You can also switch schemes from the Xcode toolbar.

From the list of available targets, select Weather Widget then on the General tab press the + button under Linked Frameworks and Libraries. Select WeatherDataKit.framework and press Add.

App Extension - Add Framework

With the framework linked, we can now implement the extension.

In the Project Navigator you will see that a new group with the widget’s name was created. This contains the extensions storyboard, view controller and property list file. The plist file contains information about the widget and most often you won’t need to edit this file, but an important key that you should be aware of is the NSExtension dictionary. This contains the NSExtensionMainStoryboard key with a value of the widget’s storyboard name, in our case “MainInterface”. If you don’t want to use the storyboard file provided by the template, you will have to change this value with the name of your storyboard file.

Open MainInterface.storyboard. You’ll see a simple view with a Hello World label. To run the extension, make sure the Weather Widget scheme is selected in Xcode’s toolbar and hit Run. A window will pop up for you to chose an app to run. This lets Xcode know which host app to run. Chose Today. With this selection, iOS will know to open Notification Center in the Today view, which in turn launches your widget. Notification Center is the Today Extension’s host app. Click Run and you should see the widget on your simulator’s/device’s Notification Center.

App Extension - Widget Initial Run

To display the weather data in our image, we first import the WeatherDataKit framework into our view controller. Add the following to the TodayViewController.swift file.

1
import WeatherDataKit

Then make the class a subclass of WeatherDataViewController by changing its declaration to the following.

1
class TodayViewController: WeatherDataViewController, NCWidgetProviding

Delete the Hello World label. Set the view’s height to 270 (Leave the width at 320).

Drag two labels and a button into the main view. Set the text of the labels as “Cupertino, CA” and “100” respectively and their color to Red: 66, Green: 145 and Blue: 211 (usually, you should select bright colors for your widget controls so that they are visible in the Notification Center’s dark blurry background). Delete the button’s title and set its image to ‘caret’. The caret.png file was included in the starter project. When you run the app, the image will not appear on the button. This is because the asset catalog has only been added to the container app’s target. To add it to the extension’s target, select Images.xcassets and in File Inspector, check Weather Widget. Leave the Weather target checked.

App Extension Demo - Asset Catalog

Drag a view into the main view and stretch it out so that its left and right and bottom sides hug the main view. Place the views roughly as shown below. You don’t need to be accurate, we will use Auto Layout for this. For visibility, I have left the view’s background color as white, but we will set it to clear color so that the whole widget blends with the Notification Center background.

App Extension - Initial Widget View

Select the view you just added, open the Identity Inspector and enter MoreDetailsContainer in the Label field of the Documents pane(you might need to expand the Documents pane to reveal the Label field). This gives the view a name and makes it easier to identify in the Document Inspector. Also set the views background color to clear color in the Attributes Inspector. You should have the following.

Widget without other data

The Apple Extensions Guide recommends widgets to be small and to have an adjustable height that allows users to show or hide information as appropriate. If your widget only shows brief information, this won’t be necessary but if it shows a lot of data, it is better to only make the most important information visible and allow the user the ability to expand the view to show more data. This is what we will do with our widget. The widget will only display the location and current temperature and on tapping the button with the caret image, the view will expand to reveal more weather data. We will set this data’s location and constraints relative to the MoreDetailsContainer view so that hiding and displaying this data will just be a matter of adjusting the MoreDetailsContainer view’s constraints.

Add the labels for the other data and place them on the MoreDetailsContainer view. Give them titles as shown below. You can set your own title, but note that I will be referring to them by their title if I need to e.g. summary label, Mostly Cloudy label, 100 label, e.t.c. I set the color of these labels to Light Text Color.

App Widget with Label Added

Select the Cupertino, CA label and then select Editor > Size to Fit Content. Do the same for the other labels that will be populated by the API call i.e. the 100, Mostly Cloudy, 0.65, 0.10 and 02:00 PM labels.

We’ll now add Auto Layout constraints to the view. Select the MoreDetailsContainer and then select Pin from the Auto Layout controls at the bottom of the Interface Builder canvas. Pin its Leading, Trailing and Bottom space to 0 and its Height to 220. Uncheck Constrain to margin checkbox. With this unchecked, there will be no padding around your view.

App Extension - Container View Constraint

Select the Cupertino, CA label and pin its Top and Leading space to 13 and 20 respectively. Make sure Constrain to margin is unchecked (for the rest of the article, have it unchecked for all the constraints we add)

cupertino_constraints

Select the caret button and pin its Top, Trailing and Bottom spaces to 10, 20, 10. Set its Width and Height to 30 and make sure both these checkboxes are selected.

caret_constraints

Select the 100 label and pin its Top and Trailing spaces to 13 and 20 respectively.

temperature constraint

Control-Drag from the Mostly Cloudy label to the Summary label and select Center Y. This will align the two labels centers. Do this for the other pairs of labels i.e. 0.65 and Humidity, 0.10 and Precipitation, e.t.c.

Select the Summary label and pin its Leading and Top spaces to 20 each. Also check its Width checkbox and set the value to 130.

Control drag from the Mostly Cloudy label to the Summary label and select Horizontal Spacing. Then select this constraint either from the view controller or from the document outline. In the Size Inspector, change the constraint’s Constant to 20.

modify_option_a

You can also select the constraint by selecting the Mostly Cloudy label, and then in Size Inspector in the Constraints pane, find the Leading Space to SUMMARY constraint and edit its Constant to 20.

modify_option_b

Control-Drag from the Humidity label to the Summary label and select Left. This will align its Leading position to the Summary label’s Leading position. Select that constraint and in the Size Inspector, make sure that its Constant is set to 0. Sometimes when you align the views Leading positions, a constant is set which will make them not properly aligned. Do the same for the Precipitation and Last Updated labels – Control-Dragging to the Summary label and selecting Left, then making sure the constraints Constant is 0.

Next select the Humidity label and Pin its Top space to 20. This sets its distance from the Summary label. Do the same for the Precipitation and Last Updated labels. Setting their distance from the conrol on top of them to 20 points.

Next Control-Drag from the 0.65 label to Mostly Cloudy label and select Left. Do the same for the 0.10 and 02:00 PM labels. This will align the labels starting point. Then select the main view and select Editor > Resolve Auto Layout Issues, under All Views, select Update Frames. Interface Builder will now show the constraints in their set positions.

Before setting up the labels to show real data from the API, run the app to make sure that everything is set correctly. You should see the following.

App Widget after auto layout

You’ll notice that the widget has a large left margin. If you want to fill the entire width of Notification Center, implement the following method. Place the following code in the TodayViewController class.

1
2
3
4
    func widgetMarginInsetsForProposedMarginInsets
        (defaultMarginInsets: UIEdgeInsets) -> (UIEdgeInsets) {
        return UIEdgeInsetsZero
    }

Run the project again and your widget will now fill the width of the Notification Center.

widget_width_fit

To implement the view’s Show More button, open the Assistant Header to reveal the TodayViewController.swift file next to the storyboard. Control Drag from the button to the view controller file and add an Outlet. Name it showMoreButton. Then Control-Drag again from the button and change the Connection to Action. Set the Type to UIButton and Name to showMore. You should have the following code added to the class.

1
2
3
4
    @IBOutlet weak var showMoreButton: UIButton!

    @IBAction func showMore(sender: UIButton) {
    }

We then need to create an outlet for the MoreDetailsContainer view’s height constraint. We’ll be changing its value to expand and shrink the view. In Document Outline, expand the MoreDetailsContainer and expand its constraints. Find the height constraint. It should be labelled Height – (220) – MoreDetailsContainer. Control-Drag from it to the view controller and create an Outlet. Name it ‘moreDetailsContainerHeightConstraint’. You should have the following.

1
    @IBOutlet weak var moreDetailsContainerHeightConstraint: NSLayoutConstraint!

Add the following property which will keep track of whether the view is expanded.

1
    var widgetExpanded = false

In viewDidLoad() add the following

1
    moreDetailsContainerHeightConstraint.constant = 0

This sets that view’s height constraints’ constant to 0, so that it’s hidden by default.

Modify the showMore() action method as follows.

1
2
3
4
5
6
7
8
9
10
11
    @IBAction func showMore(sender: UIButton) {
        if widgetExpanded {
            moreDetailsContainerHeightConstraint.constant = 0
            showMoreButton.transform = CGAffineTransformMakeRotation(0)
            widgetExpanded = false
        } else {
            moreDetailsContainerHeightConstraint.constant = 220
            showMoreButton.transform = CGAffineTransformMakeRotation(CGFloat(180.0 * M_PI/180.0))
            widgetExpanded = true
        }
    }

This checks to see if the view has been expanded, and sets the constraint back to 0, otherwise it sets it to 220, which is the height we had specified for the view. We also add an animation where the button caret faces down when the view is collapsed and points up when the view has been expanded.
With the interface set up, we should now connect the extension’s controls with the superview’s outlets. Open the extension’s storyboard file. In the Documents Outline, Control-Drag from the Today View Controller to the Cupertino, CA label and select locationLabel. Do the same for the 100 label and select temperatureLabel. Repeat for Mostly Cloudy(summaryLabel), 0.65(humidityLabel), 0.10(precipitationLabel) and 02:00 PM(timeLabel).

In TodayViewController.swift add the following property to the class.

1
    var latLong = "37.331793,-122.029584"

Add the following at the end of viewDidLoad()

1
2
3
4
5
    temperatureLabel.text = "--"
    summaryLabel.text = "--"
    timeLabel.text = "--"
    humidityLabel.text = "--"
    precipitationLabel.text = "--"

Then add the following method to the class. This makes the call to the API and updates the view.

1
2
3
4
5
6
7
8
9
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        getWeatherData(latLong, completion: { (error) -> () in
            if error == nil {
                self.updateData()
            }
        })
    }

Run the app and the widget should populate with real data.

App Weather Widget with Real Data

To enable the widget to update its view when it’s off-screen make the following changes to the `widgetPerformUpdateWithCompletionHandler`. The system takes a snapshot of the widget and periodically, will try to update it. If it updates successfully, the function calls the system-provided completion block with the NCUpdateResult.NewData enumeration. If the update wasn’t successful, then the existing snapshot is used.

1
2
3
4
5
6
7
8
9
10
    func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
        getWeatherData(latLong, completion: { (error) -> () in
            if error == nil {
                self.updateData()
                completionHandler(.NewData)
            } else {
                completionHandler(.NoData)
            }
        })
    }

Sharing NSUserDefaults between your app and a Today Extension

As stated earlier, a widget cannot directly communicate with a host app. Having them communicate may however be necessary for your project specification especially if you allow a user to configure some settings on the container app that will affect the extension. You can share data between extension and container app through NSUserDefaults.

We will see how to do this by enabling the user to select a location to view its weather information. The starter project contains a table view controller with a set number of locations the user can choose from. The location variety is hardcoded – this isn’t the best way to store data, but for this demo, it will do.

To get started, change your scheme with Product > Scheme > Weather. In the storyboard file, select the Main View of the View Controller Scene and select Editor > Embed In > Navigation Controller. This will place a navigation bar at the top of the view. Drag a Bar Button Item from the object library and place it on the right side of the navigation bar. Set its title to Edit. Control-Drag from this Bar Button Item to the table view controller and select the ‘show’ segue. Run the application and on tapping on the Edit button, you will see a table view with a list of locations. The My Location cell is selected by default. Selecting any other cell will move the check mark to it. We will save this data to NSUserDefaults so that the widget shows data of the selected location

Select Location

To enable reading from the same set of NSUserDefaults, select your main app target and choose the Capabilities tab. Switch on App Groups (you will require a developer account for this).

Create a new container and give it a unique name. According to the help, it must start with “group.”. I set the name to ‘group.com.appcoda.weather’.

Select the Weather Widget target and repeat the above process of switching on App Groups. Don’t create a new container for it though. Use the one you had created for the Weather target.

Open LocationTableViewController.swift and add the following property to the class.

1
    var defaults: NSUserDefaults = NSUserDefaults(suiteName: "group.com.appcoda.weather")

This will be used to read and write to NSUserDefaults. You must use the name of your group you created earlier as the suite name.

Modify the refresh() function as follows. Here we check to see if the user has set another location other than My Location as the location to track weather data. If they had done so, we set the value of selectedLocation to this new location.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    func refresh() {
        // hasSetLocation is set in NSUserDefaults to track whether the user has set any other location apart from My Location
        var hasSetOtherLocation: Bool? = defaults.boolForKey("hasSetLocation")
        if let hasSetLoc = hasSetOtherLocation {
            if (hasSetLoc == true) {
                selectedLocation = Location.fromRaw(defaults.integerForKey("location"))!
            }
        }

        for i in 0..<Location.NumLocationTypes.toRaw() {
            var cell:UITableViewCell = tableView(self.tableView, cellForRowAtIndexPath: NSIndexPath(forRow: i, inSection: 0))
            cell.accessoryType = selectedLocation.toRaw() == i ? UITableViewCellAccessoryType.Checkmark : UITableViewCellAccessoryType.None
        }
    }

Modify the tableView(tableView: didSelectRowAtIndexPath:) function as shown. We check for the selected location and if it isn’t My Location, then we save the location data to NSUserDefaults. These will be accessed by the container app and widget.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        selectedLocation = Location.fromRaw(indexPath.row)!

        // If other location is selected then set values that will be used in the API call and to populate the view, otherwise the default Cupertino values will be used
        if (selectedLocation != .MyLocation) {
            defaults.setInteger(indexPath.row, forKey: "location")
            defaults.setBool(true, forKey: "hasSetLocation")

            let locationData: (String, String) = getLocationData(selectedLocation)
            let locationDictionary = ["name": locationData.0, "latLong": locationData.1]

            defaults.setObject(locationDictionary, forKey: "locationData")
        } else {
            defaults.setBool(false, forKey: "hasSetLocation")
        }


        self.refresh()
    }

Below is the function that gets information on the selected location. As I mentioned before, I hardcoded the data for the demo. The function returns a Tuple that holds the location’s coordinates and name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    func getLocationData(location: Location) -> (String, String) {

        switch location {
        case .MicrosoftHQ:
            return ("Redmond, WA", "51.461231,-0.9259415")
        case .FacebookHQ:
            return ("Menlo Park, CA", "37.484765,-122.148549")
        case .GoogleHQ:
            return ("Mountain View, CA", "37.422,-122.084058")
        default:
            // This will never be reached, but switch blocks have to be exhaustive
            return ("", "")
        }
    }

In ViewController.swift, add the following property to the class.

1
    var defaults: NSUserDefaults = NSUserDefaults(suiteName: "group.com.appcoda.weather")

Add the following function to the file. This will be called before the call to the API, to check if a different location than My Location was selected and set the latLong property with the coordinates of that location. It also updates the text for the locationLabel with the name of the location.

1
2
3
4
5
6
7
8
9
10
11
12
13
    func checkForSetLocation() {
        // hasSetLocation is set in NSUserDefaults to track whether the user has set any other location apart from My Location
        var hasSetOtherLocation: Bool? = defaults.boolForKey("hasSetLocation")
        if let hasSetLoc = hasSetOtherLocation {
            if (hasSetLoc == true) {
                let locationDict: NSDictionary? = defaults.objectForKey("locationData") as? NSDictionary
                if let dictionary = locationDict {
                    locationLabel.text = dictionary["name"] as? String
                    latLong = dictionary["latLong"] as String
                }
            }
        }
    }

Call this method before the call to the API.

1
2
3
4
5
6
7
8
9
10
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        checkForSetLocation()

        getWeatherData(latLong, completion: { (error) -> () in
            if error == nil {
                self.updateData()
            }
        })
    }

In TodayViewController add the following property to the class.

1
    var defaults: NSUserDefaults = NSUserDefaults(suiteName: "group.com.appcoda.weather")

Add the following function to the class. Notice that we are using similar code in the container app’s view controller and the widget’s view controller. If you find yourself repeating code in several places, then it might be best to place the code in a framework.

1
2
3
4
5
6
7
8
9
10
11
12
13
    func checkForSetLocation() {
        // hasSetLocation is set in NSUserDefaults to track whether the user has set any other location apart from My Location
        var hasSetOtherLocation: Bool? = defaults.boolForKey("hasSetLocation")
        if let hasSetLoc = hasSetOtherLocation {
            if (hasSetLoc == true) {
                let locationDict: NSDictionary? = defaults.objectForKey("locationData") as? NSDictionary
                if let dictionary = locationDict {
                    locationLabel.text = dictionary["name"] as? String
                    latLong = dictionary["latLong"] as String
                }
            }
        }
    }

Call the method before the API call in viewDidAppear()

1
2
3
4
5
6
7
8
9
10
11
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        checkForSetLocation()

        getWeatherData(latLong, completion: { (error) -> () in
            if error == nil {
                self.updateData()
            }
        })
    }

Do the same in widgetPerformUpdateWithCompletionHandler()

1
2
3
4
5
6
7
8
9
10
11
12
    func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
        checkForSetLocation()

        getWeatherData(latLong, completion: { (error) -> () in
            if error == nil {
                self.updateData()
                completionHandler(.NewData)
            } else {
                completionHandler(.NoData)
            }
        })
    }

Run the app, change locations and test that the widget does indeed update according to the set data.

What’s Coming Next

In this article we have looked at how to create and configure the Today Extension in your app. In the coming weeks we will look at the other 5 extensions available for iOS, so stay tuned.

For your reference, you can download the complete Xcode project from here.

Source : appcoda[dot]com