Objective C study note

Redirect your app to the System Settings App

When use some functionality related to user’s privacy, say CoreLocation Framework, there would always be a problem that user may deny the app request for their current location and the app cannot work correctly. There is nothing we can make decision for the user, but at least we can help them enable that service easier.

That is the topic today — redirection to the Settings App

There are two types of redirection you want to make: redirect to the ①App Specific Settings Page and ②System Settings Page


①Redirect to App Specific Settings Page

Prior to iOS 8, there is no way to do that. So if you want to back support to iOS 7 , make sure to check the availability

NSURL *url = [NSURL URLWithString: UIApplicationOpenSettingsURLString];
if([UIApplication sharedApplication] canOpenURL: url){
        [[UIApplication sharedApplication] openURL:url];
}

Notice that url is actually the @”app settings”, which would directly get you into the page that user can enable the location request


②Redirect to System Settings

Here is the goal we want to achieve. Before user can accept your app’s request for the location service, user may globally disable the location preference, and your app cannot even trigger that request. So we need to do some additional work to make it happen.

First, go the your App’s Target, under Info, there is a section for URL Types.
URL Type Settings

Add this one which just put URL Schemes called “prefs”

Then inside your code, when you want, call (e.g I want to go to the location under privacy on Settings)

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=Privacy&path=LOCATION"]];

And that’s it!

This method should be okay to use after iOS5.1, I tested on iOS8.4 and iOS 9 it works great! Any feed back is very appreciated!

Advertisements
Objective C study note

Apply CSS into your UIWebVIew

First, you need to drag your .css file into your Xcode workspace, I dragged it into the “Supporting Files/”

Then, all you need is these three lines:

NSString *path = [[NSBundle mainBundle] resourcePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
[webView loadHTMLString:htmlString baseURL:baseURL];

If you’re curious, you can actually log out everything you have in the resourcePath of your App.

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *dirContents = [fileManager contentsOfDirectoryAtPath:path error:nil];
    NSLog(@"Contents = %@",dirContents);

If you can see your *.css file, you are good to go!

Of course, you would need to add a line of CSS code into the header of your html string to make it aware of that CSS file.

NSMutableString *finalHtmlString = [[NSMutableString alloc]initWithString:@"<link href=\"mobile.css\" rel=\"stylesheet\" type=\"text/css\">"];
[finalHtmlString appendString:htmlString];

BeforeAfter
Before                                                                 After

Objective C study note

Deep Copy and Shallow Copy in Objective C

I would like to share with you guys this topic which has bothered me so many times before and we all should pay special attention to.

Let’s do an experiment first.

I would create a Class called Person, set 2 properties as “Name” and “Age”.

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property(strong,nonatomic)NSString *name;
@property(nonatomic)NSUInteger age;
-(instancetype)initWithName:(NSString *)name Age:(NSUInteger)age;
@end

Then in the main file, I create three reference of person.

Person *personA = [[Person alloc]initWithName:@"Cong Sun" Age:24];
Person *personB = personA;
Person *personC = personA;

Then if I changed the age of the personA, guess what happened? All of the ‘age’ property for those three people has changed!  Let’s look into the memory to see what exactly happened

Screen Shot 2015-06-07 at 6.05.52 PM

Here, we can see that all the three person’s memory address are identical, which we say that personB and personC are the Shallow Copy of personA. They are all the pointers pointing to the same memory address. So we can treat them as personA’s alias.  When you use personB = personA, you are not passing all the properties of the personA to the personB, but pass the point to it.

The Deep Copy, on the contrary,  is the copy of the object’s value. After deep copy, any manipulate to the object will not affect its copy. Let’s take the NSMutableArray as a simple example.(At the beginning the reason we use the Person Class is because the Array class won’t show the actual memory address on the debug console)

NSMutableArray *array1 = [[NSMutableArray alloc]init];
NSMutableArray *shallowCopy = array1;
[array1 addObject:@1];
NSMutableArray *deepCopy = [[NSMutableArray alloc]initWithArray:array1];
[array1 addObject:@2];

and let’s pull out what happened on the memory

Screen Shot 2015-06-07 at 6.21.10 PM

The Objective C gives us a good tool to get a deep copy using its in-built initializer. We can figure out right here in the memory, the deep copy of the array1 will not add the @2 into the array however the shallow copy, which shares the same memory address will be modified as we manipulate the array1.

So how can we avoid the shallow copy mistake when necessary? I would say using ‘alloc’. The ‘alloc’ will create a new memory space for the variable and make it point to that address. Then, if the OC has that initializer to perform the deep copy, do it, otherwise, we need to copy every single property of the object to the new object. For example, we can do the deep copy for the Person class like this:

Person *personA = [[Person alloc]initWithName:@"Cong Sun" Age:24];
Person *personB = personA;
Person *deepCopyPerson = [[Person alloc]init];
//deep copy
deepCopyPerson.name = personA.name;
deepCopyPerson.age = personA.age;

personA.age = 26;

Screen Shot 2015-06-07 at 6.33.24 PM

Here we go, we can see the difference. The deepCopyPerson is not changed!

Now, see this example, we can load the memory address by using %p

        NSString *a = @"abc";
        NSString *b = a;
        NSLog(@"memory location of a = %p",a);
        NSLog(@"memory location of b = %p",b);
        a = @"def";
        NSLog(@"memory location of modified a %p",a);

The NSString is also a pointer pointing to a specific memory address. So What would happen to NSString b?

Screen Shot 2015-06-09 at 1.11.47 PM

At the first step, when we assign b = a, we can see they point to the same memory address. So b is a shallow copy of a. However, when we modify the value of a, we got this result

Screen Shot 2015-06-09 at 1.17.44 PM

The b stays the same value! This is tricky but when we see the console, we can get the reason:

Screen Shot 2015-06-09 at 1.16.27 PM

When we do a = @”def”, we are doing the same thing as

a = [[NSString alloc]initWithString:@”def”];

Which will make a point to another memory address. However the b still hold the previous memory address of a, so even it is a shallow copy, it doesn’t matter. The trick is, only the modification on the value stored in the specific memory address the pointer points to will have the effect on all of the pointers’ shallow copies 🙂

Tips:

Class like NSMutableArray has the initializer like initWithArray can make the deep copy directly! Try to dig into the documentation and be lazy when you can!

The OC’s NSCopying protocol is essentially doing a deep copy. In the situation when you want to use the NSDictionary, the key has to follow the NSCopying protocol to make a deep copy into the dictionary so that it wouldn’t be changed in the future. You can view this blog for more information http://bynomial.com/blog/?p=73