How to Write iOS Apps Without Xcode

Did you know you have iOS IDE options?

Xcode is an IDE (integrated development environment) from Apple with a long history. It’s native for iOS development, supports both Objective-C and Swift, includes XIB and Storyboard editors, compilers, debuggers and everything necessary for development.

Why would anyone want to develop iOS apps without Xcode? There are several possible reasons:

  • Xcode consumes lots of memory and works slowly on many Macs.
  • Xcode has many bugs. Apple fixes them but adds new bugs with new features.
  • Xcode works only in macOS, which makes it uncomfortable for developers coming from other platforms.

Unfortunately, Apple did everything possible to prevent developers from using other platforms; iOS simulator is part of the Xcode app package, there are no alternatives for editing Storyboards, and it’s very complicated to make a full signed build without Xcode.

So, just to be clear, there are things which are impossible, so don’t expect to overcome these restrictions:

  • Native iOS apps can be developed only on Mac. You can write code even in Windows or Linux, but you can’t build and sign it there.
  • Non-native platforms, like Flutter or React Native, won’t make iOS builds without Mac either.
  • Storyboards can be edited only in Xcode, so development without Xcode means development without Storyboards.
  • Alternative IDEs for iOS development require Xcode. You don’t need to run it, but you should have it installed.
  • Signing and uploading apps to the App Store (or Test Flight) can be done from the command line (see below), but you need to have Xcode installed.

I don’t say that Xcode is absolutely not usable. Just the opposite; it’s the easiest way to make iOS apps. But if you face one of the difficulties mentioned above or just want to try something new, this story is for you.

Native or Non-Native?

Apps for iOS can be native or non-native. Actually, it’s not black or white; there are many options between.

Absolutely native apps are written in Objective-C or Swift. Some parts of them can be written in C or C++. The user interface is usually presented as Storyboard or XIB files.

Absolutely non-native apps are just websites wrapped in WKWebView (UIWebView). Now Apple rejects such apps, but in the past, they were rather common. Obviously, such apps don’t need much interaction with Xcode, and creating one UIViewController with one WebView is hardly a problem, even if you rent Mac online and don’t have any experience with Xcode.

All other options are in the middle. They use native components, but the code is usually written in a non-native language. For example, Flutter uses Dart, React Native — JavaScript, Unity — C#. All these frameworks use their own development environments, but they all export an Xcode project. You need to build it to publish release using… Xcode. Usually, it’s not a problem. The documentation contains step-by-step instructions for people who have never seen Xcode before.

Writing Flutter apps in Android Studio is not really a topic of this story. It’s a default option, so we won’t waste time on it. We’ll talk about developing, testing, and distribution of native iOS apps without Xcode.

Which Problems Do We Have?

Let’s see why it’s not so easy to write iOS apps without Xcode.

  • Creating a project— Xcode saves your work in projects and workspaces. The workspace is just a set of projects related to each other. Both of them are folders with text files inside. The format of these files is proprietary, but they’re big and have a lot of generated ids, so they’re not supposed to be edited by humans.
  • User interface— Storyboard is another proprietary format. You can’t edit it outside Xcode.
  • Building and testing— There are command-line compilersswiftc,gccandllvm, but how to make a runnable iOS app?

iOS Project

Any app that is more complicated than “Hello world” has more than one file. In the case of iOS, even “Hello world” has more than one file.

iOS apps which are runnable in Simulator have a minimum of two components:

  • Executable binary
  • Info.plistfile

Full apps runnable on a real iOS device have more components, but we’ll discuss that later.

To create a binary, you need a minimum of two items:

  • Source code
  • Build script

iOS apps can be built from command lines; for example, using make. We’ll look at how to do it later.

A more comfortable way to build iOS apps is to use an Xcode project. I found only one app (besides Xcode) which can create such projects — AppCode. It’s one of JetBrain’s apps, similar to IDEA and Android Studio, but for Apple-specific development, macOS and iOS. It runs only on macOS and it’s paid (from $8.90/month or $89/year in April 2020).

Warning! AppCode can’t edit Storyboards. It opens Xcode when you try. And it requires Xcode to be installed; otherwise, it won’t build the app.

Structure of an Xcode project

As I mentioned earlier, Xcode projects are not supposed to be edited manually.

xcodeproj is a folder containing one file and several folders inside.

The file is project.pbxproj. It contains all the information about the project, and it’s the most critical file.

Warning! If you’re going to edit an existing project, make a backup copy.

project.pbxproj is a plist-like file. This format came from the NeXTSTEP platform. Modern plist is XML, but project.pbxproj is more similar to JSON (though it’s not JSON).

project.pbxproj is mostly a set of objects. Each object has a unique identifier (a 96-bit number or a string of 24 hexadecimal characters). Objects can be source files, linked frameworks, build phases, etc. Object can be a group, containing other objects. Objects can have attributes and type. One of the objects is a root object. It has type PBXProject.

If, after all the warnings, you decided to edit project.pbxproj file, you can use Visual Studio Code with the Syntax Xcode Project Data extension. Here you can find a detailed description of the file format.

Besides project.pbxproj, Xcode projects contain several folders. All of them are options.

project.xcworkspace is a workspace containing only one project. It’s created automatically when you open a project file in Xcode and contains build schemas, information about breakpoints, and other data, which is not a part of the project.

xcuserdata is a folder containing individual data of different users. If you’re the only developer, there will be only one folder inside. This folder is optional and can be excluded from Git and other repositories.

xcshareddata is a folder with data shared between users; for example, schemes.

If you don’t use Xcode, you’ll only need project.pbxproj.

Build iOS apps from console with make

Honestly, I think it’s too much of a headache to make a project this way. It’s easier to work out your disagreements with Xcode (you need to install it to sign the app anyway) than to do so many steps manually. But the theoretical possibility is interesting, so let’s dig deeper.

First of all, let’s get an SDK path:

xcrun --sdk iphonesimulator --show-sdk-path

The result will be something like this:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.4.sdk

Inside Makefile, you can paste the output of xcrun or use the command as part of a script:

SDKROOT:=$(shell xcrun --sdk iphonesimulator --show-sdk-path)

Let’s make an app target:

app: main.mclang -isysroot $(SDKROOT) -framework Foundation -framework UIKit -o MakeTest.app/[email protected] $^

clang is a standard compiler from the Xcode package.

We add two frameworks:

  • Foundation
  • UIKit

Output file is MakeTest.app/app.

app. main.m in this experiment.

[email protected]is an automatic variable, which evaluates to a name of a target. In our case, it’s $^is another automatic variable. It evaluates to a full list of dependencies —

And clean target:

.PHONY: cleanclean:rm MakeTest.app/app

Finally, let’s declare app a main target:

default: app

Here’s a full Makefile:

In case you’ve never used make to build projects:

  • make appbuilds anapptarget.
  • make cleancleans up (removesappfile).

As we declared a default target, we can just use the make command to build the project.

The next step is creating an app folder. Yes, iOS app is a folder.

mkdir MakeTest.app

Inside MakeTest.app there should be two files:

  • appis a binary file, which we build with ourmakecommand.
  • Info.plist(starting with a capitalI) is a property list of a project. The iOS device or simulator needs to know which binary to run, which version it has, and other data.

Here’s our Info.plist. You can change some fields if you run your own test:

The last file is main.m. In a traditional iOS project, there are three different files:

  • main.m
  • AppDelegate.h
  • AppDelegate.m

It’s just a matter of organization, not a strict rule. As all components are very small, let’s put them together.

Here’s the main function of the app. The only purpose of it is to enter the main loop. We also pass a name of application delegate — AppDelegate:

int main(int argc, char *argv[]) {@autoreleasepool {return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));}}

If you create a project with Xcode or AppCode, this function will be generated automatically.

Don’t forget to include UIKit to all Objective-C files of your project:

#import <UIKit/UIKit.h>

If you came from Swift, you may not know that in Objective-C (as well as in C++) each class must be declared and defined.

Class declaration:

@interface AppDelegate : UIResponder <UIApplicationDelegate>@property (strong, nonatomic) UIWindow *window;@end

Class definition:

@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(id)options {CGRect mainScreenBounds = [[UIScreen mainScreen] bounds];self.window = [[UIWindow alloc] initWithFrame:mainScreenBounds];UIViewController *viewController = [[UIViewController alloc] init];viewController.view.backgroundColor = [UIColor whiteColor];viewController.view.frame = mainScreenBounds; UILabel *label = [[UILabel alloc] initWithFrame:mainScreenBounds];[label setText:@"Wow! I was built with clang and make!"];[viewController.view addSubview: label]; self.window.rootViewController = viewController; [self.window makeKeyAndVisible];return YES;}@end

It creates a window, view controller, and a label in the center of it. I won’t go into detail, as it has nothing to do with coding outside Xcode. It’s just iOS programming.

The app delegate, view controllers, and other components can be placed into separate files. Even more, it’s a recommended practice. Technically, a Makefile-based project can use the same structure as a normal Xcode project.

Here’s how it looks:

Full source code of main.m:

So, what’s next?

  • Entitlement file — If you need to add any capabilities to your iOS project, you’ll need an entitlement file. It’s a
  • plistfile with key-value pair describing which resources or services of the iOS device or Apple user account it uses. You can find more details in the Apple documentation.
  • You can see that the app in our example doesn’t take full-screen. It can be fixed with adding Launch image or proper size, or Launch screen Storyboard.
  • Make two targets: for iOS devices and for simulators. iOS builds require signing, which we’ll talk about later.
  • App icon can be added as a part of the assets folder
  • Assets.xcassets, or as a set of PNG files (with references inInfo.plist).Assets.xcassetsis a folder with app assets. We’ll come back to it later.

If you have any experience in building a commercial iOS app this way, please leave a comment. I will be happy to include more information based on your experience.

Build a User Interface

I found four ways to build the user interface without Xcode and without Storyboards:

  • SwiftUI— The new UI builder from Apple can be edited in any text editor. For example, Visual Studio Code (VS Code) has a plugin to write Swift code. And it’s free. The disadvantage is that you need iOS 13 to run an app with SwiftUI. In several years, it will stop being a disadvantage, but now it’s too early to drop support of iOS 12 and earlier versions.
  • Creating components out of code— Any component can be created from Objective-C or Swift code without any UI designers. It’s long and uncomfortable, but very universal. Code can be written anywhere, and your app will run even on the first iPhone (if you manage to build it for such an old device).
  • External tools— There are some tools that allow conversion of a design (for example, made in Sketch) into native iOS code (in Objective-C or Swift). An example of such tools is Supernova. It’s paid, like any other tool with similar functionality.
  • External libraries— These libraries allow you to write short code to build a native UI. They’re free, but you need to learn to use them. It’s almost like learning a new programming language. Example: LayoutKit from LinkedIn.

There’s no perfect solution. Each of them has advantages and disadvantages, so it’s up to you what to use. I think in time, SwiftUI will be the most popular way to build UI for iOS. But if you need to publish your app faster, you’d better use another way. In any case, it’s your choice.

SwiftUI

SwiftUI is a new framework from Apple that is supposed to replace UIKit and Storyboards.

Pros:

  • It’s native for iOS. Apple will support it, promote it, and motivate developers to use it.
  • SwiftUI code can be written in any text editor.
  • SwiftUI can be mixed with UIKit in the same layout.

Cons:

  • SwiftUI can target only iOS 13 or later versions. It won’t run on iOS 12.
  • Layout preview and simulation works only in Xcode. There are no plugins for AppCode, VS Code or any other code editors.

Let’s see how SwiftUI works:

This example is borrowed from the official Apple Tutorial.

Here you can find another detailed SwiftUI tutorial.

Creating UIKit components from code

This is an absolutely universal way to create layouts. You can create components, add constraints, do basically anything. But each component will require full initialization. And working with constraints is a nightmare when you can’t see what you’re doing. Previewing layout is possible only on Simulator or an iOS device, and each correction requires the app to restart.

This is how it works:

This code should be inside theUIViewController subclass.

Each UI component should be created this way. they can be nested, like in Storyboard. All features of Storyboards and all components of UIKit are available via code, including tables, collections, etc.

External tools

Designers use Sketch, Adobe XD, Zeplin, or other tools for iOS layouts. Wouldn’t it be great if you could just export them into an iOS app? Definitely. And it’s possible, but not free.

I found several tools allowing to do so:

  • Supernova — from $20/month
  • Code Generator Plugin for Sketch — €47/year; plus Sketch, $99
  • Zeplin has Swift and Objective-C extensions exporting styles to iOS code. Free for one project, then from $17/month

Please note that prices are not fixed and can change at any moment.

The result of these tools/plugins is native iOS code, Swift, or Objective-C.

Let’s say we have a design made in Sketch. For this example, I downloaded the file Waste Management App Sketch Resource.

Sketch app opened it with a warning (version mismatch), but it’s not a problem. Next step — Supernova.

Supernova Studio has a feature to import Sketch or Adobe XD file.

The import worked, but there are some little glitches. For example, the back arrow (see screenshot). Also, on preview, the text overlaps the button, but it should be solved with constraints or UIScrollView.

The problem with the arrow can be fixed by using raster pictures or manually. Let’s see how it’s exported to iOS code. FileExport to iOS. Attempting to export showed me a list of missing fonts.

Ok, let’s ignore it for now. The system font is fine.

I chose export only for the iPhone and only for portrait orientation, so as not to go deep into details.

The language can only be Swift. As for me, it makes sense to choose the latest. Currently, it’s Swift 5. UI must be Code. Other options are Storyboard and XIB, but they can be opened only by Xcode, so it’s not an option for us.

The export process took less than a minute. It generated an Xcode project with Podfile. Podfile is almost empty; it has only a skeleton but no dependencies.

Anyway, let’s install Pods to generate a workspace.

Each screen is a separate class, a subclass of UIViewController, located in separate folders. Fonts, images, and other assets are also exported. And no Storyboards. It means we can open it with AppCode.

Here’s an example of a Supernova Studio export, file HomeActivityNavViewController:

This file contains “copyright © 2018 Supernova.” It’s an interesting detail because I made this export in March 2020.

The project builds and runs successfully. Here’s how it looks:

Obviously, the buttons don’t work, because I never added logic in Supernova Studio. But it’s possible. The button doesn’t have a green background. In my opinion, these issues are not significant and can be easily fixed in the code after export. Logic can be added in both Supernova Studio and IDE (Xcode or App Code).

External libraries

There are different layout frameworks. They appear and disappear, so if you choose this method, just find the framework which has recent updates (within last year) and which supports Swift 5 and has the features you need.

Let’s review one of the most popular — LayoutKit from LinkedIn.

The example is borrowed from the official LayoutKit website.

It creates standard UIKit components (like all frameworks) the same way as we did earlier, but it has two additional features:

  • The code is shorter and more clear.
  • It adds easy ways to auto-size components.

Another framework I’d like to review is SnapKit. Sometimes even in a Storyboard-based project, you need to create components out of code. It’s rather complicated to add a component inside a layout with constraints while keeping the component size up to date.

SnapKit helps to add constraints using code.

The example is borrowed from the official SnapKit website.

Summary

Whatever you choose, please don’t forget that your layouts should meet the Apple Human Interface Guidelines.

Assets

Most iOS apps have a special folder with assets. It’s usually one folder named Assets.xcassets. If you create a new Xcode project, this folder will be created automatically.

It contains different types of assets: images, colors, data, AR resources, sticker packs, and others. The most popular asset is image sets. Image sets, besides images, have important metadata. For example, image sets can have different images for different screen sizes, device types, resolutions. Images from a set can be rendered as an original image or as a template. The colors of template images can be changed with the tint attribute.

The asset folder has json files to store metadata. The name for all these files is the same — Contents.json. Root Contents.json usually looks like this:

{"info" : {"version" : 1,"author" : "xcode"}}

I don’t see any reason to change this file. Files inside inner folders are more interesting. This is an example of aContents.json file inside AppIcon.appiconset:

{"images" : [{"size" : "20x20","idiom" : "iphone","filename" : "[email protected]","scale" : "2x"}, ...{"size" : "1024x1024","idiom" : "ios-marketing","filename" : "[email protected]","scale" : "1x"}],"info" : {"version" : 1,"author" : "xcode"}}

The “info” section is the same. “images” contains an array of images of icons for different devices and resolutions. Attributes:

  • size — the icon size in points
  • idiom — the device type
  • filename — the name of a file. The file should be in the same folder.
  • scale —the device scale (2x or 3x for devices with retina screen, 1x for devices with low resolution).

The icon set’s Contents.json file has a similar structure. For example:

{"images" : [{"idiom" : "universal","filename" : "button_back.png","scale" : "1x"},{"idiom" : "universal","filename" : "[email protected]","scale" : "2x"},{"idiom" : "universal","filename" : "[email protected]","scale" : "3x"}],"info" : {"version" : 1,"author" : "xcode"}}

I’ll leave other asset types beyond the scope. They’re more advanced and not used in all projects.

Asset folders can be edited in Xcode and AppCode. Also, if you download image sets from some web sites, for example, material icons, you’ll get imageset folders with Contents.json inside. In all other cases, you need to create json files manually.

Working With iOS Simulator

Let’s say you built an app, and you got a binary (actually, it’s a folder, but it has a binary inside). How do you run it on the iOS simulator?

First of all, you need to run it.

AppCode can run it for you; it’s very similar to Xcode in this aspect. It supports code debugging and even running on a physical device.

If you get a folder with the iOS app, you need to follow three steps:

  • Run iOS simulator manually.
  • Drag the app folder to running iOS simulator.
  • Find it on the virtual screen and run it.

To run iOS simulator, open the Terminal app and run this command:

open -a Simulator.app

To choose a device model and iOS version, use menu FileOpen Device or HardwareDevice (in older versions).

Debugging

iOS simulator has one secret. All apps running in it are actually x86_64 apps running on your Mac (in some kind of sandbox). It means that you can debug iOS app locally, the same way as any macOS app.

First of all, run lldb:

lldb

Second, attach to a process:

(lldb)process attach --pid 12345

or:

(lldb)process attach --name MyApp

You can find your app in the Activity Monitor app, which is installed on all Macs. There you can find your app’s pid (process id).

If you don’t know how to use lldb, here’s documentation.

App Signing

You finished your app, tested it in the Simulator, and found and fixed bugs. Now it’s time to test it on a real device and go live.

First, there are several things you need to know:

  • iOS app, the same as macOS app is a folder which uses the extension
  • app.
  • To distribute an iOS app, you need to create an archive called
  • ipa. It’s a zip archive with theappfolder inside aPayloadfolder.
  • You need to sign an iOS app before making the
  • ipaarchive.
  • To sign your iOS app, you need to have an Apple developer account. You can create one on Apple developer portal.
  • Only signed apps can run on a physical iOS device, even if it’s your own iPhone.
  • You should have a certificate and a provisioning profile to sign an app. Xcode creates them automatically, but if you sign the app manually, you’ll have to request them yourself.
  • The information about your app (bundle id, entitlements) in your
  • plistfiles and your provisioning profile should match.

This sounds complicated, and it actually is. Let’s review this process step-by-step because without app signing, all previous effort doesn’t make much sense.

For this example, I’ll use an app built earlier in “Build iOS apps from console with make” section, above. It’s called MakeTest.

Your app should look like what you see in the picture above. The white sign means that you can’t run it in macOS.

Step 0. Compile an app

Before we start, you should have an app for armv7 and arm64. If you build it from Xcode or another platform, just choose a proper target. If you use the example we built previously, make some changes in Makefile:

  • Replace:
SDKROOT:=$(shell xcrun --sdk iphonesimulator --show-sdk-path)

with:

SDKROOT:=$(shell xcrun --sdk iphoneos --show-sdk-path)

2. Change build command from:

clang -isysroot $(SDKROOT) -framework Foundation -framework UIKit -o MakeTest.app/[email protected] $^

to:

clang -isysroot $(SDKROOT) -arch armv7 -arch arm64 -framework Foundation -framework UIKit -o MakeTest.app/[email protected] $^

This will generate a so-called “fat” binary with two architectures. It’s exactly what we need.

If you get any errors on this stage, you probably have problems with Xcode. Install it, if you didn’t do that yet. Run it, and Xcode installs a command-line tool after each update.

Step 1. Create a certificate

To create a certificate, open this link: https://developer.apple.com/account/resources/certificates/list. If you developed your iOS apps with Xcode on the same Mac, you can skip this step. If not, click the “+” button and add a new “Apple Distribution” certificate. I won’t go into details here. The process is rather easy, and you can find many tutorials and manuals. Download and install it when it’s ready.

Check your certificate in the Keychain Access app. You should use the full certificate name on the next step.

Step 2. Codesign

Open the Terminal app, and change the current directory to your working folder (containing MakeTest.app).

cd full_path

Type:

codesign -s "Apple Distribution: Your Account Name (TEAM_ID)" MakeTest.app

In several seconds, you’ll see the folder _CodeSignature inside MakeTest.app. It’s a digital signature. And it’s the reason not to share your certificate with people you don’t trust. This signature proves that you developed the app. If someone steals your certificate and publishes a signed app doing something illegal, your account can be blocked.

Step 3. Create a provisioning profile

Open Apple Provisioning Portal: https://developer.apple.com/account/resources/profiles/list.

Click the “+” button. You can generate several types of profiles:

  • iOS App Development — can be used only for your own devices
  • AdHoc — can be distributed to a limited amount of devices, included into the profile
  • App Store — can be uploaded to the App Store

In this example, let’s generate an ad hoc profile and create a link for the app install.

In the next step, you need to choose your app ID. Probably your id is not on the list yet.

If so, go to the identifier list and add a new App ID: https://developer.apple.com/account/resources/identifiers/list. Please remember that your app ID (Bundle ID) should match the ID in your Info.plist. You can select the capabilities you use in the app. For this test, you don’t need to choose anything. Some of them can be pre-selected; leave them without changes.

Go back to profile generation and choose your app ID. On the next screen, you need to select a certificate. It should be exactly the same certificate as you used in the previous step. If you have several certificates with different dates, check the certificate in your Keychain. Compare the expiration date. It should be the same or with a one-day difference.

In the next step, you need to choose devices. If you used Xcode before, your device is probably registered. If not, register it manually here:

To register a new device, you need to enter it’s UDID (Unique Device ID). There are two ways to get it:

  • You can connect your device with a wire to your PC/Mac and find UDID in iTunes. In macOS Catalina, there’s no iTunes, but you can find your device in Finder. Under the device name, you’ll see some additional information. Click it once or several times until you see UDID. Please note that serial number and UDID are different.
  • You can use one of the services like https://get.udid.io or similar. Open it on your iOS device and follow the instructions.

When you have your device registered, go back to profile generation, check one or more devices, and generate the profile. In the last step, you need to enter a profile name. Usually, I use the app name plus “AdHoc.” Download the profile when it’s ready.

Step 4. Provisioning

To add a provisioning profile to your app, simply copy it to the app folder and rename it to embedded.mobileprovision.

Then sign it again:

codesign -f -s "Apple Distribution: Your Account Name (TEAM_ID)" MakeTest.app

If you signed it before, add the -f flag (force).

Step 5. Entitlements

First, let’s generate the entitlement file:

security cms -D -i Payload/MakeTest.app/embedded.mobileprovision

This will output to console a big structure including entitlements. Save this structure in a file ending with .entitlements. Follow the structure below:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>application-identifier</key><string>TEAM_ID.com.test.make</string> <key>keychain-access-groups</key><array><string>TEAM_ID.*</string></array><key>get-task-allow</key><false/> <key>com.apple.developer.team-identifier</key><string>TEAM_ID</string></dict></plist>

You should have the same app ID as you did on all previous steps and TEAM_ID matching your Apple developer account.

You shouldn’t include the entitlements file in Payload. Instead, use it as an argument of thecodesign command:

codesign -f -s "Apple Distribution: Your Account Name (TEAM_ID)" --entitlements 'MakeTest.entitlements' Payload/MakeTest.app

Step 6. Create ipa

To install your app on a physical device, you need to create an ipa archive. Let’s see how to do it:

mkdir Payloadcp -r MakeTest.app Payloadzip -r MakeTest.ipa Payload

Here we are! We have an archive — MakeTest.ipa.

Step 7. Distribution

To distribute your apps, I recommend Diawi. Diawi (Development & In-house Apps Wireless Installation) is a service allowing you to upload your ipa (or apk for Android), and get a link and QR code. You send this link (and/or QR code) to the device owner (the UDID of the iOS device should be in the provisioning profile you created) and they can install it with a couple of taps.

The problem is that with the current configuration, you can’t even upload it, which leads us to step 8.

Step 8. Update Info.plist and troubleshoot

When you make a build in Xcode, it adds some fields to Info.plist before signing it.

Warning! You should update your app signature after any changes, including changes in Info.plist

.

These two fields are necessary to upload the release:

<key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array><key>MinimumOSVersion</key><string>10.0</string>

The version number can be different, depending on your target iOS version.

When you add these fields, the upload will be successful, but you may still not be able to install the app. The production version of Info.plist should contain information about compatible devices.

I copied the values from the Xcode-generated ipa. Some of them are not necessary, but they won’t do any harm:

<key>BuildMachineOSBuild</key><string>19D76</string><key>DTCompiler</key><string>com.apple.compilers.llvm.clang.1_0</string><key>DTPlatformBuild</key><string>17B102</string><key>DTPlatformName</key><string>iphoneos</string><key>DTPlatformVersion</key><string>13.2</string><key>DTSDKBuild</key><string>17B102</string><key>DTSDKName</key><string>iphoneos13.2</string><key>DTXcode</key><string>1130</string><key>DTXcodeBuild</key><string>11C504</string><key>UIDeviceFamily</key><array><integer>1</integer><integer>2</integer></array><key>UIRequiredDeviceCapabilities</key><array><string>arm64</string></array><key>UIRequiresFullScreen</key><true/><key>UIStatusBarHidden</key><false/>

Here’s the final version of my Info.plist file:

There’s a possibility that your app won’t be installed on your device yet. Very likely, you won’t see any errors. But if you do, how do you troubleshoot them?

Open the Console app on your Mac. Your iOS device should be connected.

Choose your iOS device and type your app name in the filter (upper right corner). App install generates around 50 messages, and most of them don’t have much information, so it can take hours to find and fix the problem.

In the screenshot above, you can see a problem with the entitlements file. You shouldn’t have it if you followed all the steps, but if your case is more complicated or you missed some step, you can use the Console app to check what’s wrong.

Done

If you did everything right, you’ll see your app installed on your device.

If you still have problems:

  • Try to add the app icon. You don’t need Asset Catalog for it. You can just add the necessary png files and add them to your
  • Info.plist.
  • Add PkgInfo file. Honestly, I don’t understand the purpose of it, but all Xcode-generated packages include it. It has only 8 characters:
  • APPL????.

Conclusion

It’s totally possible to create iOS apps without using Xcode. If you really have something against Xcode, you should probably use AppCode. You can write code, build, and debug right there. It has many plugins, which will make the process easier.

Layouts can be made in many different ways, using code or alternative solutions like Supernova Studio or Sketch (with the plugin).

Making iOS projects using only Terminal and text editor is extremely complicated, but totally possible. This method should be used only if it’s really necessary; for example, for automatic builds.

See you next time. Happy coding!

Source: Better Programming