📜 ⬆️ ⬇️

Distributing iOS apps by reference in a corporate environment using the Microsoft technology stack



Recently it became known about the purchase of Xamarin by Microsoft. This news has not gone unnoticed among the developer community, as well as among corporate clients. In this regard, the stories become more relevant, where the Microsoft Full Stack environment requires the integration of mobile solutions that do not lead to the need to dramatically expand the competence set of the IT-calving, or the company as a whole. For such scenarios, the choice of Xamarin as a component that fits well with a puzzle consisting of SharePoint, ASP.NET MVC, WebAPI services and Azure becomes concise.
This article describes the method of mobile application distribution within the company, mainly at the prototyping stage, using the listed Microsoft technology stack.
The method described in the article, despite the bias in the .NET environment, is applicable to iOS applications written using any other development tool, be it Apache Cordova or a classic native.


Introduction


')
In my previous article, I looked at how to distribute corporate iOS applications through an MDM solution using the example of OS X Server. This article will focus on the method of distributing applications by reference, using the Microsoft technology stack in the arsenal.

Why does the article focus on Microsoft tools? The answer lies on the surface. If the company is large, then most likely we are dealing with SharePoint, and therefore employees who have experience in developing .NET. However, all the same can be done on other technologies, for example, PHP.

As a rule, the method of distributing by reference will be chosen in cases when the number of users is very limited and they do not need automatic updating of the application, or, for the period of developing a prototype, when there is a group of testers or project curators within the company.

Note
Microsoft also has an MDM solution based on System Center Configuration Manager and Windows Intune. You can get more information about them by clicking on the links one , two and three .


Formulation of the problem



Let's define the tasks that we set for ourselves and the resources that we have to accomplish them:


We list the disadvantages that need to be considered before using our chosen method:


A brief sequence of actions when adding new users (iOS devices) is as follows:


Getting the UDID of a device is possible using iTunes or Apple Configurator 2. However, the natural desire is to automate this process. This is possible using profiles ( iOS Configuration Profile ), which are files with the extension * .mobileconfig, and having an XML structure.
In short, it works like this: in the * .mobileconfig file, you specify the parameters you want to request from the iOS device (in our case, the UDID), and the return URL where the iOS device will send the response XML file with the filled fields.

Ensuring the ability to download and install the application for a direct link also has a number of features that we consider in this article. One of them is the need to use HTTPS.


Conceptual design and work planning



So that you can estimate the scope of actions when using this method, I have prepared two schemes.

Spreading an app by reference using an Apple Developer Program account


Distributing the application by reference using the Apple Developer Enterprise Program account



As you can see, using the standard Apple Developer Program account incurs a lot of overhead. Nevertheless, we consider this option as the most difficult.

I divided my narrative into three sections, so that it was convenient to perceive the information in the desired sequence. We will call them "steps." So let's take a quick look at what we have to do.

Step 1: Get the UDID of the user's iOS device:


Step 2: Install the application via the link:


Step 3: Deployment in a corporate environment based on Windows Server 2012 (contains changes to Step 2, if external sites with valid SSL certificates are not available to you):



Note
I deliberately provide a detailed description of all stages and intermediate actions so that people, even with superficial knowledge in this area, have no difficulty in setting up the solution described. Therefore, please be lenient. The article is marked as "educational material". Thank.


Step 1: Get the UDID of the user's iOS device



To obtain the UDID of an iOS device, we will use ASP.NET WebAPI 2. This is a convenient solution that will immediately provide us with:


Creating a WebAPI project



As a platform in Step 1 and 2 I will use Microsoft Azure. By default, a valid SSL certificate is provided for * .azurewebsites.net sites, which we will need in Step 2. Of course, for corporate use, you also need to provide, at a minimum, the simplest authorization. However, this is much beyond the scope of this article.

Create a * .mobileconfig file and put it in the directory of the WebAPI project “Downloads / corp-apps.mobileconfig”
Content of * .mobileconfig file
<?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>PayloadContent</key> <dict> <key>URL</key> <string>http://iphone-udid.azurewebsites.net/api/xml</string> <key>DeviceAttributes</key> <array> <string>UDID</string> <string>IMEI</string> <string>ICCID</string> <string>VERSION</string> <string>PRODUCT</string> </array> </dict> <key>PayloadOrganization</key> <string>Corp Apps</string> <key>PayloadDisplayName</key> <string>CorpApps</string> <key>PayloadVersion</key> <integer>1</integer> <key>PayloadUUID</key> <string>B43A078F-E0E2-4F52-B1E6-C03AD7032EDF</string> <key>PayloadIdentifier</key> <string>com.CorpApps.profile-service</string> <key>PayloadDescription</key> <string>This temporary profile will be used to find and display your current device's UDID.</string> <key>PayloadType</key> <string>Profile Service</string> </dict> </plist> 



In this file, two fields are noteworthy:

The URL is the address of our API controller that will process the XML file sent from the iOS device;
In my case, I’ll provide a link to a site hosted in Azure, where / api / xml is the POST method of the WebAPI controller:
  http://iphone-udid.azurewebsites.net/api/xml 

PayloadUUID - the easiest way to describe this field is as a unique GUID, which should not be repeated within the same iOS device.
A unique UUID (PayloadUUID) can be generated on OS X using the “uuidgen” utility:

Using the uuidgen utility
  os-x-server:~ zanael$ uuidgen -- generate a universally unique identifier usage: uuidgen [-hdr] -hdr emit result in form suitable for coping into a header os-x-server:~ zanael$ uuidgen B43A078F-E0E2-4F52-B1E6-C03AD7032EDF os-x-server:~ zanael$ 


Result of running the uuidgen utility



Create an XML file in which we will store the registered UDIDs. Let's call it udids.xml, and put it in the directory of the WebAPI project “App_Data / udids.xml”.
XML file structure for storing registered UDIDs
  <?xml version="1.0" encoding="utf-8"?> <udids> <iPhone> <TimeAdded></TimeAdded> <UDID></UDID> </iPhone> </udids> 



Add a permission to Web.config for downloading profile files.
Necessary changes in Web.config
  <?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <staticContent> <mimeMap fileExtension=".mobileconfig" mimeType="application/x-apple-aspen-config" /> </staticContent> </system.webServer> </configuration> 



Add a model to store information about registered UDID in the Models folder.
IPhoneUDID.cs file
  namespace iPhoneUDID.Models { public class iPhoneUDID { public string TimeAdded { get; set; } public string UDID { get; set; } } } 



Create a HomeController (MVC controller) in the Controllers folder and add a method to it to display the list of registered UDIDs from XML.
HomeController.cs file
  using System; using System.Collections.Generic; using System.Web.Mvc; using System.Xml.Linq; namespace iPhoneUDID.Controllers { public class HomeController : Controller { [HttpGet] public ActionResult Index() { ViewBag.Title = "Home Page"; XDocument databaseXML; try { databaseXML = XDocument.Load(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml")); } catch (Exception exp) { databaseXML = new XDocument(new XElement("udids")); } List<Models.iPhoneUDID> UDIDs = new List<Models.iPhoneUDID>(); XElement iPhones = databaseXML.Element("udids"); foreach (XElement e in iPhones.Elements("iPhone")) { UDIDs.Add(new Models.iPhoneUDID { TimeAdded = e.Element("TimeAdded").Value, UDID = e.Element("UDID").Value }); } ViewBag.UDIDs = UDIDs; return View(); } } } 



Add a View (Index.cshtml) for the HomeController, which will greet the user and ask them to register their UDID.
When you click on the Take UDID button, the user will download the profile, the iOS device will understand what parameters we need and send them to the URL that we specified in the * .mobileconfig file in the XML format.
File /Views/Home/Index.cshtml
  <div class="jumbotron"> <h1>Get your UDID</h1> <p class="lead">Please, reach this page in Mobile Safari from your iPhone.</p> <p><a href="/downloads/corp-apps.mobileconfig" class="btn btn-primary btn-lg">Take UDID</a></p> </div> <div class="row"> <p> @{ foreach (iPhoneUDID.Models.iPhoneUDID item in ViewBag.UDIDs) { <h5>TimeAdded: @item.TimeAdded, UDID: @item.UDID</h5> } } </p> </div> <div class="row"> @Html.ActionLink("Clear UDIDs list", "ClearHistory", "Home", null, new { @class = "btn btn-primary btn-large" }) </div> 



Create an XmlController (WebApi2 controller) in the Controllers folder, which will catch the XML file from the iOS device, and save the UDID obtained from it to the local XML.
Since we are using a Windows machine, the second part of the file will be in a different encoding. This can be fixed using the * .plist conversion library. They can be found on GitHub, but since we only need the UDID, we can take it elementary from a given position in a row. This will allow us not to pull extra dependencies.
XmlController.cs file
  using System; using System.Net; using System.Net.Http; using System.Web.Http; using System.Xml.Linq; namespace iPhoneUDID.Controllers { public class XmlController : ApiController { [HttpPost] public HttpResponseMessage PostRawXMLMessage(HttpRequestMessage request) { string plist = request.Content.ReadAsStringAsync().Result; int begin = plist.IndexOf("UDID") + 20; int end = plist.IndexOf("<", begin); string UDID = plist.Substring(begin, end - begin); XDocument databaseXML; try { databaseXML = XDocument.Load(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml")); } catch (Exception exp) { databaseXML = new XDocument(new XElement("udids")); } databaseXML.Element("udids").Add( new XElement("iPhone", new XElement("TimeAdded", DateTime.Now.ToLongTimeString() + " - " + DateTime.Now.ToShortDateString()), new XElement("UDID", UDID))); databaseXML.Save(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml")); HttpResponseMessage response = request.CreateResponse(HttpStatusCode.MovedPermanently); response.Headers.Add("Location", "/Thanks"); return response; } } } 



Create an ThanksController (MVC controller) to which the user will be redirected after receiving his UDID.
ThanksController.cs file
  using System.Web.Mvc; namespace iPhoneUDID.Controllers { public class ThanksController : Controller { [HttpGet] public ActionResult Index() { ViewBag.Title = "Thanks Page"; return View(); } } } 



Add a View (Index.cshtml) for ThanksController.
File /Views/Thanks/Index.cshtml
  <div class="jumbotron"> <h1>Thanks for Attending!</h1> </div> 



To clear the local XML with the received UDID, add the ClearHistory method to the HomeController (MVC controller).
HomeController.cs file
  [HttpGet] public ActionResult ClearHistory() { ViewBag.Title = "Home Page"; XDocument databaseXML = new XDocument(new XElement("udids")); databaseXML.Save(Server.MapPath("/App_Data/udids.xml")); ViewBag.UDIDs = new List<Models.iPhoneUDID>(); return View("Index"); } 



This is how the general Layout (_Layout.cshtml) looks like - standard for the template
_Layout.cshtml file
  <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink(" ", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink(" ", "Index", "Home", new { area = "" }, null)</li> <li>@Html.ActionLink("API", "Index", "Help", new { area = "" }, null)</li> </ul> </div> </div> </div> <div class="container body-content"> @RenderBody() <hr /> <footer> <p>© @DateTime.Now.Year –  ASP.NET</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") @RenderSection("scripts", required: false) </body> </html> 



Now, all the user needs is to go to the site and click a couple of buttons.
User action




If you click More Details









After this, we will see its UDID on the site.
List of Registered UDIDs



Finally, we have in our hands the UDID of the iOS device, which we need to register with our developer account.
Let's go to the Apple website for developers in the Member Center section.
Read more








Add the UDID of the iOS device.
Read more




If you have not yet created an App ID, do it.
Read more




Create a Provisioning Profile for your application.
Read more














Now at any time you can add devices to this distribution profile.






Step 2: Install the application through the link



After we received the UDID of the iOS device, added it to the developer account and made changes to the Provisioning Profile, we need to provide the user with a web resource that will contain the files necessary to install the application. As such a resource, I will use Microsoft Azure, which by default provides a valid SSL certificate for * .azurewebsites.net sites. As the site name, I will choose:
  https://corp-apps.azurewebsites.net 

Note
Installing an application by reference will only work if you use HTTPS with a valid SSL certificate.

As a project to create a website, you can choose the simplest template, without any controllers. All we need is an HTML page and the ability to download several additional files.
Read more



Now we can start preparing the necessary files. To do this, export the application package via Xcode.
Read more















Fill in the information needed to distribute the application. Pay attention to the file extensions.


Read more



After the export is completed in the target folder, you will see the manifest manifest.plist, in which the paths to the required resources are specified, as well as pictures of the required format and size.
The contents of the file manifest.plist
  <?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>items</key> <array> <dict> <key>assets</key> <array> <dict> <key>kind</key> <string>software-package</string> <key>url</key> <string>https://corp-apps.azurewebsites.net/app-files/myApp.ipa</string> </dict> <dict> <key>kind</key> <string>display-image</string> <key>url</key> <string>https://corp-apps.azurewebsites.net/app-files/image.57x57.png</string> </dict> <dict> <key>kind</key> <string>full-size-image</string> <key>url</key> <string>https://corp-apps.azurewebsites.net/app-files/image.512x512.jpg</string> </dict> </array> <key>metadata</key> <dict> <key>bundle-identifier</key> <string>com.habr.hellohabr001</string> <key>bundle-version</key> <string>0.0.1</string> <key>kind</key> <string>software</string> <key>title</key> <string>myApp</string> </dict> </dict> </array> </dict> </plist> 



Now, we need to allow downloading of the specified file types from the server. This can be done by making changes to the Web.config file.
Necessary changes in the Web.config file
  <?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <staticContent> <mimeMap fileExtension=".jpg" mimeType="image/jpg" /> <mimeMap fileExtension=".png" mimeType="image/png" /> <mimeMap fileExtension=".plist" mimeType="text/plain" /> <mimeMap fileExtension=".ipa" mimeType="application/octet-stream" /> </staticContent> </system.webServer> </configuration> 



And finally, we will add for users an html-page with a special link to download the application.
Index.html file
  <!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title> </title> <link rel="stylesheet" href="dist/bootstrap.min.css"> </head> <body> <div class="container"> <br><br> <h1 class="text-center">Hello Apache Cordova</h1> <p class="text-center"> <a href="itms-services://?action=download-manifest&url=https://corp-apps.azurewebsites.net/app-files/manifest.plist" class="btn btn-info" type="button">Install App</a> </p> <br><br> </div> </body> </html> 



Great, all the user needs to do is press a few buttons.
To emphasize the universality of the method in relation to the development tools, the example used an application written in Apache Cordova. The same is true for other tools, such as Xamarin.
User action











Step 3: Deploy in a Windows Server 2012-based Enterprise Environment



So, we come to the most difficult scenario when external sites are not available to you. In this case, we will use Windows Server 2012 and IIS 8.

For clarity, we will deploy two separate sites:

Note
Installing HTTPS for the iphone-udid-zzzz.com site is optional, just take this case to demonstrate the configuration of IIS.

To access sites by specified domain names, a configured DNS server is needed (in the example, an Active Directory domain controller, DNS server, and the sites themselves are deployed on the same machine).

Let's start creating direct viewing zones for our sites. To do this, use the server manager.
Read more











Let's create a forward view zone for the UDID site of the iOS device: iphone-udid-zzzz.com .
Read more







For academic purposes, I will provide network settings that are important to consider.
Read more
Server network adapter settings (where 192.168.0.1 is the IP address of the WiFi router)



DNS settings for the WiFi router. In the case of D-Link, the order of DNS servers is important.



Network Adapter Settings for a Windows Server 2012 Virtual Machine




Create a node (A or AAAA).
Read more


Let's write down the IP address of our server



Make sure that the site is accessible through the FQDN name. Perform a DNS cache flush.





Great, we see that everything works.


We repeat the same chain of actions for the application download site: corp-apps-zzzz.com .
Read more







Now we can proceed to the installation and configuration of IIS.
Read more








We select the Web server (IIS).



Here we leave everything unchanged.



The following configuration was used in this guide:









Be careful when choosing "Automatic Reboot" on the battle server.





Installation completed.


Create a stub for our sites.
Read more









Since, to install applications on iOS devices by reference, HTTPS support is required - let's start creating SSL certificates. Unfortunately, the SSL certificates generated by IIS do not suit us, so we will use the OpenSSL utility.
This guide uses the Win32 OpenSSL command line utility, which can be downloaded from the link .
OpenSSL installation details
Select the bitness of your operating system. Win64 OpenSSL v1.0.2e (16MB Installer) was selected for this Windows Server 2012



Moments to which attention should be paid during installation.






After installing OpenSSL, create an OpenSSL-Certificates folder on drive C.

Run the command prompt. Create a certificate for iphone-udid-zzzz.com .
Using the openssl utility to create a certificate
  C:\Users\devin> cd c:\OpenSSL-Certificates c:\OpenSSL-Certificates> set RANDFILE=c:\OpenSSL-Certificates\.rnd c:\OpenSSL-Certificates> set OPENSSL_CONF=C:\OpenSSL-Win64\bin\openssl.cfg c:\OpenSSL-Certificates> c:\OpenSSL-Win64\bin\openssl.exe OpenSSL> genrsa -out iphone-udid-zzzz.key 2048 OpenSSL> req -new -x509 -sha256 -key iphone-udid-zzzz.key -out iphone-udid-zzzz.cer -days 365 -subj /CN=iphone-udid-zzz.com OpenSSL> pkcs12 -export -out iphone-udid-zzzz.pfx -inkey iphone-udid-zzzz.key -in iphone-udid-zzzz.cer Enter Export Password: Verifying - Enter Export Password: OpenSSL> exit c:\OpenSSL-Certificates> 


Execution result





Similarly, create a certificate for corp-apps-zzzz.com :
Using the openssl utility to create a certificate
  C:\Users\devin> cd c:\OpenSSL-Certificates c:\OpenSSL-Certificates> set RANDFILE=c:\OpenSSL-Certificates\.rnd c:\OpenSSL-Certificates> set OPENSSL_CONF=C:\OpenSSL-Win64\bin\openssl.cfg c:\OpenSSL-Certificates> c:\OpenSSL-Win64\bin\openssl.exe OpenSSL> genrsa -out corp-apps-zzzz.key 2048 OpenSSL> req -new -x509 -sha256 -key corp-apps-zzzz.key -out corp-apps-zzzz.cer -days 365 -subj /CN=corp-apps-zzzz.com OpenSSL> pkcs12 -export -out corp-apps-zzzz.pfx -inkey corp-apps-zzzz.key -in corp-apps-zzzz.cer Enter Export Password: Verifying - Enter Export Password: OpenSSL> exit c:\OpenSSL-Certificates> 


Execution result





The next step is importing the newly created * .pfx certificates into IIS.
Read more









Now we can change the bindings to provide an HTTPS connection.

For site: iphone-udid-zzzz.com








For a site: corp-apps-zzzz.com









Great, the next step is to make changes to the WebAPI project for the iphone-udid-zzzz.com site.

The Web.config file, to eliminate problems with WebDAV and 405 errors, the system.webServer section.
Necessary changes in the Web.config file
  <system.webServer> <validation validateIntegratedModeConfiguration="false" /> <modules runAllManagedModulesForAllRequests="true"> <remove name="WebDAVModule"/> </modules> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <add name="ExtensionlessUrl-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> <remove name="WebDAV" /> </handlers> <staticContent> <mimeMap fileExtension=".mobileconfig" mimeType="application/x-apple-aspen-config" /> </staticContent> </system.webServer> 


Certificates will also need to add changes to Web.config .
Read more
  <system.webServer> <staticContent> <remove fileExtension=".mobileconfig" /> <mimeMap fileExtension=".mobileconfig" mimeType="application/x-apple-aspen-config" /> <remove fileExtension=".crt" /> <mimeMap fileExtension=".crt" mimeType="application/x-x509-ca-cert" /> </staticContent> </system.webServer> 


File /Downloads/iphone-udid-zzzz.mobileconfig - changed URL.
Read more
  <key>URL</key> <string>https://iphone-udid-zzzz.com:9001/api/xml</string> 


The generated iphone-udid-zzzz.cer certificate has been added to the / Downloads / folder , for which you need to change the extension to * .crt . Otherwise, on the iOS device, it will open in the browser simply as a text file.
Read more


File /Views/Home/Index.cshtml - URLs (href) have been changed, a link to download an SSL certificate has been added.
Necessary changes in the Index.cshtml file
  <div class="jumbotron"> <h1>Get your UDID</h1> <p><a href="/downloads/iphone-udid-zzzz.mobileconfig" class="btn btn-primary btn-lg">Take UDID</a></p> </div> <div class="row"> <a href="/downloads/iphone-udid-zzzz.crt" download class="btn btn-info">Install SSL-Certificate</a> <p> @{ foreach (iPhoneUDID.Models.iPhoneUDID item in ViewBag.UDIDs) { <h5>TimeAdded: @item.TimeAdded, UDID: @item.UDID</h5> } } </p> </div> <div class="row"> @Html.ActionLink("Clear UDIDs list", "ClearHistory", "Home", null, new { @class = "btn btn-primary btn-large" }) </div> 


File /Controllers/XmlController.cs - added try / catch construction - wrapper for plist parsing (UDID).
Read more
  using System; using System.Net; using System.Net.Http; using System.Web.Http; using System.Xml.Linq; namespace iPhoneUDID.Controllers { public class XmlController : ApiController { [HttpPost] public HttpResponseMessage PostRawXMLMessage(HttpRequestMessage request) { string plist = request.Content.ReadAsStringAsync().Result; string UDID = "Error"; try { int begin = plist.IndexOf("UDID") + 20; int end = plist.IndexOf("<", begin); UDID = plist.Substring(begin, end - begin); } catch (Exception exc) { // } XDocument databaseXML; try { databaseXML = XDocument.Load(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml")); } catch (Exception exp) { databaseXML = new XDocument(new XElement("udids")); } databaseXML.Element("udids").Add( new XElement("iPhone", new XElement("TimeAdded", DateTime.Now.ToLongTimeString() + " - " + DateTime.Now.ToShortDateString()), new XElement("UDID", UDID))); databaseXML.Save(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/udids.xml")); HttpResponseMessage response = request.CreateResponse(HttpStatusCode.MovedPermanently); response.Headers.Add("Location", "/Thanks"); return response; } } } 



We can start building a project for the iphone-udid-zzzz.com site.
Read more










We copy the received files in the corresponding folder on the server.
Read more




I recommend reloading the site.



Also, you must allow changes to the udids.xml file within the directory on the server.
Read more







From this point on, users can register the UDID of their iOS devices on the site:
  https://iphone-udid-zzzz.com:9001 

User action
Install SSL certificate. This is optional.






If you click More Details








Register UDID iOS device.




If you click More Details








Displays the UDID of the iOS device.




In order to test the POST registration request UDID, I recommend using the free extension for Google Chrome - Postman .


Read more







Let's start building the project for the corp-apps-zzzz.com website.

Web.config file.
Content of the Web.config file
  <?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <staticContent> <remove fileExtension=".jpg" /> <mimeMap fileExtension=".jpg" mimeType="image/jpg" /> <remove fileExtension=".png" /> <mimeMap fileExtension=".png" mimeType="image/png" /> <remove fileExtension=".plist" /> <mimeMap fileExtension=".plist" mimeType="text/plain" /> <remove fileExtension=".ipa" /> <mimeMap fileExtension=".ipa" mimeType="application/octet-stream" /> <remove fileExtension=".crt" /> <mimeMap fileExtension=".crt" mimeType="application/x-x509-ca-cert" /> </staticContent> </system.webServer> </configuration> 


File index.html - changed URL (href), added link to download SSL certificate.
The user must install this certificate before installing the application. Otherwise, it will receive a certificate authentication error.
Necessary changes in the index.html file
  <!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title> iOS </title> <link rel="stylesheet" href="dist/bootstrap.min.css"> <script src="dist/jquery-1.11.3.min.js"></script> <style> h4 {line-height: 25px;} </style> </head> <body> <div class="container"> <br><br> <h1 class="text-center">Hello Apache Cordova</h1> <p class="text-center"> <a href="itms-services://?action=download-manifest&url=https://corp-apps-zzzz.com:9002/app-files/manifest.plist" class="btn btn-info" type="button">Install App</a> <a href="/app-files/corp-apps-zzzz.crt" download class="btn btn-info">Install SSL-Certificate</a> </p> <br><br> </div> </body> </html> 


File manifest.plist - changed URL (added ports).
Necessary changes in the manifest.plist file
  <?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>items</key> <array> <dict> <key>assets</key> <array> <dict> <key>kind</key> <string>software-package</string> <key>url</key> <string>https://corp-apps-zzzz.com:9002/app-files/myApp.ipa</string> </dict> <dict> <key>kind</key> <string>display-image</string> <key>url</key> <string>https://corp-apps-zzzz.com:9002/app-files/image.57x57.png</string> </dict> <dict> <key>kind</key> <string>full-size-image</string> <key>url</key> <string>https://corp-apps-zzzz.com:9002/app-files/image.512x512.jpg</string> </dict> </array> <key>metadata</key> <dict> <key>bundle-identifier</key> <string>com.habr.hellohabr001</string> <key>bundle-version</key> <string>0.0.1</string> <key>kind</key> <string>software</string> <key>title</key> <string>myApp</string> </dict> </dict> </array> </dict> </plist> 


The generated certificate corp-apps-zzzz.cer has been added to the / app-files / folder , for which you need to change the extension to * .crt . Otherwise, on the iOS device, it will open in the browser simply as a text file.
Read more



Now, to install the application, the user needs to press just a few buttons by going to the website:
  https://corp-apps-zzzz.com:9002 

User action
Install SSL certificate.







If you click More Details








Install the application.








Conclusion



In this article, we looked at how to distribute iOS applications by reference using the Microsoft technology stack.

In order to review, I posted a demo projects on GitHub.



I hope the article will seem useful to people starting to understand this topic.

Source: https://habr.com/ru/post/277765/


All Articles