<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Eldar E.</title>
    <description>The latest articles on Forem by Eldar E. (@eldare).</description>
    <link>https://forem.com/eldare</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F254610%2F4664ecea-3b11-43ed-b7f3-9c42902ccbc3.jpeg</url>
      <title>Forem: Eldar E.</title>
      <link>https://forem.com/eldare</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/eldare"/>
    <language>en</language>
    <item>
      <title>Embedding Python interpreter inside a MacOS app (and iOS app), and publish to the App Store successfully.</title>
      <dc:creator>Eldar E.</dc:creator>
      <pubDate>Sat, 03 Dec 2022 13:20:37 +0000</pubDate>
      <link>https://forem.com/eldare/embedding-python-interpreter-inside-a-macos-app-and-publish-to-the-app-store-successfully-4bop</link>
      <guid>https://forem.com/eldare/embedding-python-interpreter-inside-a-macos-app-and-publish-to-the-app-store-successfully-4bop</guid>
      <description>&lt;h2&gt;
  
  
  The struggle was real
&lt;/h2&gt;

&lt;p&gt;When I started looking into this topic, I quickly realized most of the guides, tutorials and StackOverflow answers have only partial information or straight out give bad advice, that would prevent you from publishing your app on the Apple's App Store.&lt;/p&gt;

&lt;p&gt;I had to scramble though all that and was finally able to embed a Python interpreter in a MacOS app, fully signed and with the ability to be published to the App Store.&lt;/p&gt;

&lt;p&gt;So here it is, a quick and simple guide on how-to embed a Python interpreter into your app. It's really simple when you have clear steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would we even need this?
&lt;/h2&gt;

&lt;p&gt;As a native Apple eco system developer (I mostly do iOS these days), I believe Swift is the only way to go when developing for iOS and MacOS. &lt;br&gt;
But, there are times your app requires something big you don't want to reinvent, and it's only available in Python. Then why not use it?&lt;/p&gt;

&lt;p&gt;Python has so many tools and Open Source code you can use straight out of the box, that can empower your application with amazing functionality, and you can easily call Python code from Swift.&lt;/p&gt;

&lt;p&gt;But be careful, by embedding a Python interpreter in your binary, you enlarge it by around ~100MB. So I wouldn't recommend this for simple stuff you can either do yourself in Swift or find a well maintained SPM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can you publish these kind of MacOS apps on the App Store?
&lt;/h2&gt;

&lt;p&gt;Yes. You can embed Python and publish to the MacOS App Store.&lt;/p&gt;

&lt;p&gt;While MacOS already comes with Python, you don't want to use it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can't rely on the Python version installed on the system.&lt;/li&gt;
&lt;li&gt;This will require you to delete your MacOS app's Sandbox and &lt;code&gt;Disable Library Validation&lt;/code&gt;. And once you do it, you can't submit your app to the App Store, and you even might have issues with the Notarization outside the App Store: &lt;a href="https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution"&gt;https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the other hand, by embedding the Python interpreter you allow the Python part of your app to be signed as well.&lt;br&gt;
This allows the Hardened Runtime to remain intact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NO NEED to delete the Sandbox - you need it to be able to submit your MacOS App to the App Store.&lt;/li&gt;
&lt;li&gt;NO NEED to &lt;code&gt;Disable Library Validation&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What about iOS?
&lt;/h2&gt;

&lt;p&gt;Yes. You can embed Python and publish to the iOS App Store. Just look at Pyto: &lt;a href="https://pyto.app"&gt;https://pyto.app&lt;/a&gt;&lt;br&gt;
There is no limitation from iOS App Store on doing so. It's all signed as a single app.&lt;br&gt;
The process of embedding Python on iOS is very similar to MacOS, but it might require a few more steps I will cover in a future article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-step to embed Python interpreter in a MacOS app
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add PythonKit SPM: &lt;br&gt;
&lt;a href="https://github.com/pvieito/PythonKit"&gt;https://github.com/pvieito/PythonKit&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Download Released framework for the desired Python version (for MacOS platform):&lt;br&gt;
&lt;a href="https://github.com/beeware/Python-Apple-support"&gt;https://github.com/beeware/Python-Apple-support&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extract the &lt;code&gt;python-stdlib&lt;/code&gt; and &lt;code&gt;Python.xcframework&lt;/code&gt; from the &lt;code&gt;tag.gz&lt;/code&gt; archive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy them to the root of the MacOS App, preferably via Xcode.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Xcode General -&amp;gt; Frameworks:&lt;br&gt;
5.1. Should already be there:&lt;br&gt;
    - &lt;code&gt;Python.xcframework&lt;/code&gt; is set as &lt;code&gt;Do Not Embed&lt;/code&gt;&lt;br&gt;
    - &lt;code&gt;PythonKit&lt;/code&gt;&lt;br&gt;
5.2. Add additional required framework:&lt;br&gt;
    - &lt;code&gt;SystemConfiguration.framework&lt;/code&gt; set as &lt;code&gt;Do Not Embed&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Xcode &lt;code&gt;Build Phases&lt;/code&gt;:&lt;br&gt;
6.1. Verify &lt;code&gt;Copy Bundle Resources&lt;/code&gt; contains &lt;code&gt;python-stdlib&lt;/code&gt;.&lt;br&gt;
6.2. Add bash script to Sign &lt;code&gt;.so&lt;/code&gt; binaries in &lt;code&gt;python-stdlib/lib-dynload/&lt;/code&gt;:&lt;br&gt;
IMPORTANT NOTE: &lt;code&gt;.so&lt;/code&gt; binaries must be signed with your TeamID, if you need to use &lt;code&gt;Sign and Run Locally&lt;/code&gt; it will be signed as ad-hoc, and you will need to &lt;code&gt;Disable Library Validation&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Signing as &lt;/span&gt;&lt;span class="nv"&gt;$EXPANDED_CODE_SIGN_IDENTITY_NAME&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$EXPANDED_CODE_SIGN_IDENTITY&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CODESIGNING_FOLDER_PATH&lt;/span&gt;&lt;span class="s2"&gt;/Contents/Resources/python-stdlib/lib-dynload"&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.so"&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; /usr/bin/codesign &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nt"&gt;--sign&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXPANDED_CODE_SIGN_IDENTITY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; runtime &lt;span class="nt"&gt;--timestamp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none &lt;span class="nt"&gt;--preserve-metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;identifier,entitlements,flags &lt;span class="nt"&gt;--generate-entitlement-der&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a file called &lt;code&gt;module.modulemap&lt;/code&gt; with the following code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module Python {
    umbrella header "Python.h"
    export *
    link "Python"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Place the &lt;code&gt;module.modulemap&lt;/code&gt; file inside the &lt;code&gt;Python.xcframework/macos-arm64_x86_64/Headers/&lt;/code&gt;.&lt;br&gt;
This will allow us to do &lt;code&gt;import Python&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Init Python at runtime, as early as possible:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Python&lt;/span&gt;

&lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;stdLibPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Bundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forResource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"python-stdlib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;ofType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;libDynloadPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Bundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forResource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"python-stdlib/lib-dynload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;ofType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;setenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PYTHONHOME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdLibPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;setenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PYTHONPATH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;stdLibPath&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;libDynloadPath&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;Py_Initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// we now have a Python interpreter ready to be used&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run test code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;PythonKit&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sys"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Python Version: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;major&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minor&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Python Encoding: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getdefaultencoding&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Python Path: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"math"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// verifies `lib-dynload` is found and signed successfully&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;We're in business. Now we can add whatever python code we want.
To integrate 3rd party python code and dependencies, you will need to make sure &lt;code&gt;PYTHONPATH&lt;/code&gt; contains their paths; And then you can just do &lt;code&gt;Python.import(" &amp;lt;SOME LIB&amp;gt; ")&lt;/code&gt;.
Sometimes, the python code might be too complicated to call with PythonKit from Swift, so my recommendation is to write a small Python script and call that script's method from Swift instead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Good luck.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;^(;,;)^&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt;&lt;br&gt;
I've submitted the above steps to be the official usage guide for BeeWare's Python-Apple-support. If you're having trouble with the above steps, the usage guide might contain additional info: &lt;a href="https://github.com/beeware/Python-Apple-support/blob/main/USAGE.md"&gt;https://github.com/beeware/Python-Apple-support/blob/main/USAGE.md&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>swift</category>
      <category>pythonkit</category>
      <category>macos</category>
      <category>ios</category>
    </item>
    <item>
      <title>Forcing iOS localization at runtime -the right way.</title>
      <dc:creator>Eldar E.</dc:creator>
      <pubDate>Mon, 21 Oct 2019 13:04:08 +0000</pubDate>
      <link>https://forem.com/eldare/forcing-ios-localization-at-runtime-the-right-way-2o24</link>
      <guid>https://forem.com/eldare/forcing-ios-localization-at-runtime-the-right-way-2o24</guid>
      <description>&lt;h2&gt;
  
  
  iOS localization, and how it should be done
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;From Apple internalization docs:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Localization is the process of translating your app into multiple languages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds simple enough, and most of the time it is. You add resources for the languages you wish to support and iOS will automatically select and use one according to user’s device language.&lt;/p&gt;

&lt;p&gt;But what if your app specification requires you to force a specific language at runtime? Here is where things get a bit tricky.&lt;/p&gt;

&lt;p&gt;In this short article, I’m going to talk about forcing localization, and the right way to approach this topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, you need to force a language?
&lt;/h2&gt;

&lt;p&gt;Here are some of the reasons you might need to force a specific language:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Allow users to pick a language, and switch it on the fly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The app is designated for a specific country but must be displayed in a different language.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The same app needs to be released to different countries and used only with their native tongue.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At &lt;a href="https://healthy.io"&gt;Healthy.io&lt;/a&gt; I had a requirement similar to #3, same app with multiple languages. Usually, the simplest way to achieve this is to set a different target for each variation of the app and compile only with the Base language resources.&lt;/p&gt;

&lt;p&gt;But, there’s a catch. You need to manage multiple targets and their settings, like Build Phases, Build Rules, etc.&lt;br&gt;
Every change you make to one target’s settings will need to be duplicated to other targets.&lt;/p&gt;

&lt;p&gt;An alternative is to keep a single target, with multiple schemes and set the language at runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single target, easy to maintain.&lt;/li&gt;
&lt;li&gt;Extremely easy to add a new app variations with new languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Depends on your localization resources, it might be bloated since your binary was compiled with all the languages.&lt;/li&gt;
&lt;li&gt;Apple doesn’t really like this approach, but it shouldn’t cause a rejection during the review.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Forcing a language at runtime
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;**Here is what Apple has to say on the matter: *&lt;/em&gt;*&lt;br&gt;
In general, you should not change the iOS system language (via use of the AppleLanguages pref key) from within your application.&lt;br&gt;
This goes against the basic iOS user model for switching languages in the Settings app, and also uses a preference key that is not documented, &lt;br&gt;
meaning that at some point in the future, the key name could change, which would break your application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After a short research you’ve definitely seen this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;While it does work, it’s not a complete solution for one simple reason: &lt;br&gt;
It requires an app restart. Meaning, the user starts the app for the first time, sees a wrong language, closes the app and starts it again. Great user experience! 😏&lt;/p&gt;
&lt;h2&gt;
  
  
  What should you do?
&lt;/h2&gt;

&lt;p&gt;The above line of code is the right direction, but by itself, it’s not enough to provide a good user experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;**From Apple: *&lt;/em&gt;*&lt;br&gt;
If you want to switch languages in your application, you can do so via manually loading resource files in your bundle.&lt;br&gt;
You can use NSBundle:pathForResource:ofType:inDirectory:forLocalization: for this purpose, but keep in mind that your application would be responsible for all loading of localized data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to achieve the right language on the first app start, you need to access the desired language bundle yourself:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Great! But we don’t want to deal with this part everywhere: &lt;em&gt;“keep in mind that your application would be responsible for all loading of localized data”&lt;/em&gt;.&lt;br&gt;
To make it work seamlessly we’ll need to swizzle &lt;em&gt;localizedString(forKey:value:table:)&lt;/em&gt; to access the desired bundle.&lt;/p&gt;

&lt;p&gt;In case you’re not familiar with swizzling, you should read &lt;a href="https://medium.com/@abhimuralidharan/method-swizzling-in-ios-swift-1f38edaf984f"&gt;Method swizzling in iOS swift&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;From the article:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Method swizzling is the process of changing the implementation of an existing selector at runtime. Simply speaking, we can change the functionality of a method at runtime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Please note, that while I'm against swizzling in general, I think cases like these are a good example of when it's ok to swizzle.)&lt;/p&gt;

&lt;p&gt;The complete code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And one last thing; To ensure RTL/LTR switch, you need to reload the rootViewController.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t reinvent the wheel
&lt;/h2&gt;

&lt;p&gt;That's it? Not quite. If you need Storyboard/Nibs support or just don't want to manage this yourself, then I have good news for you! There's a great library called &lt;a href="https://github.com/Abedalkareem/LanguageManager-iOS"&gt;LanguageManager-iOS&lt;/a&gt;.&lt;br&gt;
It’s small, easy to use, maintained, and supports Carthage, Cocoa-Pods and SPM. Jackpot!!&lt;/p&gt;

&lt;p&gt;Quick usage example on how to set the default language on app start with LanguageManager-iOS:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  IMPORTANT UPDATE: LanguageManager-iOS No longer supports swizzling
&lt;/h3&gt;

&lt;p&gt;The LanguageManager-iOS lib v1.1.4+, no longer swizzles, thus requires you to call String.localiz() yourself. You can find more information in the Github issue: &lt;a href="https://github.com/Abedalkareem/LanguageManager-iOS/issues/46"&gt;https://github.com/Abedalkareem/LanguageManager-iOS/issues/46&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  textAligntment = .natural
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;textAlignment = .natural&lt;/em&gt; can’t be trusted on first app start, and needs to be handled manually for &lt;em&gt;UITextView&lt;/em&gt;, &lt;em&gt;UILabel&lt;/em&gt;, and &lt;em&gt;UITextField.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;**Important Note: **Just to be on the safe side, you should set the text before textAligntment is set.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Localized Info.plist
&lt;/h3&gt;

&lt;p&gt;Sadly, Localized Info.plist ignores runtime language set, and so far I was not able to resolve this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;When you need to stray from Apple’s path, localization becomes a hassle; So I’m really glad someone took the time to make and maintain a lib like &lt;a href="https://github.com/Abedalkareem/LanguageManager-iOS"&gt;LanguageManager-iOS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have notes, additional issues/solutions, please don’t keep them to yourself.&lt;/p&gt;

&lt;p&gt;Cheers! 🎈&lt;br&gt;&lt;br&gt;
Eldar.&lt;/p&gt;

</description>
      <category>swif</category>
      <category>ios</category>
      <category>localization</category>
      <category>runtime</category>
    </item>
    <item>
      <title>Calling your other iOS app with parameters, even if it’s not yet installed; Without a website, or 3rd party services.</title>
      <dc:creator>Eldar E.</dc:creator>
      <pubDate>Mon, 21 Oct 2019 12:55:34 +0000</pubDate>
      <link>https://forem.com/eldare/calling-your-other-ios-app-with-parameters-even-if-it-s-not-yet-installed-without-a-website-or-3rd-party-services-44dl</link>
      <guid>https://forem.com/eldare/calling-your-other-ios-app-with-parameters-even-if-it-s-not-yet-installed-without-a-website-or-3rd-party-services-44dl</guid>
      <description>&lt;p&gt;Consider the following scenario:&lt;/p&gt;

&lt;p&gt;You have 2 apps, A and B. App A needs to launch App B with a username, and maybe some other more complex data structure. And we can’t assume App B is even installed.&lt;/p&gt;

&lt;p&gt;There are various ways to do this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Possible solution #1:&lt;/strong&gt; A simple Deep-Linking with parameters should do the trick; And for when App B is not yet installed, we can use a 3rd party service like Branch. But, can you really trust them to connect the dots and be able to communicate the data reliably after the user installs App B?&lt;/p&gt;

&lt;p&gt;Due to the hacky nature of such 3rd party services, while they work most of the time, occasionally they do fail to pass the data to the target app.&lt;/p&gt;

&lt;p&gt;Also, don’t forget that Apple is all about privacy these days, and services like Branch are no different than tracking tools. &lt;br&gt;
Speaking of privacy, the data you want to send to App B might be sensitive, so you need to trust Branch with your users’ data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Possible solution #2:&lt;/strong&gt; Universal Linking, is Apple’s alternative to Deep-Linking, and it does allow you to pass parameters to App B, even if it’s not yet installed. But, then you need a website and network availability. It might not be for everyone.&lt;/p&gt;

&lt;p&gt;If only App A could communicate complex (preferably Codable) data to App B, without all the hassle and disadvantages of the above orthodox solutions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introducing App Groups
&lt;/h2&gt;

&lt;p&gt;App Groups was designed to share data between the main app and it’s extensions, but it can also be used to share data between two apps under the same publisher.&lt;br&gt;
From the code perspective, App Groups is basically a UserDefaults in a shared container.&lt;/p&gt;
&lt;h2&gt;
  
  
  A quick example
&lt;/h2&gt;

&lt;p&gt;Here is what needs to be done to communicate a username string, from App A (sender) to App B (receiver), even if App B is not yet installed.&lt;/p&gt;
&lt;h3&gt;
  
  
  I. Create App Groups, on both apps:
&lt;/h3&gt;

&lt;p&gt;For both App A &amp;amp; App B, go to &lt;code&gt;Capabilities&lt;/code&gt;, enable &lt;code&gt;App Groups&lt;/code&gt;, and create a group. For this example let’s call it “group.myAmazingApp”.&lt;/p&gt;

&lt;p&gt;Note: &lt;a href="https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups" rel="noopener noreferrer"&gt;according to Apple&lt;/a&gt;, it must be group..&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F4892%2F1%2AcKETb27PQp2yONDz93NQ0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F4892%2F1%2AcKETb27PQp2yONDz93NQ0g.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  II. Create URL Scheme for app B:
&lt;/h3&gt;

&lt;p&gt;In order to allow app A to open App B, we need to setup the URL Scheme in App B. In App B, go to &lt;code&gt;Info.plist -&amp;gt; URL Types&lt;/code&gt;, and create new; For this example we’ll call it “appB”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F4904%2F1%2AISijhRR3Hvw_EXLtHT9MTg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F4904%2F1%2AISijhRR3Hvw_EXLtHT9MTg.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  III. Allow App A to access App B’s URL Scheme:
&lt;/h3&gt;

&lt;p&gt;Explicitly whitelist App B’s URL Scheme, to allow App A to call it.&lt;br&gt;
In App A, go to &lt;code&gt;Info.plist -&amp;gt; Custom iOS Target Properties&lt;/code&gt;, create LSApplicationQueriesSchemes as an Array, and add “appB”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2A-U790g9FvwyiU7I_DfN_zA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2A-U790g9FvwyiU7I_DfN_zA.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  IV. Code for App A (sender):
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  V. Code for App B (receiver):
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;App Groups is an interesting alternative to communicate data between your apps, with plenty of pros, and some cons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Simple setup and use.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allows to start an app with one or more parameters, even if the target app is not yet installed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No need to own a website for your app, or even access to network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unlike with Universal-Link and Deep-Linking, data can be complex with Codables via Data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pretty secure/private, as any data on a user’s device; Everything is stored locally, and can only be accessed by the same publisher.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It might be more secure than Deep-Linking, but it’s no more secure than your app’s container UserDefaults; So you shouldn’t use it for sensitive data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Your opinion matters
&lt;/h2&gt;

&lt;p&gt;Should this method be turned into a dual communication channel for same publisher’s app to transfer data locally? Let’s discuss in the comments.&lt;/p&gt;

&lt;p&gt;Cheers! 🎈&lt;br&gt;&lt;br&gt;
Eldar&lt;/p&gt;

</description>
      <category>swift</category>
      <category>ios</category>
      <category>deeplink</category>
      <category>alternative</category>
    </item>
  </channel>
</rss>
