Using data semphores in iOS (Synchronous Request)

Introduction

As per Apple Documentation,

A dispatch semaphore is an efficient implementation of a traditional counting semaphore. Dispatch semaphores call down to the kernel only when the calling thread needs to be blocked. If the calling semaphore does not need to block, no kernel call is made.

Continue reading “Using data semphores in iOS (Synchronous Request)”

Swift – Optionals

Introduction

With the introduction to a new programming language known as SWIFT in the past year by the biggest technical giants Apple Inc, most dominant player in the mobile market, a new stage was set for developers to come up with more. I will agree that Swift is an easier platform to learn for all the developers though specially who are new to iOS development platform but shifting from Obj-C is not easy particularly not using the (*) pointer notation [Just kidding..].

Even if you are new to iOS there are lot of concepts that are not so friendly to understand and pretty much creates a confusion while learning and using the language basics.

One of most limbo concept is Optionals. It took me really good time to understand what it is all about specially when I was coming from the Obj-C background.

As it is said, there are 2 sides of the coin. Here to its has pros as well as cons. On the cons, the highly relative term is complexity. Trust me if you do not understand why ? or ! operators are used you will land in dead zone.

Let’s start with the basics.

Continue reading “Swift – Optionals”

Start up with Laravel 4 – Installation and Configuration Mac/Linux

Lately i thought of diving into Laravel which in recent days has gained quite a popularity among the developers community. I will cover the basic installation steps of getting Laravel setup to work efficiently on the machine along with the Homestead environment. The steps as per the Laravel website seems to be very easy but developer faces a lot of issues while getting it to work properly. So lets begin.

Introduction

Laravel is a PHP based framework used for developing great applications over web. It consists of various features similar to other frameworks but are very well organized compared to other competitors in the market. Continue reading “Start up with Laravel 4 – Installation and Configuration Mac/Linux”

Using Blocks in Objective C

Introduction

When programmers and developers from the other high level languages such as Java and other similar ones shifts to Objective C, Blocks is one of the most confusing concept. But trust me its the most important concept in Objective C which is extensively used in Objective C style programming. Moreover once you know the basics its becomes really easy to use it. Continue reading “Using Blocks in Objective C”

Using Git – Common Terminal Commands

Hi ,

As we know in current industry one of the most important aspect of development cycle is the use of code version tool.

We have different options in the industry, the most common one are :

  • SVN
  • GIT

Both of these have enormous number of users in the industry, whereas GIT is coming out to be a more preferred choice for developers across the globe. This post is just a basic kickabout stuff that will be required to get you started with GIT over Linux/Unix, A set of commands commonly used for operating GIT over the terminal which has been my preferred choice rather than the tools that are available with a GUI.

So lets get started.

Continue reading “Using Git – Common Terminal Commands”

Using NSUserDefaults in iOS

Hi All,

I know that most of the people reading the post are familiar with the concept of NSUserDefaults.

Just to summarize, NSUserDefaults is a programmatic interface to interact with the user system. It allows the application to store and access the data that is specific to the user.  So lets just quickly see how to access and store the data specific to the user in the iOS application context.

Continue reading “Using NSUserDefaults in iOS”

Getting Started With RequireJs

Before I begin to jot down the concepts of RequireJS, Let me just specify this post is for the folks who are having a hard time learning this framework. In today’s world of development whether its a web or a mobile application everything is Performance. Performance which has become a cornerstone for the development, a lot of developers faces the problem to optimize the performance of the applications. For all those RequireJS is the answer. So lets begin.

What is Require JS ?

As per the definition provided by the RequireJS folks,

RequireJs is Javascript file and module loader.

RequireJs as per the line specified above, is not only the solution to load files but also helps to maintain our code and add modularity to Javascript which is not a part of the Javascript Library.

CornerStones of RequireJS

When I started to learn this library, I really had a hard time to find the answers which allows me to understand the core concepts. I did Google a lot and at the end of it was able to list the main concepts that helps to understand and use the framework as such. All the features of the library revolves around these three concepts:

  • define()
  • require()
  • require.config()

We will see the use of all three in just a moment. I always believe that its better to showcase the concepts then writing a lot theory about it. It helps the readers to understand the concepts as well as allow them to grasp the concepts exceptionally well. I am going to take a very simple example which is going to explain the concept of the three main components listed above.

How To Structure The Project

As per the RequireJS folks, they expect you to have all the files inside a script folder located in your project structure. Donot dive to deep into it otherwise reference to your files will become a bit trouble some.

Screen Shot 2014-05-26 at 1.12.47 AM

As per the image above, I am keeping all the framework files in the framework folder. Note that I have removed the version numbers from the files keeping it simple. Best Practices says that you should not include the version number with the js files instead to keep a text file along with it to have a reference to it.

Lets understand the files that are there:

index.html : As the common convention, this file initiates our application.

main.js : This is main file which is required for the require js configuration and loading the start module for the application to load all the dependencies.

jquery-private.js: This file defines a module that returns a jquery object to be used in our application code.

jquery.js: Jquery Framework file.

require.jsRequireJS Framework file.

Adding RequireJS

Its very easy to add RequireJS to the code. You can add RequireJS as another javascript library but remember there are no other libraries that are added along with it to avoid conflicts on the main html page.


<script src="js/frameworks/require.js" data-main="js/main"></script>

Lets understand the main part of this code fragment.

This code adds the RequireJS to our project along with a data-main attribute.

data-main: This attribute specifies the location of the main javascript file which consists of the configuration options along with the startup module.

Note that the data-main attribute doesnot require the .js extension along with the file. The extension is added automatically by RequireJS library.

Remember more or less we donot require to explicitly tell the RequireJS library for the JS extension. All the files with the extensions as JS are added automatically to the files, we will see that in a moment.

Main Javascript File

Lets lay down the configuration options and initialization module for the application. We will load the jQuery as the dependency in our application for some DOM manipulation.


require.config({
 baseUrl:"js/frameworks",
 paths:{
 "jquery-private":"../jquery-private"
 },
 shim:{
 'jquery':{
 exports: 'jquery'
 }
 }
 });

Require JS loads the configuration using require.config(). It consists of various options that I am not going to cover in this post. I will cover the basic options that are more or less required while defining the config for the application using RequireJS. So lets see the options one by one specified above.

baseUrl: the root path to use for all module lookups. We can opt not to define this option in the configuration.

In such a case the baseUrl is by default the page in which requireJs is loaded. If the data-main property has been used then the baseUrl is the path used in the data-main attribute.

paths: These are the mapping that are not found under baseUrl. Its not required in case you have all the paths in the same directory as the baseUrl. But its preferred to specify this option as it helps to define the alias for the libraries.

shim: It configures the dependencies, exports and custom initialization for the older/traditional brower globals that do not use define() to declare dependencies and set a module value.

 Defining the Module

The module in the requireJs is defined using the define() function. We need to just remember a thumb rule that define always returns something back to the script. These modules are the reusable code that we can use in the application to write the dependent scripts.

We can use define the following ways:

Simple Name/Value pairs


define({

name:"Codemaster",

snippet: "Gabriel"

});

As you can see that there are no dependencies in this case, So we can pass an object literal in such a case for the module. If you note that we are not returning any value in this case. Its an exception here where we are just defining the module using the object literal. This can be helpful if you are keeping a group of preferences in the application or using it to have some static data that is to be reused by the scripts.

Definition Functions


define(function()

{

//Some code work

var obj1 = {

name: "Codemaster"

snippet: "Gabriel"

}

return obj1;

});

Here as you can see we are defining a module with no dependencies and returning an object at the end of it. This is helpful when we have to write a code snippet which has no dependencies to the libraries and is self existing set of code.

Definition Functions with Dependencies


define(["dep1"],function(dep1){

var obj1 = dep1.function1();

return obj1;

});

You will be having this kind of a scenario in the majority of the applications. Here this module have a dependency on Module dep1. So we load the dependency and pass it as an argument of the function.

Please Note: The order of arguments should be same as that of the dependencies that are listed in [].

Now lets see a custom definition of the module in our code that we are referencing to make changes to the DOM element. Here we create a new module as jquery-private which consists of the custom code and it returns a function object.


define(['jquery'],function($){
var contentObj = {
'appendText': function(){
$("#testid").append("<br/>Content changed by jquery-private Js file");
}
};
return contentObj;
});

This will be saved in the jquery-private.js file for the reference. As you go back to the configuration options we can see that we have loaded this file in the path object for the configuration.

Calling require() to load modules

Now the last step is to call the require() function to initiate the process to load the module. Lets add this code to our main.js file to initialize the process to load modules.


require(['jquery-private'],function(jqPr){
jqPr.appendText();
});

require() function helps to load the modules that are defined within the application. We can load as many modules as we want as dependencies but the best part is we can choose which modules to load which alternatively reduces the payload in the application for script files.

There is one more thing that we need to remember while using requireJs. The main file for the application is loaded asynchronously. Also the load time for loading the main js file will be more than the modules that are to be loaded as it does not finish loading till application has finished loading the required modules.

So that’s it. You are ready to use the RequireJs in your application.

To refer to the example code you can visit the following link to download the source code files. Download Here

I hope that you enjoyed the post.

Exchanging data using Delegates in iOS

Introduction

The delegates are quite a powerful concept when it comes to exchanging data in iOs or query some other class without knowing much about it. This post will cover the process to create a simple delegate and how to make it work to exchange data between different view controllers.

Delegation Pattern:

As per wikipedia, The delegation pattern is a design pattern in object-oriented programming where an object, instead of performing one of its stated tasks, delegates that task to an associated helper object.

Somehow it also allows us to implement the concept of Multiple Inheritance in iOS by allowing helper class to perform operations on behalf of the base class.

Summary

The post consists of an example which consists of the following files:

CMGViewController: Base Controller which come to the view when the user loads the application. It implements the CMGViewControllerDelegate to perform an operation on behalf of CMGSecondViewController.

CMGSecondViewController: This is a view controller which asks for the user input. Once the user input the data to control and submits it, the data is displayed on the control is send back to the CMGViewController along with the input added by the user.

Download the full source of the example from here: DOWNLOAD SOURCE

Lets begin with the more interesting stuff and understand how the delegation pattern works.

Code

Step 1: Open Xcode and create a new project with Single View Application selected.

Step 2: Add a view controller to the application.

Step 3: Open the .h file for the controller


@protocol CMGSecondViewControllerDelegate <NSObject>

-(void) submitButtonActionPerformed:(NSString *) stringValue;

@end

#import <UIKit/UIKit.h>

@interface CMGSecondViewController : UIViewController
@property (strong,nonatomic) id<CMGSecondViewControllerDelegate> delegate;
@property (strong,nonatomic) IBOutlet UITextField *departmentTextField;
@property (strong,nonatomic) IBOutlet UIButton *submitButton;

-(IBAction)submitButtonPressed:(id)sender;

What we have here?

We have created a new protocol for the application with the specified name which our main view controller will implement to perform operations on behalf of our second view controller. We have declared a method here which returns nothing but takes a NSString as an argument.


@protocol CMGSecondViewControllerDelegate <NSObject>

-(void) submitButtonActionPerformed:(NSString *) stringValue;

@end

We have an instance of UITextField and UIButton which allows user to interact with the application. The user types in the data to the textfield and submits it. The data is accordingly displayed on instance of UILabel in main view controller.

Now we have to declare a property here with the type as the delegate name.


@property (strong,nonatomic) id<CMGSecondViewControllerDelegate> delegate;

Remember you have to connect your controls to the Storyboard View. I leave the connection stuff to you as I pre assume that you will be able to do that easily.

Step 4: Switch to your .m file for the controller and add the following code to it.


@synthesize departmentTextField;
@synthesize submitButton;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

// Submit Button Action Performed
-(void) submitButtonPressed:(id)sender
{
NSString *textFieldValue = [self.departmentTextField text];
[self dismissViewControllerAnimated:YES completion:^{
[self.delegate submitButtonActionPerformed:textFieldValue];
}];
}

Above code consists of an action method that is performed when the user hits the button on the controller view. It dismisses the current dialog and passes the value to the first view controller to display in the label instance. We have to make a call to submitButtonActionPerformed: method on the delegate instance to call the implementation in the main controller file. This code is added in the completion block of the dismissViewControllerAnimated: method.

Step 5: Now switch to your main controller .h file and add the following code for your delegate pattern to work correctly.


#import "CMGSecondViewController.h"

@interface CMGViewController : UIViewController <CMGSecondViewControllerDelegate>

@property (strong,nonatomic) IBOutlet UILabel *departmentLabel;

Here we add the protocol that we defined in the second view controller file in between the angle brackets so that we can access the methods and provide an implementation of it in the .m file.

Step 6: Add the following code to .m file. This consists of  implementation of protocol methods as we generally do in iOS.


//
//  CMGViewController.m
//  DelegateExample
//
//  Created by CMGabriel on 03/12/13.
//  Copyright (c) 2013 Example. All rights reserved.
//

#import "CMGViewController.h"
#import "CMGSecondViewController.h"

@interface CMGViewController ()

@end

@implementation CMGViewController

@synthesize departmentLabel;

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
CMGSecondViewController *secondViewController = (CMGSecondViewController *)segue.destinationViewController;
secondViewController.delegate = self;
}

#pragma mark - CMGSecondViewControllerDelegateMethods
-(void) submitButtonActionPerformed:(NSString *) stringValue
{
self.departmentLabel.text = stringValue;
}

@end

Step 7: Run the application to view the delegates do the working. Whatever you will specify in the textfield in the second view controller will be displayed in the main view controller on hit of a button.

So we are done with the show. In hope you enjoyed it !!!. Keep looking forward to more posts.

iOS Access webservices using NSURLConnection

Introduction

With geometric rate of progression in the mobile technology, almost each application is required to exchange data with the server. This aspect of an application is achieved using web services. Web services act as a middle ware between your server and front end client. This post will help you to access the webservices data using the predefined class in iOS framework i.e. NSURLConnection. This example demonstrates the application to connect with webservices SYNCHRONOUSLY. Will guide you to connecting with webservices using asynchronous connection in a separate post. So Let us begin!!!

PLEASE NOTE: You can download the whole source code from here – DOWNLOAD CODE

We will take a simple example to demonstrate this up.

The example consists of a TableView embedded in a NavigationController. The application connects to a public web service, in this case RottenTomatoes.com and loads the data to the table view as required. You will be required to signup with  website to generate an API Key and run the example code successfully.

Summary:

Main.storyboard – This file is a storyboard file for iOS which consists of a NavigationController with a table view embedded to it.

WSViewController.h – The header implementation file consisting of definitions to run the code successfully.

WSViewController.m – The implementation file consisting of the code to handle the request and accordingly display data to the user.

WSAppDelegate.h & WSAppDelegate.m  – The AppDelegate files for the application to manage the workflow and correct functioning of App.

Code Time:

Step 1: Prepare your application views to handle the code in the implementation file. With the introduction of storyboads in iOS 5 it takes very less effort to maintain the files for the View part of application.

Delete the view controller already created by default by Xcode.

Drag a TableViewController instance to the view and drop it.

Select the instance, go to Editor > Embed In > Navigation Controller. This will embed you current view into a NavigationController and making NavigationController as the InitialViewController.

Step 2: Refactor your code in .h file. Extend your class to UITableViewController and implement UITableViewDataSource, UITableViewDelegate and NSURLConnectionDataDelegate.

@interface WSViewController :
UITableViewController<UITableViewDataSource,UITableViewDelegate,
NSURLConnectionDataDelegate>

Step 3: Switch back to your main storyboard file.

Add the class name to property inspector.

Screen Shot 2013-12-01 at 6.04.30 pm

Step 4: Add the property declarations to the .h file

//Stores the values for the data that is returned from the webservice.
@property (strong,nonatomic) NSMutableData *wsData;
//Stores the values for the final results in an array.
@property (strong,nonatomic) NSMutableArray *results;
@property (strong,nonatomic) NSMutableDictionary *jsonData;

Now, time to add the code for connecting and rendering the data.

Step5: Switch to .m file.

Synthesize the properties declared in .h file.

@synthesize wsData;
@synthesize results;
@synthesize jsonData;

Now add the following code to your viewDidLoad to connect to the URL synchronously and fetch the data.

//Initiate the variables to store data to be used in the application.
self.jsonData = [NSMutableDictionary dictionary];
self.results = [NSMutableArray array];
self.wsData = [[NSMutableData alloc] init];

//Create the URL String using URI and API Key
NSString *uriString = [NSString stringWithFormat:@"%@%@",URI,API_KEY];

//Intial setup to make a call to webservice.
NSURL *url = [NSURL URLWithString:uriString];

//Create a request object to make a request to the URL.
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;

NSData *data = [NSURLConnection
sendSynchronousRequest:request
returningResponse:&response
error:&error];

if(error == nil)
{
[self.wsData appendData:data];
self.jsonData = (NSMutableDictionary *)[NSJSONSerialization
JSONObjectWithData:(NSMutableData *)self.wsData
options:NSJSONReadingMutableContainers
error:nil];

self.results = [self.jsonData
objectForKey:@"movies"];

}

In the above code, we have created a NSURL instance which consists of a URL that we are required to connect to fetch the data which is to be used as a datasource in our application.

NSURL instance is then wrapped in a NSURLRequest instance to be used for connecting and making a request using the NSURLConnection.

+ sendSynchronousRequest:returningResponse:error is a static method in NSURLConnection class that allows us to connect to the URL and fetch the response accordingly. It returns a NSData object.

To convert the data to JSON format we will be using the NSJSONSerialization class static method. It converts the NSData Object into a NSDictionary. Once done the data is ready to be used in the application.

Step 6: Implement the TableView Delegate and Datasource methods.

#pragma mark TableView Datasource Methods
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

-(NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger)section
{
return [self.jsonData count];
}

#pragma mark TableView Delegate Methods

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CELL_IDENTIFIER = @"WEBSERVICETABLECELL";

UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CELL_IDENTIFIER];

if(cell == nil)
{
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CELL_IDENTIFIER];
}

//Mainuplate data and set the cell text and image values.

NSDictionary *resultDictionary = [self.results objectAtIndex:indexPath.row];
NSDictionary *posterDictionary = [resultDictionary objectForKey:@"posters"];

cell.textLabel.text = [resultDictionary objectForKey:@"title"];

cell.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[posterDictionary objectForKey:@"thumbnail"]]]];
return cell;
}

Step 7: Run your code to check out the output. You will be able to see the output as the image provided below

Screen Shot 2013-12-01 at 6.21.10 pm

I hope you enjoyed the post, keep looking forward to more.

Access media/photos/videos using ALAssetsLibrary iOS/Objective-C

Introduction
With the progressive success to media applications in growing social market, the access to Media Library over iOS device is more or less one of the core requirement. This post focuses on using the ALAssetsLibrary to get access to your media on your iOS device.

What is ALAssetsLibrary ?
ALAssetsLibrary is core class added to the framework to get access to the media on your iOS device. This library was introduced in iOS 5 and has an extensive use in the media applications. All the methods, constants and other connecting libraries are listed over Apple Developer documentation.

Code Time!!!
To use the ALAssetsLibrary in your code, you will be required to import the header file into your class.

 #import <AssetsLibrary/AssetsLibrary.h>

Once the header file is added to your code, you will have access to the methods and constants that are predefined in the header file. With the code assistance provided by Xcode, you can check out other methods available that can be useful in different situations.

Now create a ALAssetsLibrary Object into your scope to be used for accessing media of your device.

ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init];

First of all to access the library you will be required check whether the user has authorized your app to access the media or not.

if([ALAssetsLibrary authorizationStatus])
{
//Library Access code goes here
}
else{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Permission Denied" message:@"Please allow the application to access your photo and videos in settings panel of your device" delegate:[PHCPCSyncController getSharedSyncInstance] cancelButtonTitle:@"Ok" otherButtonTitles: nil];
[alertView show];
}

 

The above constant authorizationStatus checks whether the user of application has provided access to the application to access the Assets or not. In case not then it shows up a alert to the end user to enable manually via Settings. One thing to remember there is no way you can change the settings through your app programatically. So this has to be done manually as its upto end user to allow access to Assets for the particular application.

Now if authorizationStatus returns true then you will be able to access the Assets. Lets move ahead and see how to do this stuff.

[assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if(group)
{
//Filter photos
photoArray = [self getContentFrom:group withAssetFilter:[ALAssetsFilter allPhotos]];
//Enumerate through the group to get access to the photos.

[contentDictionary setObject:photoArray forKey:@"Photos"];

//Filter videos

videoArray = [self getContentFrom:group withAssetFilter:[ALAssetsFilter allVideos]];
[contentDictionary setObject:videoArray forKey:@"Videos"];

[[NSNotificationCenter defaultCenter] postNotificationName:@"assetread" object:nil];

}
} failureBlock:^(NSError *error) {
NSLog(@"Error Description %@",[error description]);
}];

 

There are groups in AssetsLibrary one is for Photos and other for Videos. The above method enumerateGroupsWithTypes:usingBlock helps to do fast enumeration over the groups that exists in the library.

Now to get access to a specific group, we setup a filter using setFilter:
We can provide the specific filter in the initialization also but here we specified ALAssetsGroupAll to access all the media provided.

Once we have attached the specified filter to group we can access the media in that group. I did that in a separate method to keep the understanding clean.

-(NSMutableArray *) getContentFrom:(ALAssetsGroup *) group withAssetFilter:(ALAssetsFilter *)filter
{
NSMutableArray *contentArray = [NSMutableArray array];
[group setAssetsFilter:filter];

[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {

//ALAssetRepresentation holds all the information about the asset being accessed.
if(result)
{

ALAssetRepresentation *representation = [result defaultRepresentation];

//Stores releavant information required from the library
NSMutableDictionary *tempDictionary = [[NSMutableDictionary alloc] init];
//Get the url and timestamp of the images in the ASSET LIBRARY.
NSString *imageUrl = [representation UTI];
NSDictionary *metaDataDictonary = [representation metadata];
NSString *dateString = [result valueForProperty:ALAssetPropertyDate];

// NSLog(@"imageUrl %@",imageUrl);
// NSLog(@"metadictionary: %@",metaDataDictonary);

//Check for the date that is applied to the image
// In case its earlier than the last sync date then skip it. ##TODO##

NSString *imageKey = @"ImageUrl";
NSString *metaKey = @"MetaData";
NSString *dateKey = @"CreatedDate";

[tempDictionary setObject:imageUrl forKey:imageKey];
[tempDictionary setObject:metaDataDictonary forKey:metaKey];
[tempDictionary setObject:dateString forKey:dateKey];

//Add the values to photos array.
[contentArray addObject:tempDictionary];
}
}];
return contentArray;
}

 

This method takes the group and filter as an input, process over media and returns the array consisting of custom data.
ALRepresentation is class used to represent a particular asset. Once we have the representation from the asset, we are allowed to access various attributes of the object. All these can be found in ALAsset Class available in the Apple Developer Documentation. To know more about the options please refer to the documentation.

I hope so you enjoyed the post, look forward to more in near future!!!
Thanks for your time 🙂