<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by M. Kerem Keskin on Medium]]></title>
        <description><![CDATA[Stories by M. Kerem Keskin on Medium]]></description>
        <link>https://medium.com/@mkeremkeskin?source=rss-c94ebcb80e52------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*UYY-o41fGgiscf94dML4Vw.jpeg</url>
            <title>Stories by M. Kerem Keskin on Medium</title>
            <link>https://medium.com/@mkeremkeskin?source=rss-c94ebcb80e52------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 05 May 2026 19:54:39 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@mkeremkeskin/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Good manners of a Pull Request & Some best practices]]></title>
            <link>https://medium.com/deliveryherotechhub/good-manners-of-a-pull-request-some-best-practices-cb2de3c3aea1?source=rss-c94ebcb80e52------2</link>
            <guid isPermaLink="false">https://medium.com/p/cb2de3c3aea1</guid>
            <category><![CDATA[code-review]]></category>
            <category><![CDATA[git]]></category>
            <category><![CDATA[pull-request]]></category>
            <category><![CDATA[github]]></category>
            <category><![CDATA[best-practices]]></category>
            <dc:creator><![CDATA[M. Kerem Keskin]]></dc:creator>
            <pubDate>Wed, 17 Nov 2021 13:16:55 GMT</pubDate>
            <atom:updated>2021-11-17T14:12:51.208Z</atom:updated>
            <content:encoded><![CDATA[<h3>Good Manners of a Pull Request &amp; Some Best Practices</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*yL14v_zRCvztj0-LHUVuoQ.jpeg" /></figure><p>Hey everyone,</p><p>As software developers/engineers when we want to write on something we primarily focus on technical challenges since they are easy to describe and do not have many grey areas. For this post, I want to change this a little bit and focus on more human interaction side. I will try to talk about the <strong>etiquette &amp; good manners</strong> of a pull request.</p><p>Creating, reviewing, and handling pull requests is a subjective topic, every development team should find their own best practices about it. So in this article, I’m gonna walk you through some of the ways we are using in <strong>Yemeksepeti</strong> to make our pull requests better.</p><p>Some of them can be considered global and some of them are not, some of them may apply to only crowded teams and some of them may not be helpful to you. While reading please consider how would these ways fit your team.</p><h3>Single Responsibility Principle of a Pull Request</h3><p>Applying the single responsibility principle to pull requests is always a good idea.</p><p>Try not to include some additional stuff into the PR. For example, do not fix any typos other than your current context or do not add a tiny bug fix to a feature. In this way, you can easily describe your aim and prevent confusion. You can also eliminate the possibility of one change waiting for the other.</p><h3>Title and Description of a Pull Request &amp; Pull Request Templates</h3><p>Title and description is the first place where you can inform your teammates about the changes. PR titles can be traced via searching so adding some key information on the title would be useful.</p><p>Description of a PR should always be prepared with the same attention, whether the PR has a small or huge change. Reviewing the pull request requires changing the context for the reviewer. One minute ago you were focusing on apples and now you are looking at oranges. In order to ease this context change, the description of a PR is our first weapon. You can include anything in the description like Jira tasks, crash logs, design visuals, etc. Do not restrict your imagination for the description, every detail matters.</p><blockquote>Always think that anybody could read your PR anytime. So do not assume this PR is only for the current situation and for the current reviewers. You are leaving some piece to your codebase history. Describe everything with these attitudes.</blockquote><p>There are lots of teams in Yemeksepeti, in our team we are using Github. Github lets you create <strong>Pull Request Templates</strong><em> </em>(even though it is still primitive 😄) which can help you with the PR descriptions. I suggest you take a look at this feature, it can be very helpful 🚀</p><p>By using pull request templates you can define guidelines for your team for creating descriptions for the pull request.</p><p>There is an example pull request description below which was created with our template. You can create your pull request template by looking at the <a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository">official documentation</a>. These templates are actually <strong>markdown</strong> documents. You can set it as default or you can create multiple templates and use it via query params while creating your PR.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/907/1*dQPNkOQgt5Xz95pJ1Ehagg.png" /><figcaption>Thanks <a href="https://medium.com/u/44277fda859f">Samed Biçer</a> for doing the research and creating this template for the team 🚀</figcaption></figure><h3>Possible ways to make your Pull Request more understandable</h3><ul><li>For a long time running, big codebases (like we have in Yemeksepeti 😄) you probably have a modular project structure, and lots of common codes are being used on the entire project. If you have changed some code pieces that affect the entire project it would be nice to talk about why you have made this decision and warn reviewers to be more careful. Crowded teams probably people have their domains and mostly they work on related parts. So maybe they haven’t gone through the parts you are changing on the PR. It is the author’s responsibility to notify reviewers and keep the project safe.</li><li>Commenting for the lines on your PR is another best practice to help your teammates. I like putting <strong>official documentation</strong>, <strong>Stackoverflow,</strong> and <strong>article links</strong> to the related lines which solve some strange bug or bring a brand new concept to the codebase.</li><li>Another good practice is to arrange a meeting (probably an online one thanks to remote working 👨🏻‍💻 ) after creating the PR to tell your teammates about the PR content. <strong>Be humble</strong> and do not fall for quotes<em> </em>like<em> </em><strong><em>best codes speak for themselves</em></strong> 😄. For long PRs this can really be helpful for directing your teammates for possible code pieces that you want to be looked at. Also while your teammates review the PR you can apply some small suggested changes on the fly. I think this approach is a really good way for everyone to express their opinions and also show your trust in them doing the changes live. This practice reminds me of pair programming and while remotely working I deeply understand the value of pair programming 💪🏻.</li><li>It is the author’s duty to test the code before PR. Do not present not working code to your teammates. As I mentioned in the second topic, reviewing PRs requires context changing for the reviewers. Show some respect to them and give the best possible PR experience both with codes and the execution.</li><li>A small but important thing, if you’ve changed something important with the PR after some reviewers have accepted it please do not forget to warn already accepted teammates to review again.</li></ul><h3>Language and attitude tips</h3><ul><li>Both reviewers and the author should be polite in the comments. Naturally, we may state some disagreements in the PR and it can bring unintentional tension between the reviewers and the author. Being polite is the best way to prevent it. Do not hesitate to use emojis and reactions to lighten the mood 🙂.</li><li>Our pull requests’ quality can be different from time to time according to different things like the subject of the PR, our mood, time limitation, etc. so keep in mind that <strong>nothing is personal in the PRs</strong>. Do not take comments personally. The comments are not judging your personality or your abilities, they are just focusing on the current code.</li><li>Try not to use only a few words when leaving or answering comments. Try to use sentences as much as possible. Also when you disagree with something please come up with an alternative approach in order to avoid an unproductive loop.</li><li>When you’re a reviewer and your suggestion is too complex to tell by sentences, consider creating a pull request to an existing pull request. You should embrace it as a good habit. With this approach, you become in the same shoes as the author and you can provide better ways to tell about your solution.</li><li>Let&#39;s talk about <strong>Language</strong> literally 😄. In my opinion code reviews should be in English as the software literature is. Using the team’s native language can be helpful to ramp up understandability but does not work in multinational teams. (Even if they are closed, PRs can be reviewed in the future. So if you have non-native language speaking people in the future your older PRs will be useless for them). Plus if you are a non-native English speaker doing pull requests in English can help you to improve your level.</li></ul><h3>BONUS: Some technical approaches to improve pull requests</h3><p>Even though I’ve told you at the start I will focus on more human interaction I couldn’t leave this part out 😄.</p><ul><li>You should run your unit test before creating the PR. Even better the team should have a continuous integration step that runs unit tests when a PR is created. By doing this you can save every reviewers time who will try to run unit tests. Also when the build step is running the PR gets blocked from being merged faulty.</li><li>You should use lint tools (static code analysis) to keep your code in line with the team&#39;s preferred styles. A similar step that runs lint checks on the PR when it is created can be formed. This way you can fix style errors before your teammates point them out.</li></ul><p>Since every team has its own ways of creating and reviewing their pull requests lots of advice, opinions can be found in other articles which I left some of them below. I briefly told about our team’s practices.</p><p>Also, there are lots of stuff about the technical side of the pull requests so please read about them in order to make the best pull requests for your team 🙂.</p><ul><li><a href="https://github.blog/2015-01-21-how-to-write-the-perfect-pull-request/">https://github.blog/2015-01-21-how-to-write-the-perfect-pull-request/</a></li><li><a href="https://medium.com/google-developer-experts/how-to-pull-request-d75ac81449a5">https://medium.com/google-developer-experts/how-to-pull-request-d75ac81449a5</a></li><li><a href="https://github.com/thoughtbot/guides/tree/main/code-review">https://github.com/thoughtbot/guides/tree/main/code-review</a></li><li><a href="https://gist.github.com/mikepea/863f63d6e37281e329f8">https://gist.github.com/mikepea/863f63d6e37281e329f8</a></li><li><a href="https://github.community/t/best-practices-for-pull-requests/10195">https://github.community/t/best-practices-for-pull-requests/10195</a></li><li><a href="https://developers.google.com/blockly/guides/modify/contribute/write_a_good_pr">https://developers.google.com/blockly/guides/modify/contribute/write_a_good_pr</a></li></ul><p>Thanks for reading. <br>Please feel free to comment and share it if you like it. Also if you want to talk about your best practices please leave a response, I would be appreciated to hear them.<br>See ya! 👋🏻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cb2de3c3aea1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deliveryherotechhub/good-manners-of-a-pull-request-some-best-practices-cb2de3c3aea1">Good manners of a Pull Request &amp; Some best practices</a> was originally published in <a href="https://medium.com/deliveryherotechhub">Delivery Hero Tech Hub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Extensive Swift Enums Guide]]></title>
            <link>https://medium.com/plus-minus-one/extensive-swift-enums-guide-2869b0357c05?source=rss-c94ebcb80e52------2</link>
            <guid isPermaLink="false">https://medium.com/p/2869b0357c05</guid>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[enum]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[enumeration]]></category>
            <dc:creator><![CDATA[M. Kerem Keskin]]></dc:creator>
            <pubDate>Mon, 16 Nov 2020 13:30:20 GMT</pubDate>
            <atom:updated>2020-11-16T13:30:20.891Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DfOZpQG2h9BjeuT241NPJQ.jpeg" /></figure><p>Hi everyone,</p><p>In this article, I’m gonna talk about <strong>Enums</strong> in <strong>Swift</strong>. At <a href="http://plusminusone.co">Plus Minus One</a> we love using enums.</p><p>Enums are mostly used as a <strong>basic type defining structure</strong> by most of the developers, but actually, they can be used in a very wide range.</p><p>You can see official documentation <a href="https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html">here</a>. As you can see Enums are much more capable than you think.</p><p>I will be presenting some of the use cases of Enums from our team below.</p><h3>Enums can conform to protocols</h3><p>Yes, enums can conform protocols. You can use Swift’s own protocols or custom protocols. By using protocols with Enums you can add more capabilities.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6e550ccf064228a5d9cc2c400aba2efe/href">https://medium.com/media/6e550ccf064228a5d9cc2c400aba2efe/href</a></iframe><p>Sometimes we need to use similar but different object types in the same place. For this, we need to define a generic type for these objects. For example, we can append different FilterType and SizeType enums to the same [BaseSelectionType] array and checking theirs selectionType fields we can safely cast to the desired type.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/874/1*o74O88cPfJNkYvlGiOm_Ng.png" /><figcaption>Another example usage of enum type checking between parent &amp; child</figcaption></figure><h3>Enums with Switch Cases</h3><p>This is the most common usage of enums. Instead of <strong>if-clauses, </strong>we can use <strong>switch-case statements </strong>which make our codebase more readable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/484/1*_RO3hAijYoBMJ2ewtXW12g.png" /></figure><p>A switch-case statement can be used inside the enum with self keyword also. With the next topic, you’ll see the related example.</p><h3>Enums with Computed Properties</h3><p>We can not use <strong>stored properties</strong> with enums but we can use <strong>computed properties</strong>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d78a6007d9f5c1671f057b0c99c81ede/href">https://medium.com/media/d78a6007d9f5c1671f057b0c99c81ede/href</a></iframe><p>In this example by using a switch statement with self keyword, we are getting a string to display it on the screen for the related FilterType.</p><pre>let text = FilterType.colorDocument.getDisplayText</pre><p>We can use methods for these kinds of operations also.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/200ae6a92e2b8e794d30e0766e709654/href">https://medium.com/media/200ae6a92e2b8e794d30e0766e709654/href</a></iframe><p>We can add multiple computed properties and methods to our enum cases. By using them we can customize our enums and get all the related info inside them.</p><h3>Enums with associated values</h3><p>Creating our custom endpoints is one of the use cases where we use enums a lot. As you can see with this example we are using all the things we have talked about above.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/85ece9b4e7c77b3cf3237f6e4e4f3056/href">https://medium.com/media/85ece9b4e7c77b3cf3237f6e4e4f3056/href</a></iframe><p>By giving .getInfo(perPage: 100) to our method, we provide that request() method will get the path variable from enum with the associated value given.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wTCCVkDv0BahoQsaCKcjUQ.png" /></figure><blockquote><strong>You can not define associated values with raw values. This is because raw values are predefined uniquely and static but associated values are given while initialization, thus they are dynamic.</strong></blockquote><h3>Enums with CaseIterable</h3><p>By conforming <a href="https://developer.apple.com/documentation/swift/caseiterable">CaseIterable</a> we gain access for using allCases property, in this way we do not need to deal with adding all of the enum cases to an array. We are getting all of the cases of our enum type instantly.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a34f75e55eb1f3ab32e33cdc7bc4fb18/href">https://medium.com/media/a34f75e55eb1f3ab32e33cdc7bc4fb18/href</a></iframe><h3>Enums with Raw Values &amp; Initialization with Raw Values</h3><p>Instead of associated values, we can define our Enum cases with <strong>Raw Values </strong>in an old fashioned way.</p><p>Also, we can create enums by initializing them with raw values.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/23b425a488dc83c67cc42c006989574c/href">https://medium.com/media/23b425a488dc83c67cc42c006989574c/href</a></iframe><h3>Enums with Custom Initialization</h3><p>We can create our enum types by using custom init methods also. Let’s explain this with an example. Assume that we’re getting our filter types from the backend with integer ids. Also, our app supports multi-language.</p><p>Considering these cases, let’s define <strong>localization id</strong> of each case to their raw values and define a custom init method which takes an integer value and returns related enum case.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/794b58f33ab698e62ff55274d2152f1f/href">https://medium.com/media/794b58f33ab698e62ff55274d2152f1f/href</a></iframe><p>With this approach whenever we get FilterType ‘s from backend we can initialize them with our custom init method. By using predefined raw values we can get localized strings easily.</p><p>These kinds of little tricks would make your codebase more readable and understandable.</p><p>I’ve shown some usage of <strong>Swift Enums,</strong> but they offer more than those. You can use them <strong>recursively</strong>, they<strong> </strong>can also have <strong>extensions</strong>, <strong>subscripts, </strong>etc<strong>.</strong></p><p>To learn more about our development experiences you can look at our <a href="https://medium.com/plus-minus-one">medium page</a>. At <a href="https://www.plusminusone.co/">Plus Minus One</a>, we love to learn and share our experiences. We hope this article makes your life easier 🙏🏻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2869b0357c05" width="1" height="1" alt=""><hr><p><a href="https://medium.com/plus-minus-one/extensive-swift-enums-guide-2869b0357c05">Extensive Swift Enums Guide</a> was originally published in <a href="https://medium.com/plus-minus-one">Plus Minus One</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hidden gems of UIStackView]]></title>
            <link>https://medium.com/dolap-tech/hidden-gems-of-uistackview-3b94a0001d29?source=rss-c94ebcb80e52------2</link>
            <guid isPermaLink="false">https://medium.com/p/3b94a0001d29</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[stack-view]]></category>
            <category><![CDATA[uistackview]]></category>
            <category><![CDATA[tips]]></category>
            <dc:creator><![CDATA[M. Kerem Keskin]]></dc:creator>
            <pubDate>Thu, 02 Jan 2020 15:09:44 GMT</pubDate>
            <atom:updated>2022-03-16T06:56:54.660Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/422/1*XUk7UTjOLBBfbcTYnLLGtA.png" /><figcaption>Icons are taken from Vitaly Gorbachev’s icon set. <a href="https://www.flaticon.com/packs/gems">https://www.flaticon.com/packs/gems</a></figcaption></figure><p>Hello everyone,</p><p>Before Auto Layout and before iPhone had only one device size, everything was defined by pixels. Because you have one device size and because there is no such thing as Auto Layout generally everything was static.</p><p>In 2012 Apple introduced us <strong>iPhone 5 </strong>with a brand new<strong> 4-inch </strong>device size. Also that year they released Auto Layout because things were changing. Our views had to be responsive since we have two different device sizes.</p><p>Did you ever feel the lack of proper container view when using Auto Layout in those dark days? Or if you’re familiar with Android programming did you ever think <strong>I wish there is something like </strong><strong>LinearLayout</strong>?</p><p><a href="https://stackoverflow.com/questions/13075415/evenly-space-multiple-views-within-a-container-view"><strong>Evenly space multiple views within a container view</strong></a><strong>, </strong>please look at this stack overflow thread from 2012. Go on look at the ugly answers before UIStackView.</p><p>Finally, in <strong>2015 </strong>(after 3 years) Apple presented us UIStackView with the release of <strong>iOS 9</strong> and saved us from the trouble 😄.</p><p>Nearly for 5 years we are using UIStackView but do we know what it is really capable of? In this article, I’m going to give you some tips and how to use hidden gems of UIStackView .</p><h3>Hiding and Reordering of views</h3><p>Why do we need to hide or reorder our views, let’s start with a use case. These days A/B tests are very important to apps that are used by a wide range of users. You have to think A/B testing even with small changes in your views. In <strong>Dolap</strong> we are using it very much and believe me some times it can be challenging. But should these changes bug you that much? In order to prevent these, we are using UIStackView as much as possible.</p><p>Consider hiding an inner view in a container view or changing your view’s order with Auto Layout , think about all the constraints you have to delete and create again when changing the order. Or think about calculating and setting height constraint of the <strong>container view</strong> when hiding a subview.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2FWRMq4MMApzBeg%2Ftwitter%2Fiframe&amp;display_name=Giphy&amp;url=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2FWRMq4MMApzBeg%2Fgiphy.gif&amp;image=https%3A%2F%2Fi.giphy.com%2Fmedia%2FWRMq4MMApzBeg%2Fgiphy.gif&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=giphy" width="435" height="244" frameborder="0" scrolling="no"><a href="https://medium.com/media/390728e2683f22796aaea678aa81cc7c/href">https://medium.com/media/390728e2683f22796aaea678aa81cc7c/href</a></iframe><p>With the help of UIStackView these operations are too easy on the storyboard. Just change the order by dragging your view to the desired order or hide your view just setting it to hidden.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*mASI34jVm9mvn-wI9qmA2g.gif" /></figure><p>Or you can do the reordering in the code via using UIStackView ‘s <a href="https://developer.apple.com/documentation/uikit/uistackview/1616232-arrangedsubviews">arrangedSubviews</a> property. This array gives you the current subviews in the stack view and by using <a href="https://developer.apple.com/documentation/uikit/uistackview/1616237-insertarrangedsubview">insertArrangedSubview</a> and <a href="https://developer.apple.com/documentation/uikit/uistackview/1616235-removearrangedsubview">removeArrangedSubview</a> every items’ order can be manipulated. Pretty easy right 👍🏻</p><h3>Using UIStackView as layout manager for custom views with help of Placeholders</h3><p>At <strong>Dolap</strong> we are using storyboards to see our UIViewController with their general view hierarchy. Stack views contain inner customs views. These custom views are defined in their own Xibs so you can see only their definition in the storyboard. In this way we are preventing conflicts in the storyboards.</p><p>But there is a problem with this approach. If you put your views in aUIScrollView or you let’s say you want to see relative heights of your custom views (larger views should be seen large and smaller views are small) you can not achieve this with the default settings.</p><p>To make it possible you can usePlaceholder in Intrinsic Size attribute. Placeholders are only visible in Storyboard and will be removed in runtime. Custom views’ height will be automatically set in <strong>runtime</strong> according to the height of the views they contain.</p><p>As you can see in the gif below, those inner views are empty and no constraints are set. Yet there aren’t any auto layout errors. Views get their height from their placeholders.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*8umkF-eoaoy2ZuM6BP6yHQ.gif" /></figure><h3>Bye Bye CONSTRAINTS and CALCULATIONS!</h3><p>This<strong> the part I like the most</strong> about the UIStackView. You can ditch most of the auto layout constraints with UIStackView.</p><p>Let assume you have two labels one under the other.</p><ul><li>One is a static text label as title</li><li>Other label’s content can have multiple lines but first, you have to show only the first 2 lines of the content.</li><li>When the user taps <em>Read More</em> button, label expands and shows remaining content and vice versa. (You can see the example visual below)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-BWuspZ4apLVt1bN9Qf2Xw.png" /></figure><p>With the help of UIStackView and number of lines attribute of UILabel we can achieve desired behavior without setting any constraints to <strong>title</strong> and <strong>expanding label</strong>. In order to show how stack view can expand I put it in a container view. (You don’t need the container view actually)</p><p>This container view has constant <strong>leading, trailing, top</strong> constraints which will never change. Stack view has constant constraints to fill the container view.</p><p>So changing number of lines from 2 to 0 (zero) expands the label, then stack view and container view enlarge accordingly.</p><p>As you can see without any <strong>calculations and constraint changes </strong>we have implemented an expanding label.</p><p><strong><em>Quick Tip:</em></strong><em> Use stack view’s fixed margins instead of using stack view constraints. This will save you from the trouble changing constraints one by one for margins.</em></p><p>The gif below shows everything needs to be done to achieve what we have talked about above. Also, you see that you don’t need to run your app to test the expanding label. You can see the changes in the storyboard also. It’s a long gif I know 😄 but sometimes images speak louder than words.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eLRWOefO2QHI63ft8-3Evg.gif" /></figure><h3>How to use UIStackView Alignment &amp; Distribution</h3><p>Even though we are trying to avoid setting constant constraints, sometimes we need to set constant height or width to our views in stack view. Again let’s go over on an example.</p><p>Let’s say we need a custom profile view with a profile image and a bio.</p><ul><li>This view has a square UIImageView with a width of 50.</li><li>Images going to be set to profile image view will be square but their resolution can be changed.</li><li>The profile image view should be rounded and vertically centered.</li><li>There is a label right to the image.</li></ul><p>It’s not that hard, right? But what if the label has so many lines, how does the image stay in the center vertically, how does the view enlarge with the label? For what we learned by now, stack view helps us with the enlarging of container view according to the label’s height.</p><p>But there is a problem? If stack view’s height increases image’s height increases also. Since we want to keep the ratio and we want to round the image this makes it impossible with the current <strong>Alignment</strong> and <strong>Distribution</strong> settings.</p><ul><li><strong>Alignment: </strong>The alignment of the arranged subviews perpendicular to the stack view’s axis. In our case stack view’s axis is horizontal so Alignment property changes alignment of all arranged subviews in the vertical orientation.</li><li><strong>Distribution: </strong>The distribution of the arranged views along the stack view’s axis. In our case stack view’s axis is horizontal so Distribution property distributes all of the arranged subviews in the horizontal orientation.</li></ul><p>I’ve prepared a visual which shows both storyboard and simulator. In the simulator at the bottom, you can see the desired profile view.</p><p>By looking at the info on the labels you can see the differences between the inner stack view’s <strong>alignment</strong> and <strong>distribution</strong> properties.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iGUbddJRHLPXonRyzwbbcA.png" /></figure><h3>How to adapt orientation changes</h3><p>Orientation change can be a scary concept 😄. Most of the time we go along with the portrait orientation. But what if your product team needs a landscape screen for the new feature of your app?</p><p>Let’s say we’re dealing with a simple screen just showing some ordered views. Of course, we are going to use UIStackView. Stack view will equally space the views for us after rotating the device, but is it the best way to display?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kz3QQ3EbmK04F4dxF9h3QA.gif" /></figure><p>What happens if our designer wants to keep the same amount of area for the views after orientation change. If we were not using stack view, we would deal with constraints changes or creating new views for orientation change and hiding the old ones.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BwYRNSoHdjmRffnZvNIpKg.png" /></figure><p>As you can see from the code above, stack view’s axis can be changed during runtime. Upon detecting orientation change, we can change the axis of the stack view and then inner views will be laid out accordingly. Views will keep having the same amount of area as they had in other orientations.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ijwZezn1Dki4-Mybs5ov8g.gif" /></figure><p>We have covered some usages of UIStackView but it has much more to offer. You can work on <strong>content hugging and content compression resistance priorities </strong>for changing the views’ distributions, you can <strong>add variations</strong> for different size classes, etc.</p><p>Of course, stack view can make you angry from time to time. Like not being able to set a background color to it, UIStackView is a non-rendering view so it doesn’t have background-color property. Sometimes UIStackView can show errors in interface builders but works well in runtime 😄. But believe me, UIStackView is a good friend of yours when you’re dealing with responsive design.</p><p>You can checkout and play with the sample project I created while writing this article.</p><p><a href="https://github.com/mkeremkeskin/GemsOfUIStackView">mkeremkeskin/GemsOfUIStackView</a></p><p>Thanks for reading the article.<br>Please feel free to comment and share it if you like it.<br>See ya! 👋🏻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3b94a0001d29" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dolap-tech/hidden-gems-of-uistackview-3b94a0001d29">Hidden gems of UIStackView</a> was originally published in <a href="https://medium.com/dolap-tech">Dolap Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Transfer an iOS App to Other Developer Account]]></title>
            <link>https://medium.com/dolap-tech/how-transfer-an-ios-app-to-other-developer-account-46a29e6df603?source=rss-c94ebcb80e52------2</link>
            <guid isPermaLink="false">https://medium.com/p/46a29e6df603</guid>
            <category><![CDATA[ios-app-transfer]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[app-transfer]]></category>
            <dc:creator><![CDATA[M. Kerem Keskin]]></dc:creator>
            <pubDate>Wed, 24 Jul 2019 07:15:15 GMT</pubDate>
            <atom:updated>2019-07-25T07:00:04.325Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kXzo8qN9vXHfPbGKxUNAXQ.jpeg" /></figure><p>App transfer is fairly simple process but it’s not well known since we rarely transfer our apps. You can not find enough tutorials about the process because once we are done with it probably we won’t ever need it again. 😄</p><p><a href="https://apps.apple.com/us/app/dolap-sat-ke%C5%9Ffet-yenilen/id1127881507"><strong>Dolap</strong></a> has been founded in 2016 and joined to <a href="https://apps.apple.com/us/app/trendyol-moda-al%C4%B1%C5%9Fveri%C5%9F/id524362642"><strong>Trendyol</strong></a> in 2018. Till last week we were operating on our own <strong>Apple Developer Account</strong> and <strong>App Store Connect</strong> account. Last week we have transferred Dolap iOS App to Trendyol’s account. In this article I’m going to tell you about some hidden secrets of app transfer which are not documented or you can only see along the process which I observed.</p><p>The official page for app transfer is <a href="https://help.apple.com/app-store-connect/#/deved688524f">this</a>. Overview page gives you some important information about the process. We can finish transferring an app in 5 steps (although overview show 4 steps, I added one step to fix your development and app management issues 😂). I will add some inner steps to these main steps to walk you along the process.</p><h3>Step 1: Verify that the app can be transferred</h3><p><em>The app must meet specific criteria to be transferable. For example, at least one version of the app must have been released to the App Store. For more criteria, go to </em><a href="https://help.apple.com/app-store-connect/#/devaf27784ff"><em>App transfer criteria</em></a><em>.</em></p><ul><li>If you are using or have used any <strong>iCloud</strong> or <strong>Passbook</strong> entitlement you are free to be mad. You can not transfer your app. <a href="https://stackoverflow.com/a/19559979/697467">https://stackoverflow.com/a/19559979/697467</a>.</li><li>Anything related to TestFlight section in App Store Connect should be removed.</li><li>Some tricky things can happen if you are using in-app purchase. If so please investigate it more. Since we don’t have in-app purchase I do not have valuable information about it.</li></ul><h3>Step 2: Back up all app information</h3><p><em>Because an app is removed from your account after an app transfer, you should back up all information about the app before you transfer it.</em></p><p>I’ve backed up everything, screenshots(lots of 😄), keywords, description etc. But in the process I see that you do not need backup that much. You should be ok if you backup the things below. 🤞🏻</p><ul><li>You have to back up your reports. Because they will be lost when app is transferred.</li><li>Support Url</li><li>General App Information and App Review Information sections.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W07tKxagebnI17ulHCzPgw.png" /></figure><h3>Step 3: Initiate the app transfer</h3><p><em>The </em><a href="https://help.apple.com/app-store-connect/#/dev1d7b9bccf"><em>Account Holder</em></a><em> user in your organization initiates the app transfer. Go to </em><a href="https://help.apple.com/app-store-connect/#/deve445a9a11"><em>Initiate an app transfer</em></a><em>.</em></p><p>For initiating app transfer you have login to App Store Connect with <strong>Account Holder</strong> role. Otherwise you can not see transfer option.</p><p>To start app transfer you need <strong>Apple ID</strong> and <strong>Team ID</strong> of the transferee account.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1009/1*giZqmvWYQ8WtptcyucnZMQ.png" /></figure><p>When starting app transfer you will see a screen something like above. You see that we have some information left on TestFlight section, so all the criteria was not met. After this screen you will enter Apple ID and Team ID. Process is done for the transferor account.</p><h3>Step 4: Accept the app transfer</h3><p><em>The Account Holder in the recipient organization accepts the app transfer. Go to </em><a href="https://help.apple.com/app-store-connect/#/devab4c3498a"><em>Accept an app transfer</em></a><em>.</em></p><p>This is an easy step, transferee account just accepts the request and fills the metadata with information provided by the transferor. (Backed up information in Step 3)</p><p>Although documentation says it can take up two business days, transfer completed in 15 minutes for our case.</p><h3>Step 5: Some related changes about development and app management</h3><p>Yes finally, I hope this step can provide some answers to your questions and solve your tricky problems.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*Gox3C3_UqLSBXQEywGtL0g.gif" /></figure><p>I will go over a list of unrelated items. Some of these items can happen in your case and some of not.</p><h4><strong>- Devices are lost</strong></h4><p>Your test devices will not be transferred to your transferee account so you have add it manually again.</p><h4>- Push Notifications</h4><p>Push notifications will work as it is for your <strong>Production</strong> app. You don’t have to worry about it.</p><p>But if you are using build configurations like <strong>QA</strong>, <strong>Stage</strong>, etc. things are getting a little messy here. Because bundle ids for configurations other than <strong>Prod</strong> are not being transferred obviously. So you have delete to them from old account and create them again in the transferee account. A small hint; if you are using <strong>Automatically Manage Signing </strong>you can go over this process more smoothly.</p><p>Also you have to create new <strong>APNS certificates</strong> for this newly created bundle ids, give them to your backend guys and to third party push services to keep your push notifications working. 😥</p><h4>- App Group (App Groups Entitlement)</h4><p>We are using app groups because we have an iMessage app also. This was the hardest part for me in this whole process because I couldn’t find crumb of information about app groups and app transfer.</p><p>App groups are not transferred. In earlier versions of Apple Developer page you there was option for deleting app group as you can see in this <a href="https://stackoverflow.com/questions/29117252/xcode-6-deleting-app-group">stackoverflow thread</a>. Right now there isn’t such option available.</p><p>So I had to create new app group name in Xcode’s capabilities section and uncheck the old one. In my case this solves my question. If you are facing the same thing please investigate more for better solutions.</p><h4>- Developer Accounts, App Store Connect User</h4><p>After transfer, all related developer accounts and App Store Connect accounts should be defined with same roles in the transferee account also.</p><h4>- Universal Links (Deeplink) 🤦🏼‍♂️</h4><p>If you are using Universal Links in your app you know about <strong>apple-app-site-association(AASA)</strong>. In this file accepted url’s are defined as&lt;TEAM ID&gt;&lt;URL&gt;.</p><p>When transfer completed, your <strong>TEAM ID </strong>is changed to <strong>transferee account’s TEAM ID</strong>. So you have to change your TEAM ID in your <strong>AASA</strong> file also if you are using services line <a href="https://branch.io/">Branch.io</a> you have to change it there too.</p><blockquote><strong>A strange note: </strong>Even though our deeplinks for our <strong>QA </strong>has stopped working. Somehow <strong>Production </strong>application is working fine. After I changed team id for <strong>QA app </strong>in<strong> </strong>AASA and Branch dashboard,<strong> </strong>problem is solved for<strong> QA.</strong></blockquote><blockquote>For the <strong>Production app</strong> I’m thinking maybe deeplinks are working because current app in store is signed with the <strong>old apple developer account</strong> so it has the old team id. So now I’m waiting for next update. Will the deeplinks stop working?</blockquote><p>This was my journey on app transfer. Different apps can have slight differences in transferring processes because of their capabilities. But overall this is how you transfer your app.</p><p>If you want contribute to my article please feel free to leave a response. I hope my experiences will be useful to you. Thanks for the reading.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=46a29e6df603" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dolap-tech/how-transfer-an-ios-app-to-other-developer-account-46a29e6df603">How Transfer an iOS App to Other Developer Account</a> was originally published in <a href="https://medium.com/dolap-tech">Dolap Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Gradient Bordered Label View on iOS]]></title>
            <link>https://medium.com/dolap-tech/gradient-bordered-label-view-on-ios-37bc97076ef2?source=rss-c94ebcb80e52------2</link>
            <guid isPermaLink="false">https://medium.com/p/37bc97076ef2</guid>
            <category><![CDATA[gradient]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[M. Kerem Keskin]]></dc:creator>
            <pubDate>Wed, 19 Dec 2018 08:21:28 GMT</pubDate>
            <atom:updated>2018-12-19T08:21:28.818Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/365/1*wRx61_doa6V6RK9D6md_2Q.png" /></figure><p>Hello everybody, in this post I will talk about a custom component we are using in <a href="https://itunes.apple.com/us/app/dolap-sat-ke%C5%9Ffet-yenilen/id1127881507?mt=8">Dolap</a>.</p><blockquote><strong>IBDesignable customizable, gradient bordered label view written in Swift.</strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/180/1*xIP7Q7FFGS97RY1214tDLg.png" /></figure><p>In <a href="https://itunes.apple.com/us/app/dolap-sat-ke%C5%9Ffet-yenilen/id1127881507?mt=8">Dolap</a>, we have different types for our merchants. They can be designer, second hand store, boutique etc. We are showing these merchant types in product details below merchants’ logo.</p><p>Our designer wants to show it in a cool gradient bordered label. You can see them in the screenshots from our iOS app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/1*KUfB0_xyvCK7RM-J9J4Zcw.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/746/1*ammpIbZCJI0Mz29_J6triQ.jpeg" /></figure><p>These merchant types can increase and vary in the future. What if new merchant type comes from backend and let’s assume we have chosen to use images to represent these types?</p><p>Then we will need to update our app for every new merchant type.</p><p>To prevent updates we chose to go along with custom view getting its text from backend.</p><p>So we need to create our custom label YAY!</p><p>I like seeing instant updates in interface builder. We app developers don’t always get chance to create custom components, so this custom label is a perfect place to use IBInspectable properties with IBDesignable class and dust off our coding skills.</p><p>For this component we have one swift file which has two classes and an extension in it. Everything is done in approx. 200 lines. I will explain every method in detail. But first you can see our file’s structure below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/274/1*fCYrP3-r4v-vL_Vg4KlUJg.png" /></figure><ul><li><strong>IBDesignable variables</strong> gives you chance to edit your view on the fly. These will be available on the storyboard and effect of changing values will be seen immediately.</li><li><strong>Private variables</strong> are properties are used while configuring the view.</li><li><strong>String Extension</strong></li><li><strong>CenteredTextLayer</strong></li><li><strong>Init Methods</strong></li><li><strong>Configure</strong> <strong>Method</strong></li></ul><p>Now let’s go over the last 4 items on the list with some explanation.</p><h3>String Extension</h3><p>We have added two methods to String for getting accurate height and width values. According to given height or width we can calculate both height and width. This methods will be used both in CenteredTextLayer and configure() method.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rJYZWQB9hnd3pr_dUmtl7A.png" /></figure><h3>CenteredTextLayer</h3><p>Simple subclass of CATextLayer which helps us to center the text both horizontally and vertically. Alignment mode takes care only horizontal alignment. By overriding draw() method, we calculate the height of the given string and position the label according the calculated y-axis difference.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pqvmr-MgDkFRuLnb5V_pcg.png" /></figure><h3>Init Methods</h3><p>Init methods are pretty simple actually. We are just calling configure() method to prepare views.</p><p>One trick to consider here is: overriding prepareForInterfaceBuilder() to see changes on the storyboard instantly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*6UdUa9UrjK7K3FOIT28P3Q.png" /></figure><h3>Configure Method</h3><p>Now we are where the real stuff is happening. You can see what we are trying to do from the graphic below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MQTbnJwiXJw5SIL-xvK_qQ.jpeg" /><figcaption>Thanks to our designer <a href="https://www.behance.net/mehmetdortyol">Mehmet Dörtyol</a> for the graphic</figcaption></figure><ul><li>We will prepare a text layer which have rounded corners, border and transparent background with either given font or default font.</li><li>A gradient layer will be created with desired color set.</li><li>This <strong><em>text layer</em></strong> will mask gradient layer.</li></ul><p><strong>As result we have a label;</strong></p><ul><li>has gradient color borders</li><li>has rounded corners</li><li>has transparent background</li><li>has text with gradient colors</li></ul><p>Lastly we will add this <strong><em>masked gradient layer </em></strong>to our background view created with desired color as a sublayer. At the end we end up with the label.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/365/1*wRx61_doa6V6RK9D6md_2Q.png" /></figure><p>You can see full configure() method below. One trick to consider here is in order to position text to center and follow Auto Layout changes we have to calculate font size. If height of the text surpasses the view frame we’re gonna decrement it iteratively till it fits in the frame.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/977/1*Iu0j6MQaHeakygloIO9-pg@2x.png" /></figure><p>You can see full source code from my GitHub page.</p><p><a href="https://github.com/mkeremkeskin/GradientBorderedLabelView">mkeremkeskin/GradientBorderedLabelView</a></p><p>Also <strong>GradientBorderedLabelView</strong> is available through CocoaPods.</p><p><a href="https://cocoapods.org/pods/GradientBorderedLabelView">GradientBorderedLabelView</a></p><p>Thanks for reading the article.<br>Please feel free to comment and share it if you like it.<br>See ya! 👋🏻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=37bc97076ef2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dolap-tech/gradient-bordered-label-view-on-ios-37bc97076ef2">Gradient Bordered Label View on iOS</a> was originally published in <a href="https://medium.com/dolap-tech">Dolap Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[IBDesignable custom vertical Slider on iOS]]></title>
            <link>https://medium.com/@mkeremkeskin/ibdesignable-custom-vertical-slider-on-ios-f0169c2d1bdb?source=rss-c94ebcb80e52------2</link>
            <guid isPermaLink="false">https://medium.com/p/f0169c2d1bdb</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[custom-views]]></category>
            <dc:creator><![CDATA[M. Kerem Keskin]]></dc:creator>
            <pubDate>Mon, 19 Feb 2018 11:34:57 GMT</pubDate>
            <atom:updated>2018-02-19T11:48:28.630Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/320/1*HBVh214bML9nacgyGUvXlA.gif" /></figure><p>Hey guys,</p><p>Some time ago our designer wanted an approval page with a vertical slider. This page is coming after some form screens (where user fills some info) and by sliding down the slider you can see the page beneath it. Also by sliding the slider to bottom end, user is approving the info he has filled.</p><p>You can see a ugly demo of it at top. (Purely designed by me 😅)</p><p>For this task, I’ve chosen to create IBDesignable custom slider which uses a xib file for reusing it in other projects via storyboards. My main aim was to be able to see my custom slider on storyboard. I like to have getting instant visual feedbacks about the custom views instead of compiling &amp; running projects.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZymXNfa5gqlLX_yQ2MbvKA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NmsBm7IZMt8cP_wTFMAKVA.png" /><figcaption>Xib and Storyboard — You can see that custom view is visible on the storyboard</figcaption></figure><p>By using IBDesignable you can live edit your custom view created via xib from your storyboard, which is kind a cool I guess 😎.</p><p>In this post we have 2 chapters:</p><ul><li><strong>Chapter 1: How to create custom vertical slider which uses xib</strong></li><li><strong>Chapter 2: How to make an </strong><strong>IBDesignable custom view which includes </strong><strong>IBInspectable elements</strong></li></ul><p>Beware there will be lots of code in this post and I didn’t want to make separate posts to keep the integrity. So here you are with kinda long post. I hope you can find something useful for you.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/622/1*xm4q3NdwH3ICmnL_fE0EGg.jpeg" /></figure><h3>Chapter 1: How to create custom vertical slider which uses xib</h3><ol><li>Creating the xib</li><li>Implementing the pan gesture action &amp; communication with parent view via protocol functions</li><li>Implementing methods which creates the view</li></ol><p>These are the steps we’re gonna take. Let’s start working on our slider:</p><h4>1. Creating the xib</h4><p>For this step I’ve added some screenshots which would be more helpful for you to understand. Also you can check the project on Github to see it for yourself. You can find the link at the end of the post.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/182/1*46xDaDxbDyuHbKOMCJvbiA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/267/1*jBRQDmh61M4fjy3JRs67xw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/256/1*7ohfy8foj6IJesGzvtVwEw.png" /><figcaption>from left to right: visual of view / view hierarchy / outlets and actions connections</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/256/1*MwjluG-zEXAL547woYU2yA.png" /><figcaption>pan gesture outlets and actions connections</figcaption></figure><p>You can see view hierarchy and their IBOutlets in the screenshots.</p><ul><li><strong>sliderPath:</strong> Where all the items take place. Pan gesture uses this view to get coordinate feedbacks.</li><li><strong>thumbView: T</strong>he view that you slide with your finger. this is connected to gesture recognizer also.</li><li><strong>destinationView: </strong>Hidden view to check whether thumb has reached destination. You can see this view at the first screenshot at the bottom as selected.</li><li>I’ve chosen to add pan gesture on xib and connected its actions and outlets as you can see on the related screenshot.</li></ul><h4>2. Implementing the pan gesture</h4><p>Related IBOutlets and variables</p><pre>var delegate: KKVerticalSliderDelegate?<br>@IBOutlet weak fileprivate var sliderPath: UIView!<br>@IBOutlet weak fileprivate var thumb: UIView!<br>@IBOutlet weak fileprivate var destination: UIView!<br>@IBOutlet weak fileprivate var thumbTopConstraint: NSLayoutConstraint!<br>@IBOutlet fileprivate var panGesture: UIPanGestureRecognizer!</pre><p>Protocol for communicating to parent view. You can understand what these methods do from their names.</p><pre>protocol KKVerticalSliderDelegate {<br>    func thumbReachedDestination()<br>    func thumbCurrentPosition(_ position:CGFloat)<br>}</pre><p>This computed property checks if thumb has reached to bottom of slider.</p><pre>var isDestinationReached: Bool {<br>    get {    <br>        let distanceFromDestination: CGFloat = self.destination.center.y - self.thumb.center.y<br>        return distanceFromDestination &lt; 1<br>    }<br>}</pre><p>Calculates translation of the thumb. Then changes the top constraint of thumb to set its new position.</p><p>After getting translation we are setting a new values to original translation considering top and bottom boundaries and also thumb size.</p><p>Because the constraint changing constantly with AutoLayout, layoutIfNeeded() is called with UIView.animate() with a tiny duration to give smooth sliding effect.</p><pre>func slideThumb() {</pre><pre>    let minY = CGFloat(0)<br>    let maxY = sliderPath.bounds.size.height - thumb.bounds.size.height</pre><pre>    var translation =  panGesture.translation(in: sliderPath)<br>    var draggedDistance = thumbTopConstraint.constant + translation.y</pre><pre>    // to prevent going up from path bounds<br>    if draggedDistance &lt; 0 {<br>        draggedDistance = minY<br>        translation.y += thumbTopConstraint.constant - minY<br>    }<br>    // to prevent going down from path bounds<br>    else if draggedDistance &gt; maxY {<br>        draggedDistance = maxY<br>        translation.y += thumbTopConstraint.constant - maxY<br>    }<br>    else {<br>        translation.y = 0<br>    }</pre><pre>    self.thumbTopConstraint.constant = draggedDistance<br>    self.panGesture.setTranslation(translation, in: sliderPath)</pre><pre>    UIView.animate( withDuration: 0.05, delay: 0, options: .beginFromCurrentState, animations: {<br>    self.layoutIfNeeded()<br>    }, completion: nil)<br>}</pre><p>Returns thumb to initial location by changing its top constraint.</p><pre>func returnInitialLocationAnimated(_ animated: Bool) {<br>    thumbTopConstraint.constant = initialTopConstraint<br>    if (animated) {<br>        UIView.animate( withDuration: 0.3, delay: 0, options: .beginFromCurrentState, animations: {<br>        self.layoutIfNeeded()<br>        self.delegate?.thumbCurrentPosition(self.thumb.center.y)<br>        }, completion: nil)<br>    }<br>}</pre><p>panAction() method connected in xib. Pan gesture recognizer triggers this function, this function decide what to do by checking states.</p><p>If state is .changed , move the thumb and communicate with the parent view. <br>If state is .ended , first check for if thumb is at the bottom then notify parent view. If not return thumb to its initial position.</p><pre>@IBAction func panAction() {<br>    if panGesture.state == .changed {<br>        slideThumb()<br>        delegate?.thumbCurrentPosition(self.thumb.center.y)<br>    }<br>    else if panGesture.state == .ended {<br>        if isDestinationReached {<br>            delegate?.thumbReachedDestination()<br>        }<br>        else {<br>            returnInitialLocationAnimated(true)<br>        }<br>    }<br>}</pre><h4>3. Implementing methods which creates the view</h4><p>In this step I will show the methods which prepares the view via getting it from xib.</p><p>Related variables</p><pre>var contentView: UIView?<br>var initialTopConstraint: CGFloat = 0.0</pre><p>These are the methods which will get the view from xib and apply it to the screen. First awakeFromNib() gets called by system. loadViewFromNib() gets the container view from xib and arrangeView() sets this view’s AutoLayout properties then adds as a subview. didMoveToWindows() is a place where I set some initial properties.</p><pre>override func awakeFromNib() {<br>    super.awakeFromNib()<br>    arrangeView()<br>}</pre><pre>func arrangeView() {<br>    guard let view = loadViewFromNib() else { return }<br>    view.frame = bounds<br>    view.autoresizingMask = [.flexibleWidth, .flexibleHeight]<br>    self.addSubview(view)<br>    contentView = view<br>}</pre><pre>func loadViewFromNib() -&gt; UIView? {<br>    let bundle = Bundle(for: type(of: self))<br>    let nib = UINib(nibName: String(describing: KKVerticalSlider.self), bundle: bundle)<br>    return nib.instantiate(withOwner: self, options: nil).first as? UIView<br>}</pre><pre>override func didMoveToWindow() {<br>    //ensure thumb always start from top<br>    self.thumbTopConstraint.constant = 0<br>    self.initialTopConstraint = self.thumbTopConstraint.constant;<br>}</pre><h3><strong>Chapter 2: How to make an </strong><strong>IBDesignable custom view which includes </strong><strong>IBInspectable elements</strong></h3><p>First of all we need to add @IBDesignable to beginning of our class.</p><pre>@IBDesignable<br>class KKVerticalSlider: UIView  {<br>    //your code ...<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2vx3V-bdn-4PKi2A3AulaA.png" /></figure><p>As you can see from the official documentation prepareForInterfaceBuilder makes our custom view alive. This method doesn’t get called in runtime it’s called only by Interface Builder. Isn’t that cool?</p><p>For interface builder we arrange our view as we are doing it for the device. contentView which is being set in arrangeView() is the container view. You can see it in 3rd step of 1st chapter. These codes makes our view visible in storyboard.</p><pre>override func prepareForInterfaceBuilder() {<br>    super.prepareForInterfaceBuilder()<br>    arrangeView()<br>    contentView?.prepareForInterfaceBuilder()<br>}</pre><p>Gather around. I have two important tricks to show you !!!</p><p>When you are overriding the methods below you should be careful. init(frame: CGRect) method helps us creating our custom view programmatically. For example I’m using it like below. If you’re getting rendering issues on storyboard check these methods first.</p><pre>override init(frame: CGRect) {<br>    arrangeView()<br>}</pre><pre>required init?(coder aDecoder: NSCoder) {<br>    super.init(coder: aDecoder)<br>}</pre><p>Also when loading your view use correct bundle. You can see the related discussion in the StackOverflow thread below. This can save you from lots of headaches.</p><p><a href="https://stackoverflow.com/questions/35700191/failed-to-render-instance-of-classname-the-agent-threw-an-exception-loading-nib/35700192#35700192">Failed to render instance of ClassName: The agent threw an exception loading nib in bundle</a></p><p>I’m using IBInspectable to make my slider corners rounded. It is very simple to implement. You can see the code below also screenshots for how to use it.</p><pre>@IBInspectable var cornerRadius: CGFloat = 0 {<br>    didSet {<br>        layer.cornerRadius = cornerRadius<br>        layer.masksToBounds = cornerRadius &gt; 0<br>    }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/691/1*w1a4E5zI6CiyVyKtrFB7ug.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/686/1*w-woMaxX4Ik4wNil6upCSA.png" /></figure><p>In screenshots you can see corner radius variable is available on Attributes Inspector on the right menu. You can live change it. You can see the differences when corner radius is changed from 20 to 35. That’s it, easy right?</p><h3>Conclusion</h3><p>I hope this <strong>loooooooong</strong> tutorial gives you enough information about IBDesignable and how to prepare a custom view. If you want to dive into the code and also see how this custom slider is applied to a view controller which can be dismissed with the action on slider, you can check out the project on my Github page.</p><p><a href="https://github.com/mkeremkeskin/SliderForDismissing">mkeremkeskin/SliderForDismissing</a></p><p>Thank you for reading this article.<br>Please add a comment if you want to add something or if you need help. <br>See ya!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f0169c2d1bdb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Distributing closed source frameworks with CocoaPods and Carthage]]></title>
            <link>https://medium.com/@mkeremkeskin/distributing-closed-source-frameworks-with-cocoapods-and-carthage-44fd141cfd78?source=rss-c94ebcb80e52------2</link>
            <guid isPermaLink="false">https://medium.com/p/44fd141cfd78</guid>
            <category><![CDATA[cocoapods]]></category>
            <category><![CDATA[framework]]></category>
            <category><![CDATA[carthage]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[M. Kerem Keskin]]></dc:creator>
            <pubDate>Tue, 14 Nov 2017 13:40:03 GMT</pubDate>
            <atom:updated>2018-03-29T14:03:52.395Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JhtO9CfH_m-FFKGjn1xZ0A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Xs44R0x0z14jicpYf5MBlA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*mVVBZYITTEkCa46pP4dVdA.png" /></figure><p>Every developer works on hard things from time to time. To achieve what we want, we read from blogs, books, Github pages (source codes, gists, issues - btw reading Github issues can be very helpful), <a href="https://stackoverflow.com/users/697467/mkeremkeskin">stack overflow</a> threads etc. We collect little information from all the source above and gather them to find a solution.</p><p>For most of us, after dealing with our problem we are done with it. But what happens if we forget, or we are not present to do it again? It’s a good practice to write about the challenges we have in the process, however how many of us does it? Most of the time we choose the lazy way 😄. Meeh I’ll remember it or I’ll find the related stuff again.</p><p>This time I will change my attitude and write the challenges I’ve had when distributing our framework as a closed source project with <a href="https://cocoapods.org/">CocoaPods</a> and <a href="https://github.com/Carthage/Carthage">Carthage</a>. I hope this post will help somebody to save their time and keep their nerve 😄</p><h4>Let’s start with listing what we are working on:</h4><ol><li>We are building a simple framework (payment checkout service in my case). For now we are doing everything by ourselves, which means our framework has no inner dependencies. (Well, if you have nested dependencies this article will not go over them, so you’ll have dig some more 😅 but believe me they are not that hard.)</li><li>Framework is written in Objective-C to support both languages. Although we are fan of Swift we have chosen to go along with Obj-C regarding some considerations. (If you have Swift framework everything can be applied as the same in this article, so no worries 👊🏻 )</li><li>We want to distribute our code as closed source. So we will be distributing our universal binary (.framework file). Universal means this baby will work on every architectures for both physical devices and simulators.</li><li>We are using Github as our host for our framework.</li></ol><p>I’m hearing that enough with the chit-chat give the real stuff, before we start I assume you all have CocoaPods and Carthage installed on your mac and familiar with them and some terminal commands (Actually I’m sure since you want to distribute your framework with some dependency manager 😄 ).</p><h3>Carthage</h3><p><a href="https://github.com/Carthage/Carthage/pull/1760">https://github.com/Carthage/Carthage/pull/1760</a> till this pull request you couldn’t be able to publish your framework as closed source. It’s relatively new since it is pushed to repo on 14th of February ❤️ this year. (such a bad day to code 😂)</p><p>Carthage manual names this process <a href="https://github.com/Carthage/Carthage#archive-prebuilt-frameworks-into-one-zip-file"><strong>archiving prebuilt frameworks into one zip file</strong></a> which is lacking some info about how to read the log etc. We will go through it now.</p><blockquote>Carthage can automatically use prebuilt frameworks, instead of building from scratch, if they are attached to a <a href="https://help.github.com/articles/about-releases/">GitHub Release</a> on your project’s repository or via a binary project definition file.</blockquote><blockquote>To offer prebuilt frameworks for a specific tag, the binaries for <em>all</em> supported platforms should be zipped up together into <em>one</em> archive, and that archive should be attached to a published Release corresponding to that tag. The attachment should include .framework in its name (e.g., ReactiveCocoa.framework.zip), to indicate to Carthage that it contains binaries.</blockquote><blockquote>You can perform the archiving operation above with the carthage archive command as follows:</blockquote><pre>carthage build --no-skip-current<br>carthage archive YourFrameworkName</pre><ul><li>Before starting, open your framework project on Xcode and clean build your project via <strong>Command</strong>+<strong>Option</strong>+<strong>Shift</strong>+<strong>K</strong>. (Clean building your folder will save your from evil things most of the time)</li><li>Open a terminal and navigate to your project folder.</li><li>Type carthage build --no-skip-current to terminal. (First I will show failed build and what to do. Then we will continue)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*blgYI0kKnHhEnQmXiFxPxA.png" /><figcaption>Failed build screenshot from terminal</figcaption></figure><p>As you can see from the screenshot, this build has failed and created a .log file to somewhere. The message shown in the terminal is not enough to understand what the actual error is. To see the .log file you can use</p><pre>open -e /var/folders/db/7hhdsv416kq6693n6s5dds8m0000gn/T/carthage-xcodebuild.DsjNvZ.log</pre><p>After figuring out and solving problem build it again. (in my case it was about a build script I was using which I will mention in CocoaPods section.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PPTM1uBclLWgWTzFz_RIvg.png" /><figcaption>Successful build</figcaption></figure><p>If you see this in your terminal that means you have successfully build your framework. YAY! Now we can go to other step.</p><ul><li>Type carthage archive YourFrameWorkName to terminal.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QmGwB1qkSPoN3mFL1yclYw.png" /><figcaption>Archive completed</figcaption></figure><p>That means Carthage have archived our framework and created a .zip file which will be used to fetch our framework from Github.</p><ul><li>These .zip files are distributed using Github release attachments. Create a release with your framework version and add this .zip file as an attachment.</li><li>From now on your framework can be fetched by using</li></ul><p>github “YourCompany/YourFramework” &gt;= YourReleaseNumber</p><h3>CocoaPods</h3><p>In CocoaPods for archiving the framework you need to make some changes to your project. Carthage does this operation by itself as you can see in previous section.</p><ul><li>First set Skip Install setting to <strong>NO </strong>in Build Settings of the project.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8Z_WHmUudzSSGEqLq4Rx6Q.png" /><figcaption>Build Settings -&gt; Skip Install -&gt; NO</figcaption></figure><ul><li>If you try to archive right now the output framework will not work on iOS simulators. We have to add binaries for iOS simulators too. It is called <strong>fat</strong> framework. There is no easy way in Xcode to archive framework for all architectures and devices. We need to show our magic with external scripts. (External scripts is one the most annoying things about iOS development in my opinion 😄)</li></ul><p>At the earlier stages of developing our framework I was using external build script as a Run Script in Build Phases. But this script requires building while selecting <strong>simulator as target</strong>. Carthage is using <strong>Generic iOS Device </strong>as target. So my script was failing. You can see the script in the gist below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8db90f0b812b3745f34b112171ec9064/href">https://medium.com/media/8db90f0b812b3745f34b112171ec9064/href</a></iframe><p>Instead of using Build Phases I’ve found <strong>Post Actions Scripts</strong>. You can open post actions via <em>Edit Scheme -&gt; Archive -&gt; Open Archive Settings -&gt;Post-actions. </em>You can see the script and where you can set it below. Add the script below to your post action as in screenshot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Bu0iW0029T3duhWguAGXag.png" /><figcaption>Post-actions</figcaption></figure><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/32f99e3447e8b6a09b1df86b990be76b/href">https://medium.com/media/32f99e3447e8b6a09b1df86b990be76b/href</a></iframe><p><strong><em>Update: Few months later after this post I couldn’t be able to archive my framework with simulator architectures (i386 &amp; x86_64). After some digging I found out that I was trying to build it with iPhone 6 which you can find in the script in step 2. And iPhone 6 was not available in my XCode (I remember I deleted it). Then I’ve changed it to an available one (which is iPhone 8 in my case). Now it is working. If you are dealing with such case please consider this update.</em></strong></p><ul><li>Finally you can archive your framework. Select a <strong>real device </strong>then click Product -&gt; Archive. After archiving, the script will open a Finder window to your framework directory.</li><li>Now you can start configuring your Pod. First zip your framework with the license to use it in your .podspec . Add your license file to directory where your framework sits. Then open a terminal window in this directory. Prompt the line below to terminal.</li></ul><pre>zip -r YourFrameWork.zip LICENSE YourFramework.framework</pre><ul><li>Your .zip file is ready. As we have done it in Carthage, put this .zip file to your Github release as an attachment.</li><li>Lets modify your .podspecfile.</li></ul><pre>Pod::Spec.new do |s|  <br>    s.name              = &#39;YourFrameworkName&#39;<br>    s.version           = &#39;0.0.1&#39;<br>    s.summary           = &#39;Your framework summary&#39;<br>    s.homepage          = &#39;<a href="http://example.com/">https://yourcompany.com/</a>&#39;<br><br>    s.author            = { &#39;Name&#39; =&gt; &#39;you@yourcompany.com&#39; }<br>    s.license           = { :type =&gt; &#39;MIT&#39;, :file =&gt; &#39;LICENSE&#39; }<br><br>    s.platform          = :ios<br>    s.source            = { :http =&gt; &#39;link to your cocoapods .zip attachment&#39; }<br>    s.source_files      = &quot;add your header files which would be public&quot;<br>    s.ios.deployment_target = &#39;Your deployment target e.g. 8.0&#39;<br>    s.ios.vendored_frameworks = &#39;YourFramework.framework&#39;<br>end</pre><p><strong>s.source</strong>: link to your .zip file. In this article we are using Github so it is a link to .zip file which is inside your Github release.</p><p><strong>s.ios.vendored_frameworks</strong>: path to your framework in .zip file pointed by the <em>s.source</em></p><p><strong>s.source_files</strong>: header files for the framework for developers to use</p><ul><li>Last thing to do is to registering to CocoaPods trunk and publishing the pod.</li></ul><pre>pod trunk register yourname@yourcompany.com &#39;Your Name&#39;</pre><p>After this you will receive an confirmation e-mail from CocoaPods. Confirm it.</p><p>Open terminal and navigate to your framework directory where your .podspec file sits. Type the command below.</p><pre>pod trunk push YourFramework.podspec</pre><p>That’s it. Framework is published to CocoaPods 🎉.</p><p>So you have your framework both publish to CocoaPods and Carthage as closed source. You can maybe think what we have done is complex or maybe boring but this is the way it’s done 🙂.</p><p>I know It’s kind of a long post but I’ve tried to be as much as clear. I hope this helps somebody and makes his/her life easy.</p><p>Thanks for reading.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=44fd141cfd78" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>