Jun 25 2011

Handle complex iOS projects with multiple dependencies

Category: Objective-c and CocoaDavide Zanotti @ 5:36 pm

If you are reading this post I’m assuming you are having an hard time and tons of errors from xcode trying to compile third party libraries into your project.
I had my hard time me too! I wrote this little “tutorial” after a whole day of build failures, warnings, tests, google searches, configurations, git clone, svn checkout… and a lot of stress! I hope I will help you saving time and reach your goals, good reading ;)

To include a third party library into our iOS projects there are fundamentally 2 ways. The first, and the simplest, is to include the raw sources (headers and implementation files) into them (using drag and drop or “add files to” from the contextual menu), once done we are ready to go after a simple build. Anyway this is not an ideal solution, because in a scenario in which we have several projects all requiring a particular library, we should copy/paste its sources for each project and moreover we should update library’s sources manually every time it will get updated (in the git/svn repository by the “vendor”)!
The second and the best way, is to manage libraries we will depend on, as sub-projects and linking against them. Here is a brief to-do-list:

What to do

1. Each dependecy should be an xcode static library project, that is an .xcodeproj with a target of type library (“.a“) rather than application (“.app“). In most cases third party libraries are already configured in this way and ready to be imported (step 2)

2. Each project must be added as a sub-project to the main project. You can do this by selecting the main node in the folder tree (that one with the blue icon reporting project’s name) and adding the .xcodeproj file using “add files to...” from the contextual menu (DON’T CHECK “Copy items into destination…” because we are going to create a reference to it not a copy of it!)

3. All the static libraries must be linked to the main project: go to the Target’s “Build Phases” and add the library (.a) in “link binary with libraries

4. The main project should specify all the paths where subprojects headers are located (“header search paths” in Target’s Build Settings).

An absolute path is the best way to speciy this paths, in my case: “/Users/davidezanotti/Documents/X_CODE/CoolLibrary”

IMPORTANT:

If the headers are located under different subfolders (ie: /CoolLibraryPath/headerFolder1/, /CoolLibraryPath/headerFolder2/…)
you can specify just “/projectPath” and check “recursive” to automatically scan each subfolders for .h files, otherwise if they are in a specific folder like “/CoolLibraryPath/src/Classes/” and you don’t want a recursive search, be sure the path string ends with “/”

5. If a project we are referencing to, makes use of particular frameworks (UIKit.framework, SystemConfiguration.framework, Security.framework…), the main project must use these frameworks too. So we have to include them in Target’s “Build Phases” (under “link binary with libraries“)

How to prevent errors:

1. The main project should specify the following flags “-ObjC -all_load” in “Other linker flags” (under Target’s Build Settings). These force the loading of all headers and categories.

2. All subproject should expose their header files as “public”

3. Read CAREFULLY (word by word) instructions that come with downloaded libraries and ensure you did exactly what they say

4. Check CAREFULLY each path typed

5. Pay attention to typo errors

What to do when build fails:

It depends on what type of error(s) has occurred, anyway in 99,9% read again “What to do” and “How to prevent errors” until you will compile. Don’t be fooled by the error message, because it’s often inaccurate and misleading. For example a message like “undefined symbol for architecture i386” may tempt you play with architectures settings when the problem is completely different.
If after several attempts you are still unable to compile the project with its dependencies and you are unable to find the answers on google, try to create a new project from the scratch, add your original classes and assets and then add libraries one at time.

ps. In some case I had to change some import from to “Foo.h” to compile all the projects successfully

Special case: JSON framework

This framework (available here: https://github.com/stig/json-framework/) is used by several libraries, that unfortunately had adopted it by simply copy the sources into the project (first way). The result is that I was unable to compile my project because I get “duplicated symbol” errors because I’m using a couple of them. To solve my issues I cloned the original git repository, added that project into mine and under Target’s “Compile Sources” section of these libraries I removed .h and .m files of the JSON framework. The final result is that I left untouched the downloaded libraries (the files are still where they were), but I can compile without problems because now I don’t have duplicates, all the sources are retrieved from the JSON project once rather than one time for each sources in the library!

Tags: , ,


Jan 08 2011

Bear in mind: you have to cast your numbers to play with them in Objective-C!

Category: Objective-c and CocoaDavide Zanotti @ 10:53 am

Yes, I’m still a newbie when it comes to Objective-C, although I’ve reached a pretty good knowledge level and I’m able to build interfaces, working with files and folders, SQLite api, low level core graphics api and so on, there is always something new to learn that I was ignoring it!
Today I lost more than an hour to understand why a number didn’t get rounded properly. In a method of a class, I was using the ceil() function to round a number returned by a simple division (11/3). The unbelievable thing was that it returned 3 rather than 4 (the right value I was expecting). After several debugging and googling, I realized that I have simply to specify (cast) the numbers I’m going to divide as float to get the right value back from the division!
To specify numbers as floating values, we can use the “.f” syntax or a normal cast, the following code show the scenario:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// wrong way (it will print: "result: 0.000000 - ceiling: 3 - flooring: 12376")
NSLog(@"result: %f - ceiling: %i - flooring: %i",
11/3,
(NSUInteger)ceil(11/3),
(NSUInteger)floor(11/3));

// right 1 (it will print "result: 3.666667 - ceiling: 4 - flooring: 3")
NSLog(@"result: %f - ceiling: %i - flooring: %i",
11.0f/3.0f,
(NSUInteger)ceil(11.0f/3.0f),
(NSUInteger)floor(11.0f/3.0f));

// right 2 (it will print "result: 3.666667 - ceiling: 4 - flooring: 3")
NSLog(@"result: %f - ceiling: %i - flooring: %i",
(float)11/(float)3,
(NSUInteger)ceil((float)11/(float)3),
(NSUInteger)floor((float)11/(float)3));

Tags: ,


Nov 15 2010

Working with Cocoa dates objects

Category: Objective-c and CocoaDavide Zanotti @ 11:34 am

Preface

Using and mastering dates objects in Cocoa is not so straightforward as it would in other languages (such Java, Actionscript, JavaScript…), where you just rely on a single Date class (ok, latest releases of Java have deprecated this approach, but it was a common practice before as far I know) whose interface provides all the necessary methods to set, get and modify dates and related informations (like years, months, days and so on). Cocoa instead, takes a more sophisticated approach, increasing the flexibility and the power of the framework. Anyway this freedom comes at an obvious price, it requires a little more code to write and additional concepts and classes to learn. Dates are handled using the 5 classes: NSDate, NSCalendar, NSTimeZone, NSDateComponents, NSDateFormatter.

NSDate

NSDate is the core class that represents a point in time but it’s pretty useless alone, in fact we can create instances of this object but without the ability to configure its details (year, month, day, hours, minutes, seconds…). Its main constructors ([[NSDate alloc] init] and [NSDate date]) return the current date and the only way to initialize a different one (a future or a paste date) with only NSDate is by using previously defined dates passing them to initWithTimeInterval:sinceDate: or dateWithTimeIntervalSinceReferenceDate: otherwise choose among one of the string based constructors that accept string representation of dates (like initWithString: or dateWithString:). NSDate doesn’t provide much more than constructors, the only concrete methods whose interact with date objects are: isEqualToDate:, earlierDate:, laterDate: and compare:. These are used to compare two dates and know which is the earlier and if their are or not the same date. Date computation such finding out years elapsed between two dates, increase a date by an arbitrary amount of days or subtract hours from a date are not possible without using other date-related classes.

NSCalendar

NSCalendar represents a system of reckoning time over extended periods and it’s related to a concrete calendar, usually the Gregorian Calendar which is an international standard for civil use. Anyway is possible to initialize an NSCalendar choosing among hebrew, islamic, chinese an others system supported calendars. A nice (not programming related) reading about calendars and their history can be found here: http://astro.nmsu.edu/~lhuber/leaphist.html.
NSCalendar exposes methods for date computations and to extract date parts (years, months, days…) from a date.
In a calendar, day, week, weekday, month, and year numbers are generally 1-based.

NSTimeZone

A time zone represents a geopolitical region on Earth that has its own standard time. By convention each time zone computes its local time as an offset from the Greenwich Mean Time (GMT) (http://en.wikipedia.org/wiki/Greenwich_Mean_Time).
NSTimeZone is a class that handles time zones. A specific time zone can be assigned to an NSCalendar to influence its behavior.

NSDateComponents

This is a simple abstract class (no more than a structure) that incapsulate date’s parts (year, month, day, hour…). It’s used in conjunction with NSCalendar to initialize a specific user defined NSDate.

NSDateFormatter

This is as special formatter class that handle conversions from date to a string and vice versa by using the conventional unicode date patterns (http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns). NSDateFormatter formats dates according to the defined locale, calendar and time zone, if you don’t specify any of these references it will uses the current system settings.

Common dates tasks

Print a formatted date:

To print a formatted date we have to use NSDateFormatter and specify a formatting mask manually (by using setDateFormat) or rely on the “automatic” masks by choosing among NSDateFormatterStyle styles.

1
2
3
4
5
6
7
8
9
10
// initialize a date object
NSDate *currentDate = [[NSDate alloc] init];

// initialize a formatter object
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// set formatter's format
[formatter setDateFormat:@"eeee, dd MMMM yyyy - HH:mm"];
   
// print formatted date
NSLog(@"formatted date: %@", [formatter stringFromDate:currentDate]);

The code above will print “sabato, 13 novembre 2010 – 12:10” on my mac due to my system settings, whose I can check as following:

1
2
3
4
5
6
// get my current time zone
NSLog(@"my time zone: %@ - abbreviation: %@", [[NSTimeZone systemTimeZone] name], [[NSTimeZone systemTimeZone] abbreviation]);
// get my current calendar
NSLog(@"my calendar: %@", [[NSCalendar currentCalendar] calendarIdentifier]);
// get my current locale
NSLog(@"my locale: %@", [[NSLocale currentLocale] localeIdentifier]);

The NSLogs will print:

  • “my time zone: Europe/Rome – abbreviation: GMT+01:00″
  • “my calendar: gregorian”
  • “my locale: it_IT”

Ok, now let’s see how we can influence the formatter by changing locale and time zone:

1
2
3
4
5
6
7
8
// create an american locale
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
   
// assign custom locale to the formatter
[formatter setLocale:usLocale];

// print formatted date
NSLog(@"formatted date: %@", [formatter stringFromDate:currentDate]);

Now it will print: “Saturday, 13 November 2010 – 12:10” despite my system locale is “it_IT”.
To influence date’s hours, I can set a different time zone rather than that one being used by my system:

1
2
3
4
5
// set a GMT time zone
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
   
// print formatted date
NSLog(@"formatted date: %@", [formatter stringFromDate:currentDate]);

Now it will print: “Saturday, 13 November 2010 – 11:10“, in fact my system’s time zone is a “GMT+01:00″ and if you subtract that +1 the result is 11 instead of 12!

Create an user defined date:

To create a custom date object (a date with an arbitrary year, month, day and so on), we have first to initialize an instance of NSDateComponents with the desired date’s parts and then use a calendar to retrieve the relative NSDate:

1
2
3
4
5
6
7
8
// create a custom dateComponents object
NSDateComponents *customComponents = [[NSDateComponents alloc] init];
[customComponents setYear:2015];
[customComponents setMonth:7];
[customComponents setDay:23];

// create a date object using custom dateComponents against current calendar
NSDate *customDate = [[NSCalendar currentCalendar] dateFromComponents:customComponents];

Get date’s parts from a date

To extract date parts from an NSDate we have to use NSCalendar‘s method components:fromDate:. Components are specified by a flag parameter which can be composed using the bitwise “or” operator (the pipe symbol) and merging NSCalendarUnit constants. To get year, month and day from a date we can do the following:

1
2
// get year, month and day parts
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:date];

then we can use retrieved date parts in this way:

1
NSLog(@"year: %i - month: %i - day: %i", [components year], [components month], [components day]);

It’s important to specify all the date parts we want to use, because if we forget it and we try to access an undefined part, we won’t get an error but instead 2147483647 as the return value (which is the maximum value for a signed 32-bit integer). For example if we write:

1
NSLog(@"hour: %i", [components hour]);

we will get 2147483647 because we didn’t specify the NSHourCalendarUnit

Add or subtract date parts from/to a date:

To add or subtract date parts from/to a date, we have to create a dateComponents object configuring it with the desired gap to fill and then use calendar’s method dateByAddingComponents:toDate:options:. For example if we want to add 2 years and half to a date we can do this:

1
2
3
4
5
6
7
// create a dateComponents with 2 years and 6 months
NSDateComponents *newComponents = [[NSDateComponents alloc] init];
[newComponents setYear:2];
[newComponents setMonth:6];

// get a new date by adding components
NSDate *newDate = [[NSCalendar currentCalendar] dateByAddingComponents:newComponents toDate:date options:0];

to subtract we have to configure NSDateComponents with negative values (ie: [newComponents setYear:-2]…)

Get difference between two dates:

To get the difference between 2 dates we have to rely on NSCalendar once again and use its components:fromDate:toDate method, by specifying the date parts we are interested in and 2 different dates. Values returned from components can be negative if toDate is earlier than fromDate. The following example retrieve days difference between 2 dates:

1
NSDateComponents *componentsDiff = [calendar components:NSDayCalendarUnit fromDate:dateA toDate:dateB options:0];

Conclusion

Dates are a fascinating argument when it comes to programming in Objective C, but due to the different aspects involving date computation is really important to understand concepts beyond the mere classes implementations, especially if we want to make internationalized applications. My post would be a synthesis to help you get started faster with dates manipulation, I suggest however to read all the official documentation provided by Apple.

Tags: , , , , , , , ,


Nov 01 2010

(My) common mistakes learning Objective C and iPhone programming

Category: Objective-c and CocoaDavide Zanotti @ 7:15 am

In this post I would like to gathering all the mistakes I did in the past months while I was learning Objective C, and although I’m still a learner and I will face new obstacles soon, I hope to keep in mind some concepts and language features better, by talking about those and help new students save time on google and extra headache.
So, let’s start with the errors list:

1. No “foo” method found / “foo” may not respond to selector

I was implementing a method (inside .m file) not defined before in the header (.h)

2. Bad positioning and behavior of UITabBarController’s view

I added the UITabBarController’s view to a subview rather than the main window (the only place where you can add it)

3. Create my own null wrapper

Objective C doesn’t allow nil values to be added to collections (NSArray, NSDictionary and so on) but just objects, so I got the smart idea of realize a dummy class representing null values. In this way I can create instances of “MyDummyNullObject” and add them to any kind of collection. However I successively discovered that Cocoa was already providing such class: NSNull! Moreover it offers a better implementation, because it’s a singleton, thus you don’t have to create more than one object but just relay on it’s “factory method”: [NSNull null] which will return the shared NSNull instance.

4. Write repeated @synthetize statemens

There is no need to write “@synthetize” for each class property, we can simple write it once followed by a comma delimited list of properties (ie: @synthetize prop1, prop2, prop3, prop3, prop4;)

5. Create unnecessary pointers for scalar types

Scalar types are basically all that is not a class (NSInteger, NSUInteger, BOOL…), for these types the asterisk is not required! (basically asterisk means: “I’m gonna to create a pointer to an instance of a class”)

6. Try to compare strings and other objects by using == operator

by using == we are not comparing strings values but pointers (which will be always different despite their content). The right way is using isEqualToString:, isEqual: or compare: (and its variants)

7. Check sqlite’s database modifications in the wrong place

I spent a lot of time to figure out why my insert and update commands did not modify database content, then I realized that I was looking at the wrong file! Project’s resources are copied during the build phase, and you can find them under:
“/Users/$user_name/Library/Application Support/iPhone Simulator/$sdk_version/Applications/$app_id/$app_name.app” (of course you have to replace $foo with your personal info)

8. Initialize variables inside switch/case statement

Is not possible to initialize a variable as first declaration inside a case, if you try to compile the following code:

1
2
3
4
5
6
7
8
switch (value) {
    case 5:
        NSNumber *number = [[NSNumber alloc] initWithInt:value];
        NSLog(@"my number: %i", [number intValue]);
        break;
    default:
        break;
}

The compiler will rise an “Expected expression before NSNumber”. To avoid errors we can simply declare the instance variable before the switch statement in this way:

1
2
3
4
5
6
7
8
9
10
NSNumber *number;

switch (value) {
    case 5:
        number = [[NSNumber alloc] initWithInt:value];
        NSLog(@"my number: %i", [number intValue]);
        break;
    default:
        break;
}

or cheat the compiler by writing a command like an NSLog, before the assignment:

1
2
3
4
5
6
7
8
9
switch (value) {
    case 5:
        NSLog(@"go on without errors!");
        NSNumber *number = [[NSNumber alloc] initWithInt:value];
        NSLog(@"my number: %i", [number intValue]);
        break;
    default:
        break;
}

of course this last example is really nonsense, but it gives you the idea :)

UPDATE:
As suggested by Dmitry Chestnykh in the comments, there is a better and easy approach to avoid such errors (pay attention to the curly braces in the case):

1
2
3
4
5
6
7
8
9
switch (value) {
    case 5: {
        NSNumber *number = [[NSNumber alloc] initWithInt:value];
        NSLog(@"my number: %i", [number intValue]);
        break;
    }
    default:
        break;
}

9. Unrecognized selector sent to instance

I was declaring a pointer of type NSMutableArray and assigning it to an NSArray (a simple typo error), in this way:

1
NSMutableArray *array = [[NSArray alloc] init];

then I was filling it with “addObject”:

1
[array addObject:myObject];

and at runtime I got the “unrecognized selector sent to instance”, this because I allocated an NSArray which doesn’t have “addObject” among its methods (in fact an NSArray is immutable and objects contained in this collection are specified during its creation)… anyway I don’t understand why the compiler does not rise an error at compile time :^/

10. UITableViewCells mixed up after scrolling UITableView

One of the most complex ui element to understand in the UiKit framework is undoubtedly UITableView, because it’s composed by different elements (like UITableViewCell) and requires a good knowledge of how it has been conceived in order to make it work the right way and with good performance. The main key concept behind this cool component resides in the method “tableView:cellForRowAtIndexPath“. The scope of this one is to return the UITableViewCell for a specific NSIndexPath and is called automatically at runtime. For performance reasons, it should returns cached objects by using dequeueReusableCellWithIdentifier and only if there is no a cached version (the method returns nil), initialize the required cell (that is allocating a UITableViewCell instance and configuring its views). In case of cached cells, you must avoid adding/removing views from the cell and limit changes to colors, label’s values and similar. Any other “invasive” change can results in strange table behavior and performance decrease.

Tags: , , ,


Aug 20 2010

Image background fallback using <img/> tag error handlers

Category: javascriptDavide Zanotti @ 3:49 pm

In the project I’m working on, we make large use of external assets like images, coming from several providers. Unfortunately these links are often broken, resulting in “empty squares” which are unpleasant for the user. The best thing to do, since we don’t have the full control over these resources, is to display the classic “image not available” pic, but this is not simple as writing an if statement such:

1
2
3
4
5
if (record.image) {
    <img src="record.image" />
} else {
    <img src="imageNotAvailableLink" />
}

because in our case “record.image” is defined although its path is unreachable. So, we have to try to load the image and replace it at runtime when we know that the src is broken. How can we catch that exception? We just need to listen to onerror and onabort img events, so we can simply write this smart img tag:

1
    <img src="providerSrc" onerror="this.src=fallbackSrc" onabort="this.src=fallbackSrc" />

The this key inside an event attribute, refers to the dom node itself, so we can switch an attribute like src with a little piece of inline JavaScript.
Anyway in my scenario the problem is bigger than this example, because thumbs images are rendered as divs background in order to make a sort of polaroid effect and, at the same time, to solve a potential issue related to exceeding image dimensions or a stretched one.
Fortunately the solution I found, is not so far from the above, the only difference is that I used an invisible img tag located inside the div and when it catch the error rather than change its src it changes its parent background:

1
2
3
    <div style="background:url(providerSrc)">
        <img style="display:none" src="providerSrc" onerror="this.parentNode.style.backgroundImage='url(fallbackSrc)'"  />
    </div>

Of course in my final implementation I replaced the piece of inline js assignment with an utility function call that does the job and also trace a debug message to the console, displaying the path of the broken image… I’m pretty satisfied about my work :^)


« Previous PageNext Page »