Xcode: increment build number per configuration

In your current Xcode project, you might have different configurations and certain configurations might have different product bundle identifier, say you have a product id for staging, another for production, and another for development.

When you push your build for TestFlight for instance you have to update the build number or the version, if you have another app for staging say com.myapp.com.staging and you use agvtool or fastlane action increment_build_number which uses agvtool , the new number will be set for the whole configurations and you will find that your staging or production builds in TestFlight have no clear convention.

we can use this script as a fastlane lane .

desc("increase the current_project_version config based on configuration")
    lane :increase_build do |option|
        
        fastlane_require 'Xcodeproj'
        
        project = "../#{urProjectName.xcodeproj}"
        target = <#targetName#>
        buildConfiguration = <#release_configuration_name#> 
        CUSTOM_BUILD_NUMBER = ''
        
        project = Xcodeproj::Project.open(project)
        project.targets.each do |mtarget|
            if mtarget.name == target
                mtarget.build_configurations.each do |mbuild|
                    if mbuild.name == buildConfiguration
                        CUSTOM_BUILD_NUMBER = mbuild.build_settings['CURRENT_PROJECT_VERSION']
                        mbuild.build_settings['CURRENT_PROJECT_VERSION'] = CUSTOM_BUILD_NUMBER.to_i + 1
                    end
                end
            end
        end
        project.save()
    end

to get the build number for configuration you may use almost the same script like this

lane :get_build_number_for_configuraiton do |option|
        fastlane_require 'Xcodeproj'
        
        project = "../#{urProjectName.xcodeproj}"
        target = <#targetName#>
        buildConfiguration = <#release_configuration_name#> 
        CUSTOM_BUILD_NUMBER = ''

        project = Xcodeproj::Project.open(project)
        project.targets.each do |mtarget|
            if mtarget.name == target
                mtarget.build_configurations.each do |mbuild|
                    if mbuild.name == buildConfiguration
                        CUSTOM_BUILD_NUMBER = mbuild.build_settings['CURRENT_PROJECT_VERSION']
                    end
                end
            end
        end
        CUSTOM_BUILD_NUMBER
    end

In the info.plist file you need to set the value $(CURRENT_PROJECT_VERSION) for the key Bundle version

Versioning system in build settings should be set to Apple generic.

And make sure in the General tap the build number has a value so Xcode does not complain.

it can use some refactoring , but that should do it.

Enjoy.

Deploying through bitbucket pipeline

bitbucket pipelines is a great feature and it can facilitate a lot of work, I won’t say its better than Jenkins or other CI tools, but I like how simple it is.

Here I will be talking about deploying a PHP api codebase into a server, it could be your production code , staging, etc…

The problem I faced is that I wanted to work locally on my php project without worrying about deploying the code into the public server, though you can use netbeans syncing feature, but that means every change you make will be deployed directly without testing, also it would be hard to avoid conflicts if you are working with other developers on the same codebase. Another way to solve it, is to use git, where you push your code and then go to the server and pull your code there, but as you see here , you need to go to the server and pull your code manually.

So the idea here is that whenever I push some code into the master branch or certain branch(s), I want my code to be deployed into the server as fast as possible, without shutting the service for long time.

Bitbucket pipeline can help us tremendously here , as we can trigger the pipeline script(s) on certain triggers (manual, schedules, on commit ), check https://confluence.atlassian.com/bitbucket/configure-bitbucket-pipelines-yml-792298910.html for more info.

after the pipeline is triggered, a certain script will be executed, and there where the work is done.

the pipeline file is a YAML file (https://en.wikipedia.org/wiki/YAML), which is really convenient to use, before we start scripting we need to enable pipeline for our project, so go to settings -> Pipelines -> settings -> click on enable pipeline switch. You will find a button saying configure bitbucket-pipelines.yml, click it.

you will see a page asking you to choose a language template , I will be using php here.

you will see an editor where you can edit the yaml file and on the side there is pipelines templates sections where you can find already made scripts you can use, like deploying to Amazon s3, firebase and so on.

For me I am going to deploy my project through ssh, you may use SFTP here and there is an already made pipeline called SFTP-deploy that you can find in the pipelines templets, but if you have a non trivial code base , it will be slow to move all the files on each commit/file change!!.

So am not going to use any pipeline and stick with the default one as starter. To transfer the data over ssh , am going to use Rsync tool, which is the one used by netbeans to sync your files with the remote server quickly since whenever you make some changes like adding new file or deleting one , rsync can sync only the updated files and does not copy the whole codebase all over again as if you used SFTP-deploy or FTP-deploy pipelines.

you can read more about rsync here “https://en.wikipedia.org/wiki/Rsync” and here https://linux.die.net/man/1/rsync

ok the pipeline code


--- 
image: "php:7.2.13"
pipelines: 
  branches:
    master:
      - step:
          name: "deployment"
          deployment: "staging" 
          caches: 
            - composer
          script:
            - echo "Starting Staging deplyment from master"
            - apt-get update
            - apt-get -qq install sshpass
            - "apt-get update && apt-get install -y unzip"
            - "curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer"
            - "composer install"
            - sshpass -p $FTP_PASSWORD rsync -are "ssh" --delete ./* [email protected]$HOST:$HOST_PATH --exclude runtime/

So in the pipeline configuration file here we ask to download php docker image and use it (it might need to match the one on your server), you see there is branches and under it staging, this means that when new code is updated on the branch named “Staging” run next steps , and we have 1 steps here , the first which will install dependencies and move the code from the bitbucket CI into our server. Note that none of these commands is actually executing on our server.

note that we install sshpass tool , since where moving a the files here using ssh with password , and rsync will prompt you to ask for a password in the terminal , but the script running on the CI is not interactive so we use it to overcome that hurdle.

you see that we use something like $FTP_PASSWORD, these are called repository variables and you can set them from settings->pipelines->repository variables and the cool thing about it is that you can have secured variables for something like password, so you don’t have to put the password in a plain text in the repository.

--delete : is used to be able to delete files when deleted by the source , and --exclude runtime/ means we don’t want to sync this directory since its would be a place for things like logs, and cache and we don’t want to tamper with the web service logs.

so that’s basically it.

hope its been useful for you, took me days to figure it out !^_^.

disclaimer: this is tested on small/medium size monolithic projects only and not on production, so do your testing/research and be careful. I am just sharing my experience, but please let me know if you have suggestions or rectifications.

Protect your project from dependencies (iOS – swift)

The Issue

Here I will talk about some ways I use to protect my projects against changes in its dependencies.
Whats clear is that every library/framework you use in your project would change at some time, and sometimes we change our mind about these dependencies and replace them with others.

Lets first talk about replacing a library. So we have some library that filter an image, lets call it MOImagesFilter, where you provide it an image and what filter you want to use and then it returns the filtered image. Later you find that this library produce a couple of bugs or does not work well with orientation or 3x screen …. And you decide to use a more famous library called e.g. FastImageFilters! and try it.

But wait …

You already imported MOImagesFilter in 10 classes , and in each class there is a couple of lines related to it. FastImageFilters may have totally different APIs and eventually you find yourself updating 30 lines of code !!! Oo

Adapters to the rescue

The idea here is to point all those calls to one class thats more native to our project where you don’t event need to import it in other classes. That class will be responsible to translate the library we intend to use into one formatted language our project understands.

So we create a class lets call it ImageFiltersAdapter that deals only with MOImagesFilter and does what it does , so we don’t have to deal directly with MOImagesFilter directly at all.
We predefine a set of functions that we actually need from the library , we might not need all of the function , in our case we only needs a function where we give it an image with the intended filter and it returns a filtered image.

Later on if we decide to use FastImageFilters we only change in the adapter class , not all the e.g. 3o lines of code and 10 classes.
Another benefit is that we might not need all the functionalities from some library but just a subset.
Also anyone including you when later tries to understand what we are doing here with the filter class will understand easily instead of going to the original library and read through it.

This not only useful when we replace libraries but also when we update a library where its APIs may have changed.

Adding CocoaPod for your library (public/private)

Adding CocoaPod for your library (public/private)

MAC OS, iOS

This post takes you step by step into how to integrate cocoaPods with your library.

CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects

Am going to use an open sourced project I made before on github, and add the cocoapods into it. For you to keep up with me.

  1. Create a repository on github.

You need to Sign up first before you can create new repository. Click on the upper right plus button and choose New Repository. For Reference Lets call it YourPod.

The name should be unique, and make sure to add a license file.

2. Clone the repo

clone the repository for github using their App or through command line

git clone https://github.com/<YourAccountName>/YourPod.git

3. Install Cocoapods

From the terminal type

sudo gem install cocoapods

4. Structure Folders

cd to the folder where you cloned YourPod repository.

Let’s create 2 folders one called YourPod, and the other called Demo.

  • Add your library swift files under YourPod
  • Create an xcode project under Demo Folder
  • Commit Your Changes
  • Push them to github

CREATE THE POD

Before creating the pod. From your repository github page.

  • Click on releases
  • Click on Add release button
  • set the version to be 0.0.1 (this is important, you can set it to any number you want )
  1. Create podspec

From terminal, type

pod spec create YourPod

that will create a podspec file. Open it using xcode or any editor you like.

After you open it, it will look like this ( after removing comments and leaving most of the required attributes)

Pod::Spec.new do |s|
s.name = “YourPod”
s.version = “0.0.1”
s.summary = “A simple HUD to indicate activity, success, and failure.”
s.description = <<-DESC
You can use this Library as a loading indicator while waiting for response from api, you can mention failure and success as well
DESC
s.homepage = “Your project home page "
s.license = { :type => “MIT”, :file => “LICENSE” }
s.author = { “Your Name” => “Your Email” }
s.social_media_url = “http://twitter.com/dark_torch(can be any link)"
s.platform = :ios
s.source = { :git => “https://github.com/YourAccountName/YourPod.git", :tag => s.version.to_s }
s.source_files = “YourPod/*.swift”
s.resources = “YourPod/*.storyboard”, “YourPod/*.xcassets”
s.requires_arc = true
# s.dependency “JSONKit”, “~> 1.4”
end

The license file should be available on the remote repository (e.g. github repo) and belongs to the release you mention in the podspecs file

2. Validate the podspec

From terminal run

pod spec lint YourPod.podspec --verbose

On success you will get “YourPod.podspec passed validation.”, watchout for any warning or error* — verbose is for more detailed logs which should help you if there is any.

3. register the pod in the CocoaPods repo

Now we have our pod validated, we need to push it into cocoapods so others and we can use it. To do that first we need to declare ownership. Simple run this command in terminal from the same directory of the pod.

pod trunk register <YUOREMAIL> ‘<YOUR NAME>’ — description=’owner from macbook pro’

you will get this response

[!] Please verify the session by clicking the link in the verification email that has been sent to <your email>

you should get an email with the validation link within a minute.(It might goes to the spam folder)

4. Upload your pod

Now all we need is to push our pod, simple use this command from terminal.

pod trunk push YourPod.podspec

you should get “ — March 16th, 04:08: Push for `Your pod’ has been pushed (2.792570047 s).” alike message in the terminal.

“if you added your twitter account in the podspec, you will get mentioned in an auto-generated cocoapod new pod tweet ”

TRY IT

Now lets try it

  • Create a Podfile in the same directory where your demo.xcodeproj is.

Your pod may look like this

platform :ios, "8.0"
use_frameworks!
target :Demo do
pod "YOURPOD"
end

if your pod in swift you need to add use_frameworks!

now run

pod install

It will download the pod and create an xcworkspace for you .

Now use demo.xcworkspace , import the pod into your demo and try it.

PRIVATE PODS

you can use a private pod as well although you need few more steps.

  1. Create your own specs repository (where the pod’s podspec file is going to be hosted) (lets call it myspec)
  2. create new folder locally and copy the pod you made before and podspec file.

3. Create new repository for your pod on e.g. bitbucket and upload the files like we did before don’t forget to tag it, you can do it by running

git tag 0.1

and push the tags using

git push -u origin —- tags

4. Add Your specs repository

pod repo add <yourSpecsName> <yourPodRepoDirectory>
e.g.
pod repo add myspec https://bitbucket.org/moath/mospecs

5. Now in your podspec file change the version and source url (it’s better to use https) (Validate as we did before), then push it using >

e.g.

pod repo push myspec MOHUD.podspec — verbose

6. finally in the Podfile add source to refer to your specs repo.

e.g.

source "https://bitbucket.org/moath_torch/mospecs/"
source 'https://github.com/CocoaPods/Specs.git'
##Note: you need to add all resources

Thats should be it .

WRAP UP

Now your Pod should be searchable in https://cocoapods.org/ (if its public)

a nice thing about it is that they will rate your pod based on some metrics like documentation, it worth looking at it and trying to improve your rate.

I hope this helps someone, if you have any comment please add it.

And Thank you for reading.

Links

you can find more detailed info at Cocoapods

Lesson Learnt : UItableView Header always appearing

This is just something I faced tonight

I have a UITableView with PLAIN Style

in the dataSource the sectionNumbers is “1” I swear !!

when I run it into simluator the tableView is shifted down by some 20 pixils !!

The first thing came to my mind is to go and check it on the Interface builder
and from the ‘Size inspector” there is a property called ‘section height ‘ which set by default to ’22’
So yay I just need to change it to 0 and thats it, and thats what I did :
I typed 0 in the field and pressed a BIG Enter problem SOLVED 😀 .. but wait !!!
its not set to 0 !!, instead its 1 …tried again , maybe i just missed the zero the first time !!
but it insists to not to leave the ‘1’ , well ‘I said’ nobody cares about the extra pixil down .
ran it and everything looks OK … or better at least …
my freacking “consciousness” kept tingling me :/ ‘ I need to fix it ,and whyyyyy this happens’

So I took a look at the code and I found that this class is a subclass of another that handle the tableView Delegate ( srsly don’t do alot of subclassing , things may become messy )
SOO I found this code somewhere

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
    
    UIView *header = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 0)];
    
    return  header;
}

mmmmm looks I was trying to add some section header but then I didn’t, but wait !!
ok there is a bad non necessary method here but the ‘height’ is zero and there is no really a header …
although am getting the 22 height empty header on screen !!

srsly I dunno , but deleting this method removed the 1 pixil header … 🙂

OK OK I went to the ducomentation and I read this ( again ‘consciousness’)
This method only works correctly when tableView:heightForHeaderInSection: is also implemented.”
only !! 
so looks if we implemented the ‘viewForHeaderInSection’ no matter what object it returns the ‘section Height’ will be set to 22 as a default value .. unless we implement the  ‘tableView:heightForHeaderInSection: ‘ method WHICH I did not …
but why in the size inpector I couldn’t set it to 0 !!! .. 

Well, I guess it will remain as a mystery .. one little mystery for tonight.. 🙂

P.S. for some reason there is no spelling checker and yeah there will be speeling errorss

Presenting ViewController from UIAlertView (Attempt to present on which is already presenting )

Presenting ViewController from UIAlertView
Something I face this morning which was annoying ,is That I was trying to present a ViewController
After user Tap on a UIAlertView button.
The result was this console log !!
uialertview Warning: Attempt to present <_UIModalItemsPresentingViewController: > on <_UIModalItemAppViewController:  > which is already presenting”
The Code I was using looked like this : 
UIAlertView * alert =[[[UIAlertView alloc] initWithTitle:TITLE
message:MSG
delegate:Dgt
cancelButtonTitle:@“OK”
otherButtonTitles:nil] show];
And in The AlertView Delegate  I called presentViewController
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    
     MOImageViewController    *_imageVC =[[UIStoryboard storyboardWithName:@”Main” bundle:nilinstantiateViewControllerWithIdentifier:@”imageScreen”];
                    
                    [_imageVC setImageURLString:urlString];
                    
                     
                    // topViewController  
                    [[self topViewController] presentViewController:_imageVC animated:YES completion:Nil];

}
What turned out is that UIAlertView is treated like viewController and upon clicking at one of its Buttons it gets dismissed, So in the previous code am presenting a new viewController while dismissing the AlertView Which gives me the mentioned warning .
The simplest solution for this is simply using another AlertView Delegate which is 
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{
 // presenting view controller here safely
Thats it 🙂