Skip to content →

Category: iOS

PHImageManager requestImageData with Photos iCloud Photo Library

I came across a bug the other day because of photos in the iCloud Photo Library. In my app I was showing a grid of images to be selected. If “iCloud Photo Library” was enabled then user could see the thumbnail and pick the image, but the app failed when it tried to do something with the image. This is because the image wasn’t downloaded yet.

This is what my code looked like.

let manager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.resizeMode = .exact
requestOptions.deliveryMode = .highQualityFormat;

// Request Image
manager.requestImageData(for: asset, options: requestOptions, resultHandler: { (data, str, orientation, info) -> Void in
	// Do somethign with Image Data
 })

I found the property: PHImageRequestOptions.isNetworkAccessAllowed. When enabled, the image is downloaded from iCloud. And you can set a progressHandler to inform the user.

I updated the code to look like:

let manager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.resizeMode = .exact
requestOptions.deliveryMode = .highQualityFormat;
requestOptions.isNetworkAccessAllowed = true;
requestOptions.progressHandler = { (progress, error, stop, info) in
    if(progress == 1.0){
        SVProgressHUD.dismiss()
    } else {
        SVProgressHUD.showProgress(Float(progress), status: "Downloading from iCloud")
    }
}

// Request Image
manager.requestImageData(for: asset, options: requestOptions, resultHandler: { (data, str, orientation, info) -> Void in
	// Do somethign with Image Data
 })

The requestImageData API, returns an info dictionary providing information about the status of the request. Documentation on the key is here:
PHImageManager -Image Result Info Keys

PHImageResultIsInCloudKey A Boolean value indicating whether the photo asset data is stored on the local device or must be downloaded from iCloud.

You could also check this key to see if downloading the image is required.

Comments closed

App Store Review Times – Automation?

Recently Apple shortened their review times from about 7 days to about 1 day.

You can follow the latest review times at appreviewtimes.com
appreviewtimes

How is Apple doing this? It could be new leadership. It could policy changes. It could be more people. While it may be all of these, I think it has something to do Automation.

Each year Apple has been getting better with their testing tools. In iOS 9, they made a big improvement with UI Testing tools. Xcode now allows you to record your UI Tests. Basically click a record button and then navigate your app in the Simulator. It’s a bit more complicated than that, but almost that easy.

What if Apple modified their testing tools, so the App Store Review team could record their review sessions. Once they have a recording for an app, they can run that recording on new submissions. If it passes, they approve the App. If it fails, they give the app a quick look through and fix the UI test.

They could have started recording session months in advance and then all of a sudden turned it on. If an App hadn’t been approved in a while or it was a new app, the first review would take longer. But after that, it would be fast.

I have no idea if this is what they are really doing, just fun speculation. I hope we’ll learn more at WWDC 2016.

Comments closed

Fastlane Frameit Tips

Here are few tips I learned when setting up Fastlane Frameit.

When I first ran fame it, I got this error:

$ frameit white

iconv: /Users/doug/src/my-ios-project/fastlane/screenshots/en-US/title.strings:1:252: incomplete character or shift sequence
[16:37:51]: Could not get title for screenshot ./iPhone6-0.png. Please provide one in your Framefile.json

iconv: /Users/doug/src/my-ios-project/fastlane/screenshots/en-US/title.strings:1:252: incomplete character or shift sequence
[16:37:52]: Could not get title for screenshot ./iPhone6-1.png. Please provide one in your Framefile.json

iconv: /Users/doug/src/my-ios-project/fastlane/screenshots/en-US/title.strings:1:252: incomplete character or shift sequence
[16:37:53]: Could not get title for screenshot ./iPhone6-2.png. Please provide one in your Framefile.json
ç
iconv: /Users/doug/src/my-ios-project/fastlane/screenshots/en-US/title.strings:1:252: incomplete character or shift sequence
[16:37:55]: Could not get title for screenshot ./iPhone6-3.png. Please provide one in your Framefile.json
√
iconv: /Users/doug/src/my-ios-project/fastlane/screenshots/en-US/title.strings:1:252: incomplete character or shift sequence
[16:37:56]: Could not get title for screenshot ./iPhone6-4.png. Please provide one in your Framefile.json

As the documentation clear states: “.strings files MUST be utf-16 encoded (UTF-16 LE with BOM).”

I fixed this by saving the file ast UTF-16 in TextWrangler.
textwrangler_utf-16

Then I ran into a strange error:

$ frameit white
[16:42:10]: `mogrify -gravity Center -pointsize 55 -draw text 0,0 'Text 1' -fill #000000 /var/folders/rf/yz3fgzq17sbgkjcxfq_x6lh40000gn/T/mini_magick20160324-45310-1yizv31.png` failed with error:
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: non-conforming drawing primitive definition `text' @ error/draw.c/DrawImage/3165.

[16:42:12]: `mogrify -gravity Center -pointsize 55 -draw text 0,0 'Text 2' -fill #000000 /var/folders/rf/yz3fgzq17sbgkjcxfq_x6lh40000gn/T/mini_magick20160324-45310-qp00up.png` failed with error:
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: non-conforming drawing primitive definition `text' @ error/draw.c/DrawImage/3165.

[16:42:13]: `mogrify -gravity Center -pointsize 55 -draw text 0,0 'Text 3' -fill #000000 /var/folders/rf/yz3fgzq17sbgkjcxfq_x6lh40000gn/T/mini_magick20160324-45310-eu9yaq.png` failed with error:
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: non-conforming drawing primitive definition `text' @ error/draw.c/DrawImage/3165.

[16:42:15]: `mogrify -gravity Center -pointsize 55 -draw text 0,0 'Text 4' -fill #000000 /var/folders/rf/yz3fgzq17sbgkjcxfq_x6lh40000gn/T/mini_magick20160324-45310-1ie72eo.png` failed with error:
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: non-conforming drawing primitive definition `text' @ error/draw.c/DrawImage/3165.

[16:42:16]: `mogrify -gravity Center -pointsize 55 -draw text 0,0 'Text 5' -fill #000000 /var/folders/rf/yz3fgzq17sbgkjcxfq_x6lh40000gn/T/mini_magick20160324-45310-ji9gyl.png` failed with error:
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: unable to read font `(null)' @ error/annotate.c/RenderFreetype/1153.
mogrify: non-conforming drawing primitive definition `text' @ error/draw.c/DrawImage/3165.

I came across a forum thread that said I needed to install ghostscript. So I installed Ghostscript with Homebrew like:

$ brew install gs
==> Installing dependencies for ghostscript: libtiff, little-cms2
==> Installing ghostscript dependency: libtiff
==> Downloading https://homebrew.bintray.com/bottles/libtiff-4.0.6.el_capitan.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libtiff-4.0.6.el_capitan.bottle.tar.gz
🍺  /usr/local/Cellar/libtiff/4.0.6: 259 files, 3.4M
==> Installing ghostscript dependency: little-cms2
==> Downloading https://homebrew.bintray.com/bottles/little-cms2-2.7.el_capitan.bottle.tar.gz
######################################################################## 100.0%
==> Pouring little-cms2-2.7.el_capitan.bottle.tar.gz
🍺  /usr/local/Cellar/little-cms2/2.7: 16 files, 1M
==> Installing ghostscript
==> Downloading https://homebrew.bintray.com/bottles/ghostscript-9.18.el_capitan.bottle.tar.gz
######################################################################## 100.0%
==> Pouring ghostscript-9.18.el_capitan.bottle.tar.gz
🍺  /usr/local/Cellar/ghostscript/9.18: 709 files, 61M

Then it ran successfully:

$ frameit white
[16:45:21]: Added frame: '/Users/doug/src/my-ios-project/fastlane/screenshots/en-US/iPhone6-0_framed.png'
[16:45:23]: Added frame: '/Users/doug/src/my-ios-project/fastlane/screenshots/en-US/iPhone6-1_framed.png'
[16:45:25]: Added frame: '/Users/doug/src/my-ios-project/fastlane/screenshots/en-US/iPhone6-2_framed.png'
[16:45:28]: Added frame: '/Users/doug/src/my-ios-project/fastlane/screenshots/en-US/iPhone6-3_framed.png'
[16:45:30]: Added frame: '/Users/doug/src/my-ios-project/fastlane/screenshots/en-US/iPhone6-4_framed.png'
Comments closed

Managing where rows can be moved to in UITableView

In an app I was working on, I needed to prevent users from moving rows from section 1 to section 0. After reading the docs, I found Apple has just the API: Reordering Table Rows.

Here’s how I used the API. If the proposed destination section is 0, then return the current indexPath. As a result the user will be unable to draw a row from section 1 to section 0.

func tableView(tableView: UITableView, targetIndexPathForMoveFromRowAtIndexPath sourceIndexPath: NSIndexPath, toProposedIndexPath proposedDestinationIndexPath: NSIndexPath) -> NSIndexPath
{
    
    if proposedDestinationIndexPath.section == 0 {
        return sourceIndexPath
    }
    
    return proposedDestinationIndexPath;
}

My problem was simple, but this API could be used for all kinds of reordering problems.

Comments closed

Xcode 7 crashing when toggling Assistant editor

I had a Xcode project which was crashing whenever I tried to close the Assistant editor.  Here is what I did to fix it.

  1. Close the project in XCode
  2. Delete the following files using the Terminal.  Where “myproject” is the name your project.
rm -rf myproject.xcworkspace/xcuserdata

rm -rf myproject.xcodeproj/xcuserdata

rm -rf myproject.xcodeproj/project.xcworkspace

If you don’t feel comfortable using the Terminal, you can also right click on the file and choose “Show Package Contents” and delete the files.

If you don’t use CocoaPods, you may not have a myproject.xcworkspace

This solution may fix other Xcode crashing issues.

Comments closed

Migrate NSUserDefaults to App Groups – Swift

I needed migrate my NSUserDefaults to using App Groups. I came across this post Migrating to App Groups, which was doing exactly what I wanted in Objective-C. Here’s the version I rewrote in Swift:

func migrateUserDefaultsToAppGroups() {
	
	// User Defaults - Old
	let userDefaults = NSUserDefaults.standardUserDefaults()
	
	// App Groups Default - New
	let groupDefaults = NSUserDefaults(suiteName: "group.myGroup")
	
	// Key to track if we migrated
	let didMigrateToAppGroups = "DidMigrateToAppGroups"
	
	if let groupDefaults = groupDefaults {
		if !groupDefaults.boolForKey(didMigrateToAppGroups) {
			for key in userDefaults.dictionaryRepresentation().keys {
				groupDefaults.setObject(userDefaults.dictionaryRepresentation()[key], forKey: key)
			}
			groupDefaults.setBool(true, forKey: didMigrateToAppGroups)
			groupDefaults.synchronize()
			print("Successfully migrated defaults")
		} else {
			print("No need to migrate defaults")
		}
	} else {
		print("Unable to create NSUserDefaults with given app group")
	}
	
}

Gist

Comments closed

Cocoapods – Generated duplicate UUIDs

I recently added another target to my Podfile to support tvOS. Here is an example of what the Podfile looks like:

source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!

target 'AppiOS' do
        platform :ios, '9.0'
        pod 'AFNetworking', '3.0.0-beta.1'
end

target 'AppTV' do
    platform :tvos, '9.0'
    pod 'AFNetworking', '3.0.0-beta.1'
end

I got this really long error complaining of duplicate UUIDS. Here is a shortened version:

[!] [Xcodeproj] Generated duplicate UUIDs:

PBXFileReference -- 
.... /Products/AFNetworking.framework

From what I understand, this deterministic UUIDs is a harmless warning and can be disabled by running the following command in the terminal. Then run pod install again.

$ export COCOAPODS_DISABLE_DETERMINISTIC_UUIDS=YES
Comments closed

Downgrading Cocoapods

I recently had some issues with a pre-release version of Cocoapods. To fix it, the suggestion was to downgrade Cocoapods to a previous version. It wasn’t obvious how to do that, but this is what I learned.

First you can figure out which version of Cocoapods you are on with the command:

pod --version

You can also see all the version of Cocoapods you have installed with this command:

sudo gem list cocoapods

Next uninstall Cocoapods. If you have multiple version, you will have the choice of uninstalling all or a specific version.

sudo gem uninstall cocoapods

Finally you can install the specific version with this command:

sudo gem install cocoapods -v 0.39.0.beta.3
Comments closed

WWDC 2015 – Wish list

Last year I made a WWDC 2014 Wish List. I got at least one thing I wanted. But Apple also gave me a whole bunch of things that I didn’t know I wanted.

Here is an updated list for WWDC 2015.

  • Bug Fixes – I’d be very happy if there were less new features and more bug fixes with the current products.  For example please fix the networking issues.  And I’m still having issues with Gmail in OS X Mail.
  • 3rd Party Complications on Apple Watch – Complications are awesome.  I’d like to be able to expose a complication from my app.
  • Complications on iOS Home Screen – Having complications on the Apple Watch, makes me want them on the iOS home screen.
  • Siri Extensibility – I’d like to see Apple open up Siri to third party apps.  For example if I have RunKeeper on my iPhone, I’d like to be able to tell Siri “Start my run”, “End my run”.  When Apple announced HomeKit in 2014, it allowed custom names for things in your house.  A step forward, but I’d like to see more.
  • AppleTV SDK – I’d love to be able to write apps for the AppleTV.
  • Sandboxing – I’m glad that Apple has sandboxing in the Mac App Store.  But it’s too restrictive.  Apps like Coda should be able to run.  Apple should start by sandboxing Xcode.
  • Filesystem – Mac OS X and iOS are due for a new filesystem. ZFS was rumored for awhile but fell through.
  • Extensions – I love extensions, but there are a few problems.  The difference between a share and action extension is confusing.  It’s hard to tell when new extension are available.   There should be more trigger points for extensions.
  • Default Applications on iOS – On OS X you can set a default application for things like mail and web browser.  I wish you could do the same on iOS.
  • Multiple profiles on iOS – It would be nice if iOS supported multiple users, so a family could share an iPad.
Comments closed

iOS File Size Formatter

iOS has a handy formatter for file sizes. Here is an example of formatting at long into a readable string:

// Get file size
unsigned long long size = [[NSFileManager defaultManager] 
  attributesOfItemAtPath:path error:nil].fileSize;
NSLog(@"size: %@", @(size));

// Format file size to a readable string
NSString * fileSize = [NSByteCountFormatter stringFromByteCount:size
   countStyle:NSByteCountFormatterCountStyleFile];
NSLog(@"fileSize: %@", fileSize);

Output:

size: 65346
fileSize: 65 KB

Comments closed