Take charge of the iOS/tvOS/watchOS simulators with Control Room

Nice tool by Paul Hudson:

Control Room is a macOS app that lets you control the simulators for iOS, tvOS, and watchOS – their UI appearance, status bar configuration, and more. It wraps Apple’s own simctl command-line tool, so you’ll need Xcode installed.

Some features, such as sending example push notifications or move between light and dark mode, require Xcode 11.4 or later.

Control Room →

Open your React Native’s Xcode project file from the CLI with xed

For a long time I always used the open command to open the Xcode project file contained inside React Native Projects.

bramus in ~/repos/projects/react-native/example
$ open ios/Example.xcodeproj

It wasn’t until I recently started a new React Native project that react-native init afterwards informed me to use xed instead.

The xed tool launches the Xcode application and opens the given documents, or opens a new untitled document, optionally with the contents of standard in.

With xed you don’t need to know the name of the project file. Just pass in the ios folder as an argument and it will handle things for you:

bramus in ~/repos/projects/react-native/example
$ xed ios

💁‍♂️ xed handles both workspaces (.xcworkspace) and regular project files (.xcodeproj) so no worries there 🙂

Furthermore, if you don’t want to remember yet another extra command, you can always add it as a script to your package.json:

{
	…
	"scripts": {
		…
		"xcode": "xed ios"
	},
	…
}
Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Upgrading from React Native 0.53.x to 0.57.4 (and to Xcode 10 and Gradle 4 along with that), a journey

Back in November, after 6 months of working on other projects, I picked up development for the EV-Point Mobile App again. The app is built using React Native and is available on both iOS and Android.

As Xcode got a new major release (from Xcode 9 to Xcode 10) since my last time working on the project I figured I’d trigger a new build on Bitrise, then see which errors would came at me, and continue from thereon. What I didn’t know however is that it would eventually take me 3 days, 17 failed builds, lots of Google Fu, and some guesswork to get everything working again. Here’s an overview of the biggest hurdles that I had to take, along with their solutions. I hope they might help you too.

There’s always a green light at the end of the build-pipeline tunnel …

My initial plan was to first get the current version of the app – still running React Native 0.53.3 – to build again in Xcode 10, and only then upgrade the React Native version to the latest one (0.57.4 at the time of writing). During the course of my journey I eventually flipped that around, as React Native 0.57.x already contained some fixes that I was in need of.

👋 I’m using Bitrise for automatically building all my apps. If you sign up through this referral link, we both get extra build-time: https://app.bitrise.io/users/sign_up?referrer=09c28434a5e3e18d

~

Problem #1: iOS: React Native project won’t build on Xcode 10

Of course my first Bitrise trigger using Xcode 10 would fail (but one can always try, right?). What I didn’t expect is that quite a lot of things were going wrong (REF):

  • Xcode Signing Issues
  • Failures indicating that libfishook.a could not be linked
  • Third party things such as glog that were not installed
  • etc.

As I stumbled from one build error into the other (locally, of course) and tried fixing these one by one, I eventually found that the most easy solution would be to upgrade to the latest RN version (0.57.4 at the time of writing) as it already contained most of the fixes.

Solution #1 (part1): Upgrading to the latest RN version

To upgrade to the latest RN version I used react-native-git-upgrade. I didn’t immediately jump from RN 0.53 to RN 0.57 though, but gradually went through all intermediate releases.

# install react-native-git-upgrade
yarn global add react-native-git-upgrade

# Go to project folder
cd ~/repos/evpoint/mobileapp/

# Upgrade to RN 0.54.4
react-native-git-upgrade 0.54.4

# Upgrade to RN 0.55.4
react-native-git-upgrade 0.55.4

# Upgrade to RN 0.56.1
react-native-git-upgrade 0.56.1

# Upgrade to RN 0.57.4
react-native-git-upgrade 0.57.4

Using react-native-git-upgrade to upgrade RN versions works like a charm, but it might will require you to resolve a few merge conflicts …


Upgrading from one React Native version to the other …

Once I performed all upgrades I tried building my app locally and … yes, great succes!

💁‍♂️ Note that when upgrading from RN 0.53 to 0.57, the Android build system will have migrated from Gradle 3 to Gradle 4. As we’re now focussing on getting iOS to build, we’ll skip this for now and tackle it later (see Problem #4 below)

Solution #1 (part2): Manually installing the third party scripts

UPDATE: This is fixed with the release of React Native 0.57.5 (#277c19c). You can skip this step if you’re running 0.57.5 or newer.

After successfully building locally I pushed my changes online in order for Bitrise to kick into action. Bitrise however failed. Hmmz …

- Build input file cannot be found: `./node_modules/react-native/Libraries/WebSocket/libfishook.a`
- `configure: error: in `(..._/node_modules/react-native/third-party/glog-0.3.4'`
- `✗ Build input file cannot be found: 'node_modules/react-native/third-party/double-conversion-1.1.6/src/strtod.cc'`
- `No member named '__rip' in '__darwin_arm_thread_state64'`

Turns out that there was an issue with third party scripts not getting installed correctly. Locally they were, however.

FYI: Locally it worked fine because – as it turns out – a second build would correctly struggle through it. As Bitrise always starts from scratch (e.g. with a non-existing node_modules folder) it couldn’t make it through.

To fix this I added an extra step in the build-pipeline to Manually install Third Party Scripts + Configure Glog, (REF):

#!/usr/bin/env bash -e

if [ ! -d "node_modules/react-native/third-party" ]; then
  cd node_modules/react-native ; ./scripts/ios-install-third-party.sh ; cd ../../
  cd node_modules/react-native/third-party/glog-0.3.5/ ; ./../../scripts/ios-configure-glog.sh ; cd ../../../../
fi

I’ve added this step right before Bitrise’s Certificate and Profile Installer step.

💡You’ll also need to execute this step locally, especially when starting from scratch. You can safely add this script to your local build-script as it contains a built-in check to not double install the Third Party Scripts in case they are already present.

~

Problem #2: Android: Program type already present: android.support.v4.app.ActionBarDrawerToggle$DelegateProvider

On Android I was still getting build errors though. My first one was something like this:

Error:Program type already present: android.support.v4.app.ActionBarDrawerToggle$DelegateProvider

java.lang.NoClassDefFoundError: Failed resolution of: android/support/v4/animation/AnimatorCompatHelper;
java.lang.NoSuchFieldError: No field notification_template_lines of type

Solution #2: Fixate the SDK version for com.android.support to a specific version

Thankfully someone else had already stumbled onto this problem and provided a fix: fixate the SDK version for com.android.support to version 24.2.1. You can do this by adjusting your app/android/build.gradle file:

android {

    …

    // Fix for "Program type already present: android.support.v4.app.ActionBarDrawerToggle$DelegateProvider"
    configurations.all {
        resolutionStrategy.eachDependency { DependencyResolveDetails details ->
            def requested = details.requested
            if (requested.group == 'com.android.support') {
                if (!requested.name.startsWith("multidex")
                        && !requested.name.startsWith("exifinterface")) {
                    details.useVersion '24.2.1'
                }
            }
        }
    }

}

~

Problem #3: Android: Unable to find a matching configuration in project

When running ./gradlew assembleDebug I could successfully build for Android. Running ./gradlew assembleRelease however, yielded this error:

Unable to find a matching configuration of project :react-native-devsettings-android:
  - Configuration 'debugApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'preview' and found incompatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debug' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
      - Required org.gradle.usage 'java-api' and found compatible value 'java-api'.
  - Configuration 'debugRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'preview' and found incompatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debug' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
      - Required org.gradle.usage 'java-api' and found incompatible value 'java-runtime'.
  - Configuration 'releaseApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'preview' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'release' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
      - Required org.gradle.usage 'java-api' and found compatible value 'java-api'.
  - Configuration 'releaseRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'preview' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'release' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
      - Required org.gradle.usage 'java-api' and found incompatible value 'java-runtime'.
	at org.gradle.internal.component.model.AbstractDependencyMetadata.selectConfigurationUsingAttributeMatching(AbstractDependencyMetadata.java:46)
	at org.gradle.internal.component.model.LocalComponentDependencyMetadata.selectConfigurations(LocalComponentDependencyMetadata.java:90)
	at org.gradle.internal.component.local.model.DslOriginDependencyMetadataWrapper.selectConfigurations(DslOriginDependencyMetadataWrapper.java:60)
	at org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.EdgeState.calculateTargetConfigurations(EdgeState.java:143)
	... 89 more

Solution #3: Use matchingFallbacks

The fix was to adjust the defined buildType of debug so that it falls back to the release‘s buildType values in case one is missing. This can be done by using the matchingFallbacks instruction inside your app/android/build.gradle file (REF, REF):

android {

    …
    buildTypes {
        release {
            …
        }
        preview {
            …
            matchingFallbacks = ['release', 'debug']
        }
    }

}

~

Problem #4 Android: Execution failed for task ':react-native-config:verifyReleaseResources'

The final Android problem I encountered was that dependencies would not properly compile anymore with the change to Gradle 4. Packaged like react-native-config and react-native-devsettings-android would fail on their verifyReleaseResources step.

Solution #4: Upgrade packages and/or force subprojects to use same compileSdk/BuildTools as main project

Thankfully packages like react-native-config have already landed fixes for this issue. Merely upgrading your affected dependencies using yarn will fix it.

If you’re using packages do not yet have fixes – like react-native-devsettings-android (ISSUE) – you can thankfully force them to use the correct compileSdk/BuildTools version from within your own android/build.gradle file (REF, REF):

subprojects {
    …

    // Force subprojects to use same compileSdk/BuildTools as main project
    afterEvaluate {project ->
        if (project.hasProperty("android")) {
            android {
                compileSdkVersion rootProject.ext.compileSdkVersion
                buildToolsVersion rootProject.ext.buildToolsVersion
            }
        }
    }

}

~

Furthermore I have a few classic issues – such as signing certificates that were expired – which I had to tackle. Supposedly the release of RN 0.58 – which I’m looking forward to – should no longer require the adjustment mentioned above.

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Fixing iTunes Connect “ERROR ITMS-90717” with ImageMagick

When recently publishing an app to the App Store (or at least trying to get it published back then), I couldn’t upload the app via the Application Loader. I got back ERROR ITMS-90717

“Invalid App Store Icon. The App Store Icon in the asset catalog in ‘Your.app’ can’t be transparent nor contain an alpha channel.”

You can also get a likewise warning later on in iTunes Connect, when uploading the “App Previews and Screenshots”

To easily fix this, you can use ImageMagick’s convert command to remove the alpha:

convert input.png -alpha off output.png

To convert all .png files in a folder, combine the command above with find:

find . -name "*.png" -exec convert "{}" -alpha off "{}" \;

Now replace the assets in your Xcode project file, build your app again, and re-send it to Apple 🙂

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

XCode 9.0: Fixing a Slow/Unresponsive iOS Simulator

Whilst initially implementing react-native-maps into a project I noticed that the iOS Simulator didn’t quite like it. Whenever the Mapview was visible on the screen, the Simulator was maxing out on something.

Turns out the OpenGLES.framework that comes with XCode 9.0 – and not react-native-maps itself – is the culprit. That version is buggy and makes the iPhone Simulator have issues when rendering hardware accelerated content (3D, Mapkit, etc).

Awaiting the release of XCode 9.1 – which contains a new and fixed version – there’s a workaround which involves using the OpenGLES.framework that came bundled with XCode 9.0 beta 3 (which isn’t affected). Full instructions on how to get an use this version are available as a Gist on GitHub.

⚠️ Installing frameworks/binaries from unfamiliar/untrusted resources always involves some risk. I can only say that I’ve been using the linked version without any issues. Your mileage may vary. Use at your own risk.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

ionic/cordova emulate vs. Xcode 7

After the App Store automatically updated Xcode to version 7 (without me actually wanting this for now, but hey …) running ionic emulate ios -c -l would not do much anymore:

  • The app would launch but get stuck at the loading screen
  • No console.log() calls would be logged

Fixing it manually

To fix this, update the platforms/ios/projectname/projectname-Info.plist file with the following settings:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

Add the snippet above at the end of the file, right before the closing </dict></plist>

Do note that you’ll have to (re)do this every time you run ionic platform add ios (which can happen when working in a team with multiple people)

Fixing it automatically

Alternatively you can use a before_emulate hook to have this change applied automatically before starting the iPhone Simulator.

  1. Put the contents below in ./hooks/before_emulate/010_ios_allowarbitraryloads.sh
  2. Make the script executable (chmod 755 ./hooks/before_emulate/010_ios_allowarbitraryloads.sh)

Every time you (or one of your teammates) runs ionic emulate ios it will check if the change in platforms/ios/projectname/projectname-Info.plist has already been made or not. If not, the settings are added.

#!/bin/bash

# Allow iPhone Simulator to make Arbitrary Loads
# @ref https://www.bram.us/2015/09/29/ionic-emulate-vs-xcode-7/

XCODEVERSION=`xcodebuild -version | grep Xcode | sed 's/Xcode //g'`
XCODEMAINVERSION=`echo $XCODEVERSION | cut -d "." -f 1`
PROJECTNAME=`xmllint --format --xpath "//*[local-name()='widget']/*[local-name()='name'][1]/text()" config.xml`

if [[ "$CORDOVA_PLATFORMS" == "ios" ]]
then

	if [[ "$XCODEMAINVERSION" > 6 ]]
	then

		echo "iPhone Simulator (XCode $XCODEVERSION) is being used. We might need to adjust $PROJECTNAME-Info.plist to allow Arbitrary Loads!"

		PLISTBUDDY="/usr/libexec/PlistBuddy"
		TARGET="platforms/ios/$PROJECTNAME/$PROJECTNAME-Info.plist"
		HASSETTING=`$PLISTBUDDY -c "print :NSAppTransportSecurity:NSAllowsArbitraryLoads" "$TARGET" 2>&1`

		if [[ "$HASSETTING" == "true" ]]
		then

			echo " - NSAllowsArbitraryLoads already enabled. Not adjusting $PROJECTNAME-Info.plist"

		else

			echo " - NSAllowsArbitraryLoads not enabled. Adjusting $PROJECTNAME-Info.plist"

			$PLISTBUDDY -c "Add :NSAppTransportSecurity dict" "$TARGET"
			$PLISTBUDDY -c "Add :NSAppTransportSecurity:NSAllowsArbitraryLoads bool YES" "$TARGET"

		fi

	fi

fi
Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

XCode: Quick Look for UIViews

debugquicklook-2

Xcode 5.0 introduced an interesting new feature: Quick Look for variables. Basically, it lets you inspect variables and their contents in a graphical way. Xcode 5.1 improves the feature even further with the addition of debugQuickLookObject. This method allows you to provide quick look content for any of your own classes. A bit like debugDescription but more advanced.

Using this, Inferis created an implementation which allows one to quick look at UIViews. Usage is really simple:

Just include UIView+DebugQuickLook.m into your project, and all your views automagically gain debugQuickLookObject.

Download the implementation directly, or install using CocoaPods:

pod 'UIView+DebugQuickLook', :git => 'https://github.com/Inferis/UIView-DebugQuickLook.git'

UIView+DebugQuickLook Source (GitHub) →
Quick Look for UIView (Tech background) →

CocoaPods Tech Talk Video

Great tech talk by the folks over at Ray Wenderlich on CocaoPods, the dependency manager for Objective-C. Be sure to click through on the link below as the original article contains a truckload of useful links.

CocoaPods Tech Talk Video →
Source Code used in the talk (.zip) →
CocoaPods →

iPhone Simulator: The simulated application quit

Fresh OS X install, fresh XCode install, and starting the iPhone Simulator (without launching any custom app) quits at start-up and spits out a “The simulated application quit” error dialog.

Turns out the Asepsis feature from TotalFinder (the feature which prevents the creation of .DS_Store files) is the culprit. Disable that feature and all will work fine.