Sunday, 4 June 2017

TextColor and backgroundColor Properties of UISearchBar

UISearchBar search text color

If we want to set the search text color in UISearchBar, we can do this either programmatically or in interface builder.

In order to change the text color in UISearchBar, we have to access the UITextField inside the UISearchBar. We can do this by using valueForKey("searchField")

var textFieldInsideSearchBar = yourSearchbar.valueForKey("searchField") as? UITextField

textFieldInsideSearchBar?.textColor = yourcolor

To set the text color of UISearchBar in interface builder,  go to in identity inspector- > User Defined Runtime Attributes -> in Key Path add searchField.textColor  and in Type write Color and in value set color.

In swift we can also create an extension for UISearchBar

public extension UISearchBar {

   public func setTextColor(color: UIColor) {
       let svs = subviews.flatMap { $0.subviews }
       guard let tf = (svs.filter { $0 is UITextField }).first as? UITextField else { return }
       tf.textColor = color
   }
}

UISearchBar background color

If you want to set the background color of UISearchBar,  you can change it in your ViewController and don't want anywhere else to effect then use

for view in searchBar.subviews {
           for subview in view.subviews {
               if subview .isKindOfClass(UITextField) {
                   let textField: UITextField = subview as! UITextField
                   textField.backgroundColor = UIColor.redColor()
               }
           }
    }

But if you want it to be change in whole app and targeting the iOS 9.0 or later then should be using appearanceWhenContainedInInstancesOfClasses like

UITextField.appearanceWhenContainedInInstancesOfClasses([UISearchBar.self]).backgroundColor = UIColor.redColor()

Sunday, 28 May 2017

Uses of Block in Objective C

Block is same as the c function.The basic idea of a block is to treat a small piece of code as if it were a value. The piece of code can then be passed as a parameter in messages or assigned to a variable. 

With the help of block it's very easy  to write code which is simple , easy to debug, readable and maintainable. So let's have a look at the syntax and see how it works:


^{NSLog(@"This is a block"); }


A block is the only object that begins its life in stack, rather than heap memory.A very effective use of a block literal is to deal with a callback. 

Creating a block variable:

int  (^sum)(int, int) = ^(int a , int b){
 return a+b;
}

This  is a simple block definition . It takes two parameters  of type int and return one int parameter.
To call this block as:

int c = sum(3,5);
NSLog(@"sum is %d",c);

When a block is created, it will capture, or close around, the values of those variables that the code has referred to and that are in the same lexical scope of the block. Essentially, a snapshot is taken of these values. These values are preserved in the memory allocated for the block and cannot later be changed. It is said that these variables are "captured by value".
To change the this behavior(to use the latest values of the variable), we can declare these variable as 
__block storage specifier.

__block int z =5;

Block as method parameter: We can also use block as method parameter to use call back. For example:

-(void)validateString:(NSString *)str onCompletion:(void(^)(bool isValidate))completion{
          
 if(str.length ==0){
    completion(NO); //call block
}else{
    completion(YES);
}
}

Now call this method as 

[self validateString:@"demoiOS" onCompletion:^(bool isValidate){
    
if(isValidate){
 //str is valid, handle it
}else{
  // str is not valid, handle it here
}       
   
}];


Thursday, 11 May 2017

Closure Capture list

In Swift, closures are reference type means they reference to objects and properties which comes in the defined context of the closure. What it's means? To understand this concept, let's have a look on  this example:

var a = 23
var b = 35


Note: I am not digging in the deep about what is closure and how it work. I assume you already know the basics of the closure. By definition closure is an object like other objects in Swift which can be stored, referenced and passed as a argument to the functions .

Now create a closure which will print the value of these two variable.

var myClosure: () -> ()  = { print("a = \(a) and b = \(b)") }


Here we define  a closure and stored it in a myClosoure . We can call a closure like any other function s in Swift as:

myClosure()

This will print the value of a and b   as:

a = 23 and b = 35

Now try to change the value of  a and b and then call the  closure 

a = 45
b = 55
myClosure()

The output will be 

a = 45 and b = 55

What is this? It means closure captures the latest value  of the properties and object which comes in the defined context of the  closure. 

Now we have idea of how closure is reference type.  A closure in Swift keeps the reference to the properties , it doesn't copy them by default. But we can changes this default behavior  of the closure means in Swift it's possible to allow closure to copy the value of properties and objects in the defined context of the object. The rescue is called Capture List .

To allow a closure to copy the properties of the object we pass these value in an array which is called Capture List. 

To copy the value we add these value in an array and place this array before in keyword in a closure.

Now edit the above defined closure in order to copy the value of the properties like this:

var a = 23
var b = 35

let  myCopyClosure:() -> () = { [a ,b] in 
              print("a = \(a) and b = \(b)") 
               }

//call the closure
myCopyClosure()

This will print the value as earlier:

a = 23 and b = 35

Try to change the value and then call the closure 

a = 45
b = 55
myCopyClosure()

Now the output will be

a = 23 and b = 35


Did you see the difference?  This time closure copy the value of properties which are added in the capture list of the closure. The closure copy the value of  a property , it's have at the time when we defined the closure. Now it's doesn't matter how many time we change the value of a  and b .

Reference: Official document of Swift from Apple

Sunday, 7 May 2017

Working with Optional Chaining in Swift

Optional chaining is process for querying and calling properties, methods, and  subscripts on an optional that might  be currently nil. If the optional contains a value then call to properties, methods or subscript will success otherwise call will return nil if any optional is nil in the calling chain.

Optional chaining is alternative to forced unwrapping.  In forced unwrapping we place an exclamation mark(!) after the optional value to force the unwrap of its value.

But in optional chaining we put the question mark(?) after the optional value . The main difference between forced unwrapping an optional chaining is that  in forced unwrapping if optional doesn't have a value then we get a run time error. But in optional chaining if optional doesn't have value means it's nil then it gracefully returns nil?.(optional nil).

A point to remember about optional chaining is that if we are accessing properties, calling methods or accessing subscripts , they all return nil? if optional chaining fails at any levels.

 The result of optional chaining call is of the same type as the expected return value, but wrapped in an optional.

Example :

     class MyClass{

     var  address:Address?
}

class Address{

  var streetName =  "12A Arthur Road"
}


let myClass  =  MyClass()

First we are accessing the  with forced unwrapping:

let address = myClass.address!.streetName

This call will give a run time error because currently the value of address is nil in MyClass.

Now with optional chaining :
 let address = myClass.address?.streetName

This call will end gracefully because with optional chaining if it fails at any level it returns nil. It is same as calling nil in objective C.

Thursday, 27 April 2017

Upload an app with share extension to App store in iOS

I discussed in Sharing data between app and a share extension blog. In this you will find what is app extension and how to share data between an app and a share extension .

After adding a new target in our app for a share extension and running it on device , the next question comes in mind is that which app id we will use for uploading an app to iTunes connect. An app extension is bundled with the app when we create a binary of the app and installed when we install the app on the device.

Because each target have separate app id, so we need to create a separate app id for the target in developer site and have to create separate provisioning profile for each target.

So follow these steps to create a share extension and upload it on iTunesConnect:

After adding a new target for the share extension in the project, set the bundle identifier for this new target in target setting in Xcode and then visit the developer site to create a new app id for this new target.

1) In developer site, create a new app id  for your share extension and enter valid details for it like     bundle identifier which you added in target setting of the share extension in Xcode.

2) After creating app id for the share extension , create the provisioning profile for the new app id. Here you should create development , adhoc and app store provisioning profile.
     Download all necessary provisioning profile and switch back to Xcode.

3) In Xcode , set the downloaded provisioning profile for the share extension by selecting the share extension target and set developer and app store provisioning profile here.
     Also make sure you have also set the correct provisioning profile for your main app targets.

4)Now it's time to create an app on iTunes in order to upload it for reviewing by Apple. So visit     iTunesConnect and login with valid credentials.

5) This is the main answer to the question,"Which app id will be used for creating an app on iTunes , which has a target for share extension". As we discussed earlier, your share extension is bundled with main app so , we create an app with the app id of your main app not with the app id of share extension.
 So create an app with  app id of your main iOS app and fill all necessary information here to create an app and save it.

6) Now in Xcode , if you have setup all the things correctly, it's time to create an archive of the     project and upload it to iTunesConnect. So select project from project navigator window in Xcode      and create an archive from Product-> Archive menu.
  Here as you follow the steps, you will see your share extension is bundled with the main app. and that's it.

Reference:developer.apple.com

Sunday, 23 April 2017

Share Data between app and share extension in iOS

App extension provide  a way to perform core functionality of the app, from the activity view controller. For example , if we are seeing an photo in Photos app, then if we want to share this photo on Facebook, we can open it with  Facebook by choosing Facebook form the default activity view controller of the Apple. 
We can also list our app in the default list of the sharing app in activity view controller provided by the Apple by default.
I am not discussing the fundamental of how to create a share extension in iOS app. Today I am going to discuss how to share the data between app and share extension. 

Because iOS app is sandboxed, so to  have common access to data between two targets, we have to create a shared container, in which both targets have read/write access.

1) To create a shared container, enable the group service for the app from the Xcode setting and give the name of group in reversed domain. example: group.appIdentfier

2) Now also enable the group service for the extension target. Here don't create an another group, just choose the group we have created last.

3) Now switch to developer account and create separate provisioning profiles for app and share extension with group configured.

Now we have a shared container, so to have common access to data(like login preferences ) in both targets(app and share extension) we can store these kinds of data in shared UserDefaults. 

Write data in shared UserDefaults: 
if let defaults = UserDefaults.init(suiteName:
        "sharedGroupName"){
        //read and write to UserDefaults       
        }


To read and write files into shared document directory, get the url of document directory and perform task, which is similar to as we do with default file manager.

if let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: AppConstants.kSharedGroupName){
 }

Here url is the path of the document directory, You can create file and folder at this path.

        





Thursday, 6 April 2017

Search content using UISearchBar


When we have a large number of data to display in UITableView, then looking for a particular  data is very tedious task. To overcome this problem we can enable a search functionality on the tableview. As I used this in one of my app.

UISearchBar provide a functionality to search the content we look for from the specific view (here it is table view).It is very simple to use UISearchBar in an app. Steps are as follows:

1) Add UISearchBar property in your view controller's interface file  and set its delegate

         self.searchBar = [[UISearchBar alloc]init];
        self.searchBar.showsCancelButton = YES;
        self.searchBar.delegate = self;
        [self.searchBar becomeFirstResponder];

2)I am displaying the search bar in navigation bar as:
        self.navigationItem.titleView = self.searchBar;


3) Implement the delegate method of the  UISearchBar 

-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar{
    
    //hide the searchBar, and reload the tableview to show original data
    isSearching = NO;    
}
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
    //start the search
    NSLog(@"search btn cliked");
    [searchBar resignFirstResponder];
    [self searhTableList];
    
}
-(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
    NSLog(@"search should begin editing");
    isSearching = YES;
    return YES;
}

Here when user type some text to search and tap the search button, we are searching the table list and then if any matches occur we add the specific row's data to a mutable array and we reload the table view to show the filtered result.

To store the filtered result, declare a mutable array in interface file , here it is filteredArrData,


-(void)searhTableList{
    NSString *searchString = self.searchBar.text;
   
    for (NSString *data in self.arr_tableData) {
         
        NSRange range = [data rangeOfString:searchString options:NSCaseInsensitiveSearch];
         
        NSComparisonResult result = [data compare:searchString options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:range];
        
        if (result == NSOrderedSame) {
            [self.filteredArrData addObject:data];
        }

    }

    if (self.filteredArrData.count == 0) {
         //show an alert, no results found
        
    }else{
         //reload the tableview
          
     }

That 's it. Here I am showing how to implement search on table's data.I am not showing how to use same table view to show the search result and original data(full data to show in table view).